2. 参数扩展
最简单的参数扩展情况如下:
foo=fred
echo $foo
当我们想在变量名里额外添加字符的时候就会遇到问题. 假设我们想编写一个简短的脚本程序来处理名为1_tmp和2_tmp的2个文件. 我们试试如下的办法:
#!/bin/sh
for i in 1 2 3
do
my_secret_process $i_tmp
done
但是在每次的循环里,我们都会看到如下的出错信息:
my_secret_process: too few arguments
到底在哪里出错了呢?
问题在于shell试图替换变量"$i_tmp",而这个变量其实并不存在. 但shell并不会认识到这个错误,相反,它会把一个空值做这个并不存在的变量的值. 因此根本不会有参数被传递到my_secret_process. 为了解决变量名里类似于"$i"这样的扩展情况,我们需要把"i"扩在一对花括号{}里,就像下面这样:
#!/bin/sh
for i in 1 2 3
do
my_secret_process ${i}_tmp
done
以后,在每次循环里,"i"的值被替换为"$(i)",从而给出了正确的文件名. 也就是说,我们把参数的值替换为一个字符串了.
在shell里我们可以采用很多种参数替换办法. 这样做通常都会让我们得到一个精巧的解决方案.
常见的参数扩展替换如下:
${param:-default} 如果param是空,就把它设置为default的值
${#param} 给出param的长度
${param%word} 从param的尾部开始删除匹配word的最小部分并返回剩余部分
${param%%word} 从param的尾部开始删除匹配word的最大部分并返回剩余部分
${param#word} 从param的头部开始删除匹配word的最小部分并返回剩余部分
${param##word} 从param的头部开始删除匹配word的最大部分并返回剩余部分
在对字符串进行处理的时候,这些扩展替换经常是很有用的. 特别是对字符串进行部分删除的那后4个,在对文件名和路径进行处理时作用极大. 请看下面的例子:
#!/bin/sh
unset foo
echo ${foo:-bar}
foo=fud
echo ${foo:-bar}
foo=/usr/bin/X11/startx
echo ${foo#*/}
echo ${foo##*/}
bar=/usr/local/etc/local/networks
echo ${bar%local*}
echo ${bar%%local*}
exit 0
上面的程序输出如下:
bar
fud
usr/bin/X11/startx
startx
/usr/local/etc/
/usr/
注释:
第一个语句"${foo:-bar}" 给出的值是"bar",因为在这条语句被执行的时候foo没有任何值.但变量foo并没有发生任何变化,它还停留在unset状态.
如果这条语句是"${foo:=bar}", 就会把此变量设置未$foo,这个字符串操作符的作用是检查foo是否存在且不为空. 如果是,它返回变量的值;否则就把foo赋值为bar并返回它.
"${foo:?bar}"将在foo不存在活被置为空的情况下显示"foo:bar" 并放弃这条命令.
"${foo:+bar}" 会在foo存在并不为空的情况下返回bar.
根据UNIX系统中管道的概念,前一个操作的结果经常需要通过人工进行重定向.假设需要用cjpeg这个程序把一个gif文件转换为jpeg文件:
$ cjpeg image.gif > image.jpg
但是,如果文件很多时,怎么让重定向自动进行呢? 很简单,这样就可以了:
#!/bin/sh
for image in *.gif
do
cjpeg $image > ${image%%gif}jpg
done
这个脚本可以起名为GifToJpeg,它在当前子目录里为每个gif文件创建一个jpeg文件.
~
~
~