shell脚本编程技巧几例

条件判断

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

 

上一篇:express中app.get和app.use的解析


下一篇:Linux系统编程之线程深度详解(有实例)