利用Shell编程,我们可以方便的实现更复杂的功能。比如说列出当前目录下所有的C文件,我们可以
在命令行下直接输入以下脚本:
for file in *.c
do
print "$file"
done
我们可以把这些脚本写入文件中,通过调用文件来执行脚本,这对于编写大型脚本是十分有帮助的
。下面我就来介绍Shell编程的一些基本语法和方法。更详尽的用法读者可以参考Neil Matthew和
Richard Stones的著作Begining Linux Programming,笔者参考的是其第三版(Third Edition)。
1. 一个完整的实例
还是以上面打印出当前目录下所有包含main函数的C文件为例,首先创建一个文件sPrint,利用编辑
器输入以下脚本:
#!/bin/sh
for file in *.c
do
print "$file"
done
exit 0
将该文件保存后,在命令行下输入:
/bin/sh sPrint
脚本被执行,所有包含main函数的C文件都在屏幕上输出。
脚本以#!/bin/sh开始,在Shell脚本中,#后面的内容为注释,在本程序中#!后面的语句为将要执行
Shell脚本的程序,紧接着的脚本跟我们在命令行下输入的脚本完全一样,为逻辑程序;最后的exit命令
返回0表示脚本执行成功。前面在Linux文件系统一节中我们讲过,“grep EXIT_ /usr/include/*.h”命令可
以找出exit常量的定义,有兴趣的读者可以试试。另外需要说明的是,exit命令对脚本程序不是必须的
,但如果我们需要在其它地方调用该脚本,exit命令返回的代码可以让我们方便的判断该脚本是否执行
成功;如果你的脚本在没有定义退出状态的情况下退出,那么脚本中最后执行的那条命令的状态将作
为脚本的返回值。
也许有人会问,我们可不可以直接输入文件名来执行脚本呢?输入命令:ls -l sPrint,我们不难看出
,sPrint文件为不可执行文件,于是我们尝试着将其改为可执行文件:chmod +x sPrint。现在,我们在
命令行下再输入:./sPrint,当敲下Enter键后,哦也,成功啦!哈哈哈~~~~~~~
cat /etc/shells命令可查看当前系统可用的Shell,echo $SHELL命令可查看当前正在使用的shell,
2. 通过以上实例,我们对Shell编程已经有了一个整体的了解,现在我们就来学习Shell脚本的语法。
2.1 变量
在shell脚本中,所有的变量都被当做字符串,并且是大小写敏感的。变量无需声明,即可使用,当
我们需要引用变量的时候,我们需要在前面加上符号$,例如:
str=Hello
echo $str
如果给变量赋值时包含空格,这个值必须被引号包围,如:
str="Hello World"
echo $str
但如果是从终端读入变量,就不需要引号了,如:
read str
echo $str
注意:当对变量用单引号或反斜杠时,变量不发生值的替代,而双引号照常替代,如:
Str=Hello
echo '$Str'
echo /$Str
echo “$Str"
结果为:
$Str
$Str
Hello
虽然变量无需声明便可使用,但若没有赋值即使用,系统会报错:ksh: var: parameter not set。以下
Shell用于判断变量是否定义:
if [[ ${var+""} = "" ]] #或者可用if [ ${var:-""} = "" ],此语句为变量替代,读者可参考后面的2.7.2
then
echo var not defined !
else
echo $var
fi
2.2 一些重要的环境和参数变量
变量 描述
$HOME 主目录
$PATH 命令的搜索目录
$IFS 输入分隔符
$0 Shell脚本的名字
$# 传递参数的个数
$$ Shell脚本的进程ID,在脚本内部通常用来生成临时文件的名字
$1,$2…… 指定的参数
$* 一个变量中由IFS变量定义的分隔符所分割的所有参数
$@ 同$*,但不受IFS变量影响
举例:
IFS=’’
set foo bar bob
echo “$@”
foo bar bob
echo “$*”
foobarbob
unset IFS
echo “$*”
foo bar bob
一个脚本示例:
#!/bin/sh
#*******tVar.sh*******
salutation=”Hello”
echo $salutation
echo “The program $0 is now running”
echo “The second parameter was $2”
echo “The first parameter was $1”
echo “The parameter list was $*”
echo “The user’s home directory is $HOME”
echo “Please enter a new greeting”
read salutation
echo $salutation
echo “The script is now complete”
exit 0
运行脚本:
$ ./tVar.sh foo bar bob
Hello
The program ./try_var is now running
The second parameter was bar
The first parameter was foo
The parameter list was foo bar bob
The user’s home directory is /home/Ubuntu
Please enter a new greeting
Nice
Nice
The script is now complete
注意:用env命令可以查看当前系统的运行环境
2.3 布尔逻辑判断
在Shell脚本中,test或者"["命令是用于逻辑判断的(可在/usr/bin目录下查看),如:
if test -f fred.c
then
............
fi
或者,
if [ -f fred.c ]
then
............
fi
test命令常用来进行三种条件的逻辑判断,即:字符串比较,算术比较,与文件相关的条件判断
字符串比较 结果
string1 = string2 相等为真
string1! = string2 不相等为真
-n string 不为空为真
-z string 空为真
注意:”=“和变量之间必须有空格分隔
算术比较 结果
expr1 -eq expr2 相等为真
expr1 -ne expr2 不相等为真
expr1 -gt expr2 前者大于后者为真
expr1 -ge expr2 前者不小于后者为真
expr1 -lt expr2 前者小于后者为真
expr1 -le expr2 前者不大于后者为真
!expr1 表达式为假为真,反之亦然
文件条件 结果
-d file 文件为目录为真
-e file 文件存在为真
-f file 文件为常规文件为真(不包括目录),常用来判断文件是否存在
-g file 文件许可权为组用户为真
-r file 文件可读为真
-s file 文件非空为真
-u file 文件许可权为创建者为真
-w file 文件可写为真
-x file 文件可执行为真
举例:
#!/bin/sh
#*******tCheck.sh*******
str1='hello'
str2='hello world'
expr1=96
expr2=13
if test ”$str1“ = ”$str2“
then
echo The two string is equal
else
echo /$str1 and /$str2 is not equal
fi
if [ $expr1 -gt $expr2 ]
then
echo /$expr1 is greater than /$expr2
else
echo /$expr1 is not greater than /$expr2
fi
if test -f fred.c
then
echo File Exist!
else
echo File not Exist!
fi
结果:
/$str1 and /$str2 is not equal
/$expr1 is greater than /$expr2
File not Exist!
注意:使用”["时,"["和变量之间应有空格分隔,你把它看成一个字符串就行了(你不妨试试ls -l
/usr/bin/[ 命令)。在test语句中,为了防止意想不到的结果发生,需要将变量以双引号包围。有兴趣的读
者可以试试不加双引号会出现什么结果。
2.4 流程控制
2.4.1 if语句
看到这里,相信读者已经对if语句有了一个初步的了解,不论为何种语言,if语句都是流程控制的基
础,在Shell脚本中,if语句的语法为:
if condition
then
statements
else
statements
fi
这里需要说明的是if语句以fi结束,此外,关键字then和if不是写在同一行的,如果你习惯将他们写在
同一行,则需要在condition后面加分号”;“区分;当if语句有多条分支时,可以根据需要增加elif关键字
,相当于C语言中的else if。示例:
#!/bin/sh
#*******iChoice.sh********
echo Is it morning? Please answer yes or no:
read tDay
if [ "$tDay" = "yes" ]
then
echo Good morning!
elif [ "$tDay" = ”no" ] ; then
echo Good afternoon!
else
echo Sorry, input error!
exit 1
fi
exit 0
2.4.2 for语句
还记得我给出的Shell快速入门的例子吗?那是一个典型的for语句。for语句的语法结构为:
for variable in values
do
statements
done
其中value可为字符串集或Shell命令
例1:
for var in foo bar bob
do
echo $var
done
结果:
foo
bar
bob
例2:
for file in $(ls *.sh)
do
lpr $file
done
结果:
当前目录下所有的.sh文件被打印
2.4.3 while语句
while语句的语法结构为:
while condition
do
statements
done
示例:
wNum=1
while [ "$wNum" -le 10 ]
do
echo Go on!
wNum=$(($wNum+1))
done
上面的例子中用到了$(( ))结构,该结构和老的Shell中的expr关键字一样,用于表达式的计算,但前
者性能更加优越。
一个文件拷贝的程序
#!/bin/ksh
while read LINE
do
cp /rbmwes/work/20100618/dba/$LINE /home/bmwwork/CN
echo #LINE
done < /rbmwes/work/20100618/bin/bmw_dba.lst
此例将bmw_dba.lst中列出的所有文件拷贝到/home/bmwwork/bwm目录
2.4.4 until语句
until语句的语法结构为:
until condition
do
statements
done
until语句的语法结构和while语句相同,不同的是until语句当条件不为真时执行。
示例:
uNum=1
until [ "$wNum" -gt 10 ]
do
echo Go on!
uNum=$(($uNum+1))
done
上例中until语句循环执行十次后,当nNum为11,即条件为TRUE时终止执行。
2.4.5 case语句
case语句用于多路分支的情况,它的语法结构为:
case variable in
pattern [ | pattern]...) statements ;;
pattern [ | pattern]...) statements ;;
......
esac
每个case分支可以包含多个匹配的式样,多条执行的语句,双分号";;"表示一个case分支的结束
#!/bin/sh
#*******cChoice.sh********
echo Is it morning? Please answer yes or no:
read tDay
case "$tDay" in
[y|Y] | [yY] [eE] [sS])
echo Good morning!
echo Enjoy sunshine!
;;
[n|N] | [n|N] [o|O])
echo Good afternoon!;;
*)
echo Sorry, input error!
exit 1
;;
esac
exit 0
注意:最上面的case分支优先执行,所有必须注意case分支匹配变量的顺序
2.4.6 break语句
break语句用在循环语句中终止当前循环体的执行,如:
#!/bin/sh
for file in *
do
if [ -d "$file" ]
then
break
fi
cat “$file”
done
echo “$file” is a directory
exit 0
上述代码用于向屏幕输出文件内容(当然可改为打印文件),当遇到目录时终止代码的执行
2.4.7 continue语句
continue语句用在循环语句中终止当次代码的执行,并不退出循环体,如:
#!/bin/sh
for file in *
do
if [ -d "$file" ]
then
echo "$file" is a directory
continue
fi
cat "$file"
done
exit 0
该代码依然用于向屏幕输出文件内容,但当遇到目录时,程序并不停止执行,直到所有的文件内容
都被输出。
2.4.8 List语句
这里的List语句指命令列表,分为与List和或List,List语句中的命令从左至右依次执行,但与List只有
在前面的命令为TRUE时才执行后面的命令,当有命令为FALSE时立即停止执行;而或List刚好相反,
当前面的命令为FALSE时才执行后面的命令,当有命令为TRUE时立即停止执行
与List的语法结构为:
statement1 && statement2 && statement3.......
或List的语法结构为:
statement1 || statement2 || statement3.......
举例:
[ -d MyDirectory ] && echo Directory exist! || mkdir MyDirectory || echo Greate Failed!
上面的例子用于判断目录是否存在,如果存在就在屏幕上输出目录存在的信息,否则就创建该目录
,如果目录创建失败,比如说已经有一个同名文件,就在屏幕上输出目录创建失败的消息。该例的综
合性很强,囊括了两种List的用法。比较通用的一个句型为:
[ -f file ] && command for true || command for false
读者可根据自己的需要,灵活运用List句型。此外,还有一点要说明的是,我们可以用大括弧{ }组成
代码块,比如说上例中,我们希望目录创建成功后也给出提示信息,可以这样写:
#!/bin/sh
[ -d MyDirectory ] && echo Directory exist! || { mkdir MyDirectory && echo Directory Create!
} || echo Greate Failed!
注意:在 echo Directory Create! 语句后必须换行,使用“{ }”时,“}”必须另起一行书写。如果你一定要
将它们写在同一行,可用分号“;”分隔,即:echo Directory Create! ; } || echo Greate Failed! ,这种
方法在前面介绍if语句的时候提过,其实这是一种对多行命令普遍适用的方法。
2.5 函数
函数是一系列代码的集合,用于完成特定的功能。使用函数可以让Shell脚本更加结构化。
2.5.1 一个简单例子
#!/bin/sh
#*******funcDemo*******
foo () {
echo Function foo is executing.......
echo The first parameter is $1
echo Function ended
}
echo Script starting......
foo $*
echo Script ended
exit 0
首先,为脚本添加执行权限:chmod +x funcDemo
运行脚本:./funcDemo foo bar bob
结果:
Script starting......
Function foo is executing.......
The first parameter is foo
Function ended
Script ended
上述脚本首先定义了一个函数foo,在后面的脚本中调用了该函数,并将脚本的参数列表传递给了此
函数,函数内部会打印出其参数列表的第一个参数。
注意:在调用函数之前必须给出函数的定义,通常的做法是在脚本开始给出所有函数的定义
2.5.2 变量
变量分为全局变量和局部变量,在函数内部可用local关键字声明局部变量,否则就是全局变量。局
部变量的作用域仅在函数内部,如果局部变量的名字与先前定义的全局变量重名,则在函数内部全局
变量将被覆盖。举例:
#!/bin/sh
#*******varDemo*******
str="global variable"
foo () {
echo Function foo is executing.......
local str="local variable"
var="new variable"
echo print $str
echo Function ended
}
echo Script starting......
foo $*
echo print $str
echo print $var
echo Script ended
exit 0
运行结果:
Script starting......
Function foo is executing.......
print local variable
Function ended
print global variable
print new variable
Script ended
2.5.3 返回值
在函数内部可用return命令返回数字,而让函数返回字符串的一般做法是将字符串存储在一个变量中
,当函数调用结束后就可以使用该变量了。
举例:
#!/bin/sh
#*******retDemo*******
yes_or_no() {
echo “Is your name $* ?”
while true
do
echo -n “Enter yes or no: “
read x
case “$x” in
y | yes ) return 0;;
n | no ) return 1;;
* ) echo “Answer yes or no”
esac
done
}
echo “Original parameters are $*”
if yes_or_no “$1”
then
echo “Hi $1, nice name”
else
echo “Never mind”
fi
exit 0
运行结果:
Original parameters are foo bar bob
Is your name foo?
Enter yes or no: yes
Hi foo, nice name
此外,在函数内部,echo命令也可以向外部输出变量,如:
foo () {
echo "Hello world" ; }
result=$(foo)
2.6 常用命令
当你执行一个命令的时候.系统的寻找方式是: alias / function / 内部命令/外部命令
2.6.1 点命令(.)
当Shell脚本在执行外部命令或者调用其它脚本的时候,通常会为它们创建新的运行环境(即subshell)
,而当它们执行完毕后父Shell又会遗弃该坏境,所以对该环境做的任何修改都会遗失。在实际工作中
,我们往往希望先用脚本为我们随后运行的应用配置环境,这在管理多工程时尤为重要。比如说前面
在介绍Linux库文件时提到的设置加载动态链接库路径的LD_LIBRARY_PATH变量,我们可以在脚本中
修改该变量的值,当该脚本终止执行后,我们发现LD_LIBRARY_PATH变量的值并没有改变!不过,
幸运的是source和点命令为我们实现这种技术提供了可能,这使得命令列表可以与调用它们的脚本在同
一个Shell环境下执行。
举例:
#!/bin/sh
#*******classic_set*******
version=classic
PATH=$PATH:.
#!/bin/sh
#*******latest_set*******
version=latest
PS1="Junlixxu: ->>"
运行:
$ chmod +x classic_set
$ classic_set
$ bash: classic_set: command not found
$ ./classic_set
$ echo $version
$ . ./classic_set
$ echo $version
classic
$ chmod +x latest_set
$ latest_set
$ echo $version
classic
$ . latest_set
Junlixxu: ->>echo $version
latest
Junlixxu: ->>
上述程序实例请读者自行揣摩,有一点需要提醒的是:“."命令与其它命令之间有空格分隔。
2.6.2 冒号命令(:)
":"命令是一个空命令,当用在循环语句中作为判断条件时相当于true,即while :与while true是等价
的。不过":"是内部命令,比用true时的速度更快。
实例:
#!/bin/sh
if [ -f fred.c ]
then
:
esle
echo File not exist!
fi
在上述实例中,若文件fred.c存在,程序将什么也不做。
2.6.3 eval命令
eval命令用来取变量的值,而这个变量也为变量的值,例如:
foo=hello
x=foo
eval y='$'$x
echo $y
结果为:hello
如果不用eval关键字,则结果为:$foo
2.6.4 exec命令
exec命令有两种用途,第一种为以其它程序取代当前Shell的执行,如:
exec -c vi hello.c
在脚本中,上述命令将使vi命令取代当前Shell的执行。当你退出vi编辑器的时候你就会发现,先前打
开的终端关闭了!如果执行脚本的程序根本不存在,上述脚本行之后的脚本将不会被执行。
exec命令的第二种用法为修改当前的文件描述符,如:
exec 3< afile
上述命令将使文件描述符3打开使得从文件afile中读取数据,不过这种用法很少使用。
2.6.5 exit命令
exit命令用于退出脚本的执行并返回一个代表脚本执行状态的代码n,如果你在命令提示符下输入exit
命令,将导致结束当前终端的执行。如果你的脚本在没有定义退出状态的情况下退出,那么脚本中最
后执行的那条命令的状态将作为脚本的返回值。
在脚本编程中,状态代码0表示执行成功,而1-125为用户自定义错误代码,即你可以根据自己的需
要指定它们表示的意义,而不需要定义全局错误变量。126及以后的数字为保留数字,用于特殊含义,
如:
126 文件不可执行
127 找不到命令
128及以上 信号发生
举例:
#!/bin/sh
if [ -f fred.c ]; then
exit 0
fi
exit 1
2.6.6 export命令
通常情况下,父Shell中定义的变量在子Shell中是不可使用的,而如果在定义变量的时候使用了export
关键字,那么就可以在子Shell中使用该变量了。export命令为它的参数创建一个环境变量,当前Shell调
用的所有脚本和程序都可以继承该变量。
举例:
#!/bin/sh
#*******export1*******
echo $foo
echo $bar
exit 0
#!/bin/sh
#*******export2*******
foo="variable foo"
export bar="variable bar"
./export1
exit 0
运行:
$ chmod +x export1
$ chmod +x export2
$ ./export2
variable bar
$
在上面的例子中,脚本export2调用了脚本export1用于输出export2中定义的两个变量,由运行结果可
以看出,只有用export关键字声明的变量bar可以使用。
实际上,在上例中,由脚本export1调用的脚本仍可以使用变量bar,即如果在一个脚本中由export关
键字声明了一个变量,那么在该脚本调用的脚本,调用脚本调用的脚本中都可以使用该变量。读者不
妨将脚本export1拷贝一份,然后在export1中再调用拷贝脚本试试。
命令set -a或者set -o allexport将会导出此后定义的所有变量。
2.6.7 expr命令
expr其实就是一个命令行计算器,一般格式为:
expr arguement operator arguement
如:
$ expr 3 + 5
8
$ expr 13 - 5
6
$ expr 6 /* 9
54
$ expr 45 / 7
6
$ expr 45 % 7
3
注意:使用乘号时,必须用反斜线屏蔽其特定含义,否则shell可能因误解星号的意义报告语法错误
expr命令还可以用于字符串操作,如:
$ expr length "hello world"
11
$ expr substr "hello world" 3 5
llo w
变量赋值:
$ x=0
$ x=`expr $x +1`
$ echo $x
1
注意:语句x=`expr $x +1`与x=$(expr $x+1)功用相同,需要提醒的是前者中的符号并非单引号,而
是与“~”同键的那个反单引号。关于expr命令更详细的用法,请在命令提示符下输入man expr命令查看
。在执行expr命令时会调用新的Shell来处理expr命令,所用速度较慢;在现代脚本中,一般用$((......))
句型进行表达式的赋值运算,这在前面讲while等流程控制语句时已经介绍过。
2.6.8 printf函数
和echo函数一样,printf函数用于字符串的输出,只不过printf函数多了一些格式化的参数,很像C语
言中的printf函数,如:
$ printf “Hello %s,your age is %d/n" bob 19
Hello bob, your age is 19
一般来说,这里的printf函数不支持浮点数,因为Shell中的算术运算通常为整算。
2.6.9 set命令
前面讲一些重要的环境和参数变量的时候,我已经用到了set这个命令。set命令用于将字符串参数化
。我们知道可以用date命令得到当前系统的日期,但如何从这个日期中取出我们关注的参数呢?这时
set命令就发挥作用了!比如说取当前系统时间:
set $(date)
echo $4
2.6.10 unset命令
unset命令用于从当前环境中删除变量或者函数,如unset x将把变量x从当前环境中删除。有一点需
要注意的是"x="命令只是将x置为null,但仍然存在,但unset命令将从环境中彻底删除变量。此外,
unset命令不能删除Shell本身定义的只读变量,比如说前面提到的IFS变量
2.6.11 shift命令
shift命令用于左移参数列表中的变量,如:
#!/bin/sh
#******shiftDemo*******
while [ "$1" != "" ]
do
echo $1
shift
done
exit 0
运行:
$ chmod +x shiftDemo
$ ./shiftDemo foo bar bob
foo
bar
bob
$
2.6.12 trap命令
trap命令用于定义当特定信号发生时所采取的命令,其一般格式为:
trap command signal
可用man -l 命令查看系统定义的信号及其数字,用man signal命令可查看信号的详细信息。
举例:
#!/bin/sh
#*******trapDemo*******
trap 'rm -f /tmp/my_tmp_file_$$' INT
echo creating file /tmp/my_tmp_file_$$
date > /tmp/my_tmp_file_$$
echo Press interrupt (CTRL+C) to interrupt.......
while [ -f /tmp/my_tmp_file_$$ ]
do
echo File exists
sleep 2
done
echo The file no longer exists
exit 0
在上面的脚本中,首先由trap命令定义了当INT信号发生时的一个rm操作,接着利用进程ID创建了一
个临时文件,并向这个临时文件中写入了当前系统的日期时间,后进入while循环,判断文件是否存在
,若存在将当前进程休眠2秒,当按下CTRL+C键后,rm操作发生,文件被删除,脚本终止。
2.7 辅助命令
2.7.1 捕获命令
这里所谓的捕获命令是指取得命令执行的结果并为我们所用。前面讲expr命令时提过和”~“同键的那
个反单引号可用于获取表达式的执行结果,事实上这是一个普遍适用的命令。在现代脚本中我们通常
用$(...)命令取代``命令,因为它避免了后者在命令中使用$,`,/等字符时的复杂转换规则,后者在使用这
些字符时需要”/"来进行转义。
$(...)命令举例:
#!/bin/sh
whoisthere=$(who)
echo The current directory is $PWD
echo The current users are $whoisthere
exit 0
如果你想捕获那些执行结果为输出到标准输出设备的命令的输出参数并为我们的程序所用,你可以
试试xargs命令。举例:
描述:输出当前目录下所有符号链接或经过压缩的ASCII文件并显示
命令:file -Lz * | grep ASCII | cut -f1 -d: | xargs ls -ltr
解释:
file -Lz *:用于查找是符号链接或者经过压缩的文件;
输出结果:
anaconda-ks.cfg: ASCII English text
install.log: ASCII text
insall.log.syslog: ASCII text
mbox: ASCII mail text
grep ASCII:用于搜索包含 "ASCII" 字符的字符串并产生;
输出结果:
anaconda-ks.cfg: ASCII English text
install.log: ASCII text
insall.log.syslog: ASCII text
mbox: ASCII mail text
cut -f1 -d:表示采用冒号作为分隔符,并取出第一列。所以尽显示文件名;
输出结果:
anaconda-ks.cfg
install.log
install.log.syslog
mbox
xargs:将上述列表作为参数进行传递,一次传递一个。
xargs ls -ltr:用于接收输出并对其执行 ls -ltr 命令,命令等价于:
ls -ltr anaconda-ks.cfg
ls -ltr install.log
ls -ltr install.log.syslog
ls -ltr mbox
(此例来自:http://blog.youkuaiyun.com/madding/archive/2009/07/23/4372851.aspx)
2.7.2 参数扩展
我们可以用"{ }"命令进行参数扩展,如:
ls -l export{1,2,3}
将会列出文件export1,export2和export3的详细信息
ls -l DCMS_{Prototype,FunctionSpecification}_20091022
道理同上。
当文件很多时通过这种枚举的方法显然非常雍沓,我们能否通过变量替换实现这种效果呢?
如:
#!/bin/sh
#*******maketemp*******
for i in 3 25 459 234 123 789 345 664 86 527
do
date > my_temp_file_${i}
sleep 1
done
exit 0
#!/bin/sh
#*******cptemp*******
i=1
while [ "$i" -lt 1000]
do
if [ -f my_temp_file_${i} ]
then
cp my_temp_file_${i} /home/project/my_file_${i}
fi
i=$((i+1))
done
exit 0
上述第一个脚本用于模拟产生一些临时文件,而第二个脚本则将临时文件拷贝到系统home目录下一
个名为project的目录下,看了这个例子,相信大家对参数扩展已经有了熟悉的认知,事实上,在Shell
脚本中我们可以使用多种方式的变量替代:
参数扩展 描述
${param:-default} 如果参数为空,就用default替代
${#param} 指定参数的长度
${param%word} 从参数最后删除与word匹配的最小部分并返回剩下的部分
${param%%word} 从参数最后删除与word匹配的最大部分并返回剩下的部分
${param#word} 从参数最前删除与word匹配的最小部分并返回剩下的部分
${param##word} 从参数最前删除与word匹配的最大部分并返回剩下的部分
这些替代常常用在针对字符串的操作,后面四条规则对于处理文件名和路径非常有帮助。举例:
#!/bin/sh
#*******SubstDemo*******
unset foo
echo ${foo:-bar}
foo=bob
echo ${foo:-bar}
foo=/unbuntu/MyProject/my_file/my_file_x
echo ${foo%my_file*}
echo ${foo%%my_file*}
foo=/usr/local/bin
echo ${foo#*/}
echo ${foo##*/}
exit 0
运行结果:
bar
bob
/unbuntu/MyProject/my_file/
/unbuntu/MyProject/
usr/local/bin
bin
再看一例:
#!/bin/sh
#*******getJpg******
for image in *.gif
do
cjpeg $image > ${image%%gif}jpg
done
exit 0
在此例中,cjpeg命令将为当前目录下的所有GIF文件创建一个JPEG文件。