变量
声明、使用、只读、删除
myUrl="runoob.com"
echo $myUrl ${myUrl}
readonly myUrl
unset myUrl
1、定义变量时,不加 $
,变量名和等号之间 不能有空格
2、使用变量,变量名前面加 $
。
3、{ }
可选,加不加都行,加花括号是为了帮助解释器识别变量的边界
4、unset
命令,不能删除只读变量
字符串(拼接、长度、截取、查找)
字符串可以用单引号,也可以用双引号,也可以不用引号。
单双引号的区别
单引号字符串的限制:
①单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;②单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
双引号的优点:
①双引号里可以有变量;
②双引号里可以出现转义字符
拼接字符串
your_name="runoob"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
# hello, runoob !
错误拼接
greeting_3='hello, ${your_name} !'
# hello, ${your_name} !
获取字符串长度
echo ${#your_name} # 输出 6
提取/截取子字符串 :从字符串第 2 个字符开始截取 4 个字符:
echo ${your_name:1:4} # 输出 unoo
查找子字符串 :查找字符 i 或 o 的位置(哪个字母先出现就计算哪个):
echo `expr index "$your_name" io` # 输出 4
数组(定义、读取、长度)
bash支持一维数组(不支持多维数组),并且没有限定数组的大小
定义数组
在 Shell 中,用 括号
来表示数组,数组元素用 "空格"
符号分割开。定义数组的一般形式为:
arrays=(value0 value1 value2 value3)
或
array=(
value0
value1
value2
value3
)
单独定义
,可以不使用连续的下标,而且下标的范围没有限制
array[5]=value
读取数组
${array[0]}
使用 @
符号可以获取数组中的所有元素
echo ${array[@]}
获取数组的长度
length=${#array[@]}
length=${#array[*]}
取得数组单个元素的长度
lengthn=${#array[n]}
注释
单行注释
# 开头,会被解释器忽略。
多行注释
:<
EOF 也可以使用其他符号:
:<<'
注释内容...
'
:<
非常规方法:可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。
运算符
原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。awk
是一个优良的文本处理工具,Linux及Unix环境中现有的功能最强大的数据处理引擎之一。awk 是三位创始人的首字母。expr
是一款表达式计算工具,用于在UNIX/LINUX下求表达式变量的值,一般用于整数值,也可用于字符串。
表达式和运算符之间要有空格
,完整的表达式要被 ``
包含。
val=`expr 2 + 2`
算术运算符
+
、-
、*
、/
、%
、=
、==
、!=
1、条件表达式要放在方括号
之间,并且要有空格
,例如: [ $a == $b ]
2、乘号(*)前边必须加反斜杠(\)才能实现乘法运算;val=`expr \$a \* $b`
3、在 MAC 中 shell 的 expr 语法是:$((表达式)),此处表达式中的 "*" 不需要转义符号 "\" 。
关系运算符
命令 | 说明 |
---|---|
-eq |
相等 |
-ne |
不等 |
-gt |
大于 |
-lt |
小于 |
-ge |
大于等于 |
-le |
小于等于 |
布尔运算符
!
非 、-o
或 、-a
与
逻辑运算符
&&
、||
字符串运算符
命令 | 说明 |
---|---|
= |
相等 |
!= |
不等 |
-z |
长度为0 |
-n |
长度不为0 |
$ |
字符串不为空 |
[ $a = $b ]
[ -z $a ]
文件测试运算符
命令 | 说明 |
---|---|
-b file |
检测文件是否是块设备文件,如果是,则返回 true。 返回 false。 |
-c file |
检测文件是否是字符设备文件 |
-d file |
检测文件是否是目录 |
-f file |
检测文件是否是普通文件(既不是目录,也不是设备文件) |
-g file |
检测文件是否设置了 SGID 位 |
-k file |
检测文件是否设置了粘着位(Sticky Bit) |
-p file |
检测文件是否是有名管道 |
-u file |
检测文件是否设置了 SUID 位 |
-r file |
检测文件是否可读。 |
-w file |
检测文件是否可写 |
-x file |
检测文件是否可执行 |
-s file |
检测文件是否为空(文件大小是否大于0) |
-e file |
检测文件(包括目录)是否存在 |
其他检查符:
-S file 判断某文件是否 socket
-L file 检测文件是否存在并且是一个符号链接
[ -b $file ]
...
echo、printf、test
echo
echo "It is a test"
echo It is a tests
echo "It is a test" > myfile //显示结果定向至文件
echo '$name\"' //原样输出字符串,不进行转义或取变量(用单引号)
echo `date` //显示命令执行结果
echo -e "OK! \n" //显示换行
echo -e "OK! \c" //显示不换行
显示变量(从命令行里获取值)
read
命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量
#!/bin/sh
read name
echo "$name It is a test"
以上代码保存为 test.sh,name 接收标准输入的变量,结果将是:
sh test.sh
OK #标准输入
OK It is a test #输出
printf
printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。
printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。
默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。
printf "Hello, Shell\n"
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
# 郭靖 男 66.12
%s、%c、%d、%f
都是格式替代符
%-10s
指一个宽度为10个字符(-
表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
%-4.2f
指格式化为小数,其中.2
指保留2位小数。
#!/bin/bash
# format-string为双引号
printf "%d %s\n" 1 "abc"
#1 abc
# 单引号与双引号效果一样
printf '%d %s\n' 1 "abc"
#1 abc
# 没有引号也可以输出
printf %s abcdef
#abcdef
# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def
#abcdef
printf "%s\n" abc def
#abc
#def
printf "%s %s %s\n" a b c d e f g h i j
#a b c
#d e f
#g h i
#j
# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"
# and 0
printf的转义序列
命令 | 说明 |
---|---|
\a |
警告字符,通常为ASCII的BEL字符 |
\b |
后退 |
\c |
抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略 |
\f |
换页(formfeed) |
\n |
换行 |
\r |
回车(Carriage return) |
\t |
水平制表符 |
\v |
垂直制表符 |
\\ |
一个字面上的反斜杠字符 |
\ddd |
表示1到3位数八进制值的字符。仅在格式字符串中有效 |
\0ddd |
表示1到3位的八进制值字符 |
test
数值测试
if test $[num1] -eq $[num2]
...
a=5
b=6
result=$[a+b] # 代码中的 [] 执行基本的`算数运算`, 等号两边不能有空格
字符串测试
if test $num1 = $num2
...
文件测试
if test -e ./bash
...
逻辑操作符
Shell提供了与( -a )、或( -o )、非( ! )
三个逻辑操作符用于将测试条件连接起来,其优先级为:!最高,-a次之,-o最低
。
if test -e ./notFile -o -e ./bash
...
流程控制
和Java、PHP等语言不一样,sh的流程控制不可为空。在sh/bash里,如果else分支没有语句执行,就不要写else
。
if else
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
写成一行(适用于终端命令提示符):
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
if else语句经常与test命令结合使用,如下所示:
if test $[num1] -eq $[num2]
then
echo '两个数字相等!'
else
echo '两个数字不相等!'
fi
for 循环
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
写成一行
for var in item1 item2 ... itemN; do command1; command2… done;
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
for 其他用法
对于习惯其他语言 for 循环的朋友来说可能有点别扭。
for((assignment;condition:next));
do
command_1;
commond_..;
done
这里的 for 循环与 C 中的相似,但并不完全相同。
通常情况下 shell 变量调用需要加 $,但是 for 的 (()) 中不需要
#!/bin/bash
for((i=1;i<=5;i++));do
echo "这是第 $i 次调用";
done;
while 语句
#!/bin/bash
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done
以上实例使用了 Bash let 命令,它用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量。
while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM,按
echo '按下 退出'
echo -n '输入你最喜欢的网站名: '
while read FILM
do
echo "是的!$FILM 是一个好网站"
done
无限循环
while :
do
command
done
while true
do
command
done
for (( ; ; ))
until 循环
一般 while 循环优于 until 循环
,但在某些时候—也只是极少数情况下,until 循环更加有用。
语法:
until condition
do
command
done
实例:
#!/bin/bash
a=0
until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done
case
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;;
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac
跳出循环
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue
。
break命令
break命令允许跳出所有循环(终止执行后面的所有循环)。
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字:"
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
break
;;
esac
done
continue
continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字: "
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的!"
continue
echo "游戏结束"
;;
esac
done
运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句 echo "游戏结束" 永远不会被执行。
case ... esac
case ... esac 与其他语言中的 switch ... case 语句类似,是一种多分枝选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。
case 值 in
模式1)
command1
...
;;
模式2)
command1
...
;;
*)
command1
...
;;
esac
case 后为取值,值可以为变量或常数。
值后为关键字 in,接下来是匹配的各种模式,每一模式最后必须以右括号
结束,模式支持正则表达式。
#!/bin/sh
site="runoob"
case "$site" in
"runoob") echo "菜鸟教程"
;;
"google") echo "Google 搜索"
;;
"taobao") echo "淘宝网"
;;
esac
函数
[ function ] funname [()]
{
action;
[return int;]
}
1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。return后跟数值n(0-255),还可以通过echo 直接返回。
3、注意,shell中通过return返回是有限制的,最大返回255
,超过255,则从0开始计算
#!/bin/bash
demoFun(){
echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"
下面定义一个带有return语句的函数:
#!/bin/bash
funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"
函数返回值在调用该函数后通过$?
来获得。注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
函数参数
在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,$1表示第一个参数,$2表示第二个参数...
带参数的函数示例:
#!/bin/bash
funWithParam(){
echo "第一个参数为 $1 !"
echo "第十个参数为 $10 !" //会输出:第十个参数为 10 ! 而非第十个
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
注意,$10
不能获取第十个参数,获取第十个参数需要 ${10}
。当n>=10时,需要使用${n}
来获取参数。
另外,还有几个特殊字符用来处理参数:
命令 | 说明 |
---|---|
$# |
传递到脚本的参数个数 |
$* |
以一个单字符串显示所有向脚本传递的参数 |
$$ |
脚本运行的当前进程ID号 |
$! |
后台运行的最后一个进程的ID号 |
$@ |
与$*相同,但是使用时加引号,并在引号中返回每个参数。 |
$- |
显示Shell使用的当前选项,与set命令功能相同。 |
$? |
显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
补充:
1、$?
仅对其上一条指令负责,一旦函数返回后其返回值没有立即保存入参数,那么其返回值将不再能通过 $?
获得。
2、函数与命令的执行结果可以作为条件语句使用。要注意的是,和 C 语言不同,shell 语言中 0 代表 true,0 以外的值代表 false
。