文件测试
文件测试是编写可靠脚本的一个必要部分。
当if语句出现嵌套时,fi语句总是对应离它最近的if语句。以逐层缩进方式编排的if嵌套语句可以使if语句和fi语句的对应关系看起来更清楚易懂。
#!/bin/sh
file=./testing
if [ -d $file ]
then
echo "$file is a directory"
elif [ -f $file ]
then
if [ -r $file -a -w $file -a -x $file ]
then # nested if command
echo "You have read, write, and execute permission on $file,"
fi
else
echo "$file is neither a file nor a directory,"
fi
null命令
冒号代表的null命令是shell的一个内置命令,它不做任何事情,只返回退出状态0。
null命令有时被放在if命令后面作为一个占位符,这时if命令不执行实际操作,只需要在then后面放置一条命令,否则,程序会生成报错信息,因为then语句后面必须添加内容。
null命令通常被用作循环命令的参数,其作用是让循环无限地进行下去。
脚本范例
#!/bin/sh
name=Tom
if grep "$name" databasefile > /dev/null 2>&1
then
:
else
echo "$1 not found in databasefile"
exit 1
fi
交互式范例
[root@centos fuhao]# DATAFILE=
[root@centos fuhao]# : ${DATAFILE:=$HOME/db/datafile}
[root@centos fuhao]# echo $DATAFILE
/root/db/datafile
[root@centos fuhao]# : ${DATAFILE:=$HOME/junk}
[root@centos fuhao]# echo $DATAFILE
/root/db/datafile
case命令
case命令是一个多路分支命令,可用来代替if/elif命令。case变量的值与值1、值2等值逐一比较,直至找到与之匹配的值。如果某个值与case变量匹配,程序就执行该值后面的命令,直至遇到双分号,然后跳到词esac后面继续往下执行。如果没有找到与case变量匹配的值,程序就执行默认值*)后面的命令,直至遇到;;或esac。
值*)的功能与if/else条件中else语句的相同。case的值里可以用shell通配符,还可以用竖杠(管道符)对两个值取或。
case的值以单个圆括号终止。
注意:把双分号单独写在一行可以方便对脚本的调试。
双分号用于结束语句块,且控制跳转到esac之后。
如果所有值都不能匹配case表达式,就执行*)后面的命令。
esac语句结束case命令。
#!/bin/sh
# Scriptname: colors
echo -n "Which color do you like?"
read color
case "$color" in
[Bb]l??)
echo I feel $color
echo The sky is $color
;;
[Gg]ree*)
echo $color is for trees
echo $color is for seasick;;
red | orange) # The vertical bar means "OR"
echo $color is very warm!;;
*)
echo No such color as $color;;
esac
echo "Out of case"
here文档常与case命令搭配使用。
可以用here文档生成一个显示在屏幕上的选项菜单,要求用户选择一个菜单项,然后用case命令对照选项集测试用户的输入,以执行相应的命令。
#!/bin/sh
echo "Select a terminal type: "
cat << ENDIT
1) vt 120
2) wyse50
3) sun
ENDIT
read choice
case "$choice" in
1) TERM=vt120
export TERM
;;
2) TERM=wyse50
export TERM
;;
3) TERM=sun
export TERM
;;
esac
echo "TERM is $TERM."
--------循环命令--------
for循环命令在某个对象列表上按指定次数执行命令。
格式
for 变量 in 词表
do
命令(命令组)
done
#!/bin/sh
# Scriptname: backup
# Purpose:
# Create backup files and store them in a backup directory
dir=/root/fuhao/testing/backupscripts
echo "$dir"
for file in memo[1-5]
do
if [ -f $file ]
then
cp $file $dir/$file.bak
echo "$file is backed up in $dir"
fi
done
词表中的$*和$@扩展的结果几乎完全一样,唯一的不同是当它们被括在双引号中时,$*的值是一个字符串,而$@的值则是一组独立的词。
#!/bin/sh
# Scriptname: permx
for file # Empty wordlist
do
if [ -f $file -a ! -x $file ]
then
chmod +x $file
echo $file now has execute permission
fi
done
While对紧跟在它后面的那条命令求值,如果该命令的退出状态为0,就执行循环体内的命令。执行到关键字done后,控制回到循环的顶部,while命令再次检查那条命令的退出状态。循环将一直继续下去,直到while计算的那条命令的退出状态非0为止。该命令的退出状态非0时,程序将从关键字done之后开始执行。
格式
while 命令
do
命令(命令组)
done
#!/bin/sh
# Scriptname: sayit
echo Type q to quit.
go=start
while [ -n "$go" ] # Make sure to double quote the variable
do
echo -n I love you.
read word
if [ "$word" = q -o "$word" = Q ]
then
echo "I'll always love you!"
go=
fi
done
test命令(也是while后面的方括号)的选项-n用来测试字符串是否非空。
只要有分号分隔,就可以把do语句和while命令写在同一行上。
until命令只在它后面的命令失败时(即命令返回非0的退出状态时)才执行循环语句。执行到关键字done时,控制回到循环顶部,until命令再次检查其后那条命令的退出状态。直到until测到那条命令的退出状态变成0为止,循环退出。
格式
until 命令
do
命令(或命令组)
done
#!/bin/sh
# Scriptname: hour
hour=1
until [ $hour -gt 24 ]
do
case "$hour" in
[0-9] |1[0-1]) echo "Good morning!"
;;
12) echo "Lunch time."
;;
1[3-7]) echo "Siesta time."
;;
*) echo "Good night."
;;
esac
hour=`expr $hour +1`
done
循环控制命令
shift命令指定参数时,将参量列表左移指定的次数;没有给定参数时,shift命令仅把参量列表左移一次。一旦列表被移动,左端那个参数就被永远地删除了。while循环遍历位置参量列表时,经常会用到shift命令。
格式
shift [n]
#!/bin/sh
# Name: doit
# Purpose: shift through command-line arguments
# Usage: doit [args]
while [ $# -gt 0 ]
do
echo $*
shift
done
break命令用于强行退出循环,但不退出程序(退出程序要用exit命令)。break在退出无限循环时很有用。
格式
break [n]
#!/bin/sh
while true; do
echo Are you ready to move on\?
read answer
if [ "$answer" = Y -o "$answer" = y ]
then
break
else
ls
fi
done
echo "Here we are"
continue命令在条件为真时,把控制转回循环的顶部。continue下面的所有命令都被忽略。如果被嵌套在多层循环之内,continue将控制转回最内层循环的顶部。如果给它一个数字作为参数,continue就能将控制转到任一层循环的起点。假定嵌套了3层循环,最外层循环是第3层,中间那层是第2层,最里面那层是第1层循环。
不过,如果给continue命令的数字参数大于循环的层数,就退出整个循环。
格式
continue [n]
#!/bin/sh
# Scriptname: mailem
# Purpose: To send a list
for name in `cat mail_list`
do
if [ "$name" = "richard" ];then
continue
else
mail $name < memo
fi
done
使用嵌套循环时,可以给break和continue命令一个整型的数字参数,让控制可以从内层循环跳到外层循环。
shell可以通过管道或重定向将输入从文件传到循环,也可以通过管道或重定向将输出从循环传到文件。shell将启动一个子shell来处理I/O重定向和管道。
将循环的输出重定向到一个文件
#!/bin/sh
# Program name: numberit
# Put line numbers on all lines of memo
if [ $# -lt 1 ]
then
echo "Usage: $0 filename " >&2
exit 1
fi
count=1 # Initialize count
cat $1 | while read line # Input is coming from file on command line
do
[ $count -eq 1 ] && echo "Processing file $1... " > /dev/tty
echo $count $line
count=`expr $count + 1`
done > tmp$$ # Output is going to a temporary file
mv tmp$$ $1
把循环的输出通过管道传给UNIX命令
#!/bin/sh
for i in 7 9 2 3 4 5
do
echo $i
done | sort -n
在后台执行循环
#!/bin/sh
for person in bob jim joe sam
do
mail $person < memo
done &
关键字done后的符号&使得循环在后台运行。
使用exec命令,不需要创建子shell,就能打开或关闭标准输入和标准输出。这样,如果启动循环,循环体内创建的所有变量在循环终止后都还能保留。如果在循环中用了重定向,则循环内创建的所有变量都会消失。
exec命令常被用来打开文件(根据文件名或文件描述符)以供读写。注意:文件描述符0、1和2已预留给标准输入、标准输出和标准错误输出。文件打开后将得到下一个可用的文件描述符。
#!/bin/sh
# Scriptname: speller
# Purpose: Check and fix spelling errors in a file
exec < tmp # Opens the tmp file
while read line # Read from the tmp file
do
echo $line
echo -n "Is this word correct? [Y/N] "
read answer < /dev/tty # Read from the terminal
case "$answer" in
[Yy]*)
continue;;
*)
echo "What is the correct spelling? "
read word < /dev/tty
sed "s/$line/$word/g" tmp > error
mv error tmp
echo $line has been changed to $word.
esac
done
IFS和循环
shell的内部字段分隔符(internal field separator, IFS)的值包括空格、制表符和换行符。IFS被那些需要分析词列表的命令(如:read、set和for)用作词(token)分隔符。如果列表使用其他不同的分隔符,用户也可以重新设置IFS。改变IFS的值之前,最好先把它的初始值保存到另一个变量中,这样。一旦需要就可以很方便地把IFS恢复为默认值。
#!/bin/sh
# Scriptname: runit
# IFS is the Internal field separator and defaults to
# spaces, tabs, and newlines.
# In this script it is changed to a colon.
names=Tom:Dick:Harry:John
OLDIFS="$IFS" # Save the original value of IFS
IFS=":"
for persons in $names
do
echo Hi $persons
done
IFS="$OLDIFS" # Reset the IFS to old value
set Jill Jane Jolene # Set positional parameters
for girl in $*
do
echo Howdy $girl
done