条件判断
shell脚本中经常用到条件判断,而其中“=”用于字符串相等判断。需要注意的是,使用“=”号时,如果字符串变量未定义或者字符串变量为空,则容易产生错误。比如下面的两个例子。
#!/bin/sh
X=
if [ $X = "hello" ]; then
echo X has value hello
else
echo X does not have value hello
fi
如果运行以上脚本,则会得到以下错误:
line 3: [: =: unary operator expected
如果变量未定义,也会得到类似结果。
#!/bin/sh
#X=
if [ $X = "hello" ]; then
echo X has value hello
else
echo X does not have value hello
fi
line 3: [: =: unary operator expected
产生这个问题的根本原因,在于“=”左边的变量取值为空。因此解决办法就是避免"="两边出现空。具体有以下一些常见技巧:
技巧一:将变量取值后用双引号扩起来。
#!/bin/sh
X=
if [ "$X" = "hello" ]; then
echo X has value hello
else
echo X does not have value hello
fi
技巧二:加前缀字符。
#!/bin/sh
X=
if [ X$X = Xhello ]; then
echo X has value hello
else
echo X does not have value hello
fi
附上shell脚本条件判断中常用的运算符。
表1 字符串运算符
运算符 | 说明 |
---|---|
= | 判断两个字符串相同 |
!= | 判断两个字符串不同 |
-z | 判断字符串长度为0 |
-n | 判断字符串长度非0 |
[ $str ] | 判断字符串非空 |
表2 数值比较运算符
运算符 | 说明 |
---|---|
-eq | 判断两个数值相等 |
-ne | 判断两个数值不等 |
-gt | 判断左边数值大于右边数值 |
-lt | 判断左边数值小于右边数值 |
-ge | 判断左边数值大于或等于右边数值 |
-le | 判断左边数值小于或等于右边数值 |
表3 文件测试运算符
运算符 | 说明 |
---|---|
-b file | 判断特殊块文件 |
-c file | 判断特殊字符文件 |
-d file | 判断目录 |
-f file | 判断普通文件、目录或特殊文件 |
-g file | 判断文件是否设置了群组ID |
-k file | 判断文件是否设置了sticky标记 |
-p file | 判断命名管道 |
-t file | 判断文件是否已打开并关联到终端 |
-u file | 判断文件是否设置了用户ID |
-r file | 判断文件可读 |
-w file | 判断文件可写 |
-x file | 判断文件可执行 |
-s file | 判断文件长度大于0 |
-e file | 判断文件或目录存在 |
变量替换
在shell脚本中要获取一个变量的值,可通过${var}这种形式。这种形式还有几种扩展。一种典型的扩展应用就是当变量未定义时,采用一个默认值。如下边的例子。
#!/bin/sh
echo value of X is ${X:-default}
value of X is default
表4中列出了变量替换的几种形式。
变量替换形式 | 说明 |
---|---|
${var} | 返回变量的值 |
${var:-word} | 如果变量为空或者未设置,则返回值word;但不改变变量的值。 |
${var:=word} | 如果变量为空或者未设置,则将变量的值设置为word,并返回值word。 |
${var:?message} | 如果变量为空或者未设置,则将message打印到标准错误输出。 |
${var:+word} | 如果变量设置了,则返回值word;但不改变变量的值。 |
后台运行
在shell中运行一个命令时,如果在命令后面加上&则可以将命令放到后台运行。这各技术同样也是用于shell脚本。对于shell脚本而言,除了能将已有的命令放到后台,还可以把一个函数放到后台运行。后台运行的本质,其实是创建一个子进程,在子进程中运行指定的命令或者shell脚本函数。shell中的特殊变量$!可以用于获取最近一次创建的子进程ID;而特殊变量$$则获取当前进程ID。运用后台运行技术,可以实现shell脚本的多任务并发,从而编写出功能强大的shell脚本。
以下是一个简单的例子及其运行结果。
#!/bin/sh
child1_pid=
child2_pid=
echo In parent, pid is $$
do_child1()
{
echo In child1
sleep 5
exit 0
}
do_child2()
{
echo In child2
sleep 5
exit 0
}
do_child1 &
child1_pid=$!
do_child2 &
child2_pid=$!
wait $child1_pid
echo child1 $child1_pid exited
wait $child2_pid
echo child2 $child2_pid exited
exit 0
In parent, pid is 73243
In child1
In child2
child1 73244 exited
child2 73245 exited
参数处理
在shell脚本编程中,有两种情况会用到参数传递:脚本调用和函数调用。调用脚本时,$0固定为脚本命令本身,$1为第一个参数,$2为第二个参数,以此类推。调用函数时,也采用类似的方式,即$1为第一个参数,$2为第二个参数,以此类推。需要注意的是,不论在函数里还是函数外,$0都表示脚本命令,这时特殊指出。另外脚本调用和函数调用都采用特殊变量$#来表示参数个数,而特殊变量$*则表示所有参数。特殊函数shift可用于参数左移,不带参数表示左移一个,即$2变成$1。
以下例子演示了脚本参数和函数参数。
#!/bin/sh
echo script command: $0
echo Number of script arguments: $#
echo All arguments: $*
arg_func()
{
echo inside function, script arg0: $0
echo Number of function arguments: $#
while [ $# != 0 ]; do
echo function arg: $1
shift
done
}
arg_func
arg_func 1 2 3
while [ $# != 0 ]; do
echo script arg: $1
shift
done
运行这个脚本并传入参数得到如下结果:
./test-arg.sh a b c d e f
script command: ./test-arg.sh
Number of script arguments: 6
All arguments: a b c d e f
inside function, script arg0: ./test-arg.sh
Number of function arguments: 0
inside function, script arg0: ./test-arg.sh
Number of function arguments: 3
function arg: 1
function arg: 2
function arg: 3
script arg: a
script arg: b
script arg: c
script arg: d
script arg: e
script arg: f