第四部分 基础Shell编程
十六、Shell脚本介绍
增加脚本执行权限,用 chmod u+x name.sh
执行脚本,使用 ./name.sh
十七、条件测试
测试一些条件,返回结果储存在 $? 中。测试条件,来实现一些分支判断。
使用test命令,或者[空格 命令 空格]来执行测试
文件测试的选项有:
-d 文件夹
-f 普通文件
-L 链接文件
-r 可读
-w 可写
-x 可执行
-s 非空文件
例如$ [ -w myfirstc.c ]
$ echo $?
0
逻辑连接两个测试:
-a 与运算,如[ -w file1 -a -w file2 ]
-o 或运算,如[ -r file1 -o -r file2 ]
! 非运算,如[ ! -w myfirstc.c ]
字符串的测试:
= 字符串相等,如[ "$STR1" = "$STR2" ]。注意,比较两个字符串变量时,必须在变量外加引号。
!= 字符串不等,如[ "good" != "bad" ]
-z 空串测试,如[ -z $EDITOR ]
-n 非空串测试,如[ -n $EDITOR ]
数值测试:
-eq ==
-ne =
-lt <
-le <=
-gt >
-ge >=
同样的,比较的为数字变量时,要在变量外加上引号,数字也要加引号,如[ "$NUM" -eq "20" ]
expr命令的用法
命令expr一般用于整数值,也可用于字符串。其一般用法有:
循环中实现计数,类似i++。
$ LOOP=0
$ LOOP= `expr $LOOP + 1`
测试一个变量是否为数值:
$ VALUE=12
$ expr $VALUE + 10 > /dev/null 2>&1
$ echo $?
0
如果$VALUE不是一个数字,则$?返回一个非零的数字。
用来模式匹配:
没看懂,暂时略去。
十八、控制流结构
if then else fi 控制
模式一:
if [ 测试语句 ] &&或者|| [ 其他测试语句 ]
then
执行语句
elif
执行语句…
else
执行语句
fi
模式二:
if 某shell命令 >/dev/null 2>&1
then
执行语句
else
执行语句
fi
如果if的同一行跟着一个then的话,测试的条件或者shell命令后面要加分号,再加then。
if [ 语句 ]; then
一些特殊的:
if [ -t ]; then 用于测试是否脚本运行于交互模式
if [ $# -eq "3" ]; then 用于检测传给脚本的参数个数
echo "`basename $0` : it's right!"
fi
null符号: ,即冒号。表示什么也不做,相当于Python中的pass语句。
if [ -z "`ls -A $DIRNAME`" ]; then
do something
else: #什么也不做
fi
case 语句控制
case 值 in
模式1)
命令1…
;;
模式2)
命令2…
;;
esac
模式中,可以使用*, ?, [范围], 模式1|模式2,
for in do done语句控制
for 变量名 in 列表
do
命令1
命令2
done
一些简单例子
for loop in 1 2 3 4
for loop in "orange potato apple"
for i in `ls`
使用参数
for param in "[email protected]"
for param in "$*"
变量计数实现i++
counter=0 #必须初始化!
for filename in `ls`
do
counter=`expr $counter + 1`
done
echo "Total number of the files is $counter"
while以及until循环
while 或 until [ 测试语句 ]
do
命令…
done
一个until的例子,监控root用户的登录
#! /bin/sh
IS_ROOT=`who | grep root`
until [ "$IS_ROOT" ]
do
sleep 5
done
echo "it's the root"
这里虽然IS_ROOT是在循环体外定义执行的,但是每次循环还是会执行并更改它的值?只是猜测。
while read NAME DEPT SALARY
do
echo "$NAME\t$DEPT\t$SALARY"
done
如何实现c中的while(1)循环
while : #注意null符号“:”的使用,使这个while成为一个死循环
break和continue
break 跳出最内层的整个循环
continue 跳过本步循环,开始下一步,并不跳出循环
再次提到如何输出文本块,类似Python中的'''符号,使用:
cat <<SOMETHING
blah blah
wahaha
SOMETHING
十九、Shell中使用函数
定义函数,使用:
function 函数名()
{
…
}
向函数传递参数:
还是使用$0…$9这样,最好复制到函数内的一个变量,避免意外更改参数并更有意义。
返回值
return
return 0 #无出错返回
return 1 #有出错返回
在调用者用测试返回值,使用:
foo $VAR
if [ $? -eq "0" ]; then
或者
if foo $VAR; then
创建函数文件
在开头加入 #! /bin/sh
写入各种函数
包含函数和常量
使用时,在调用者的开头写上这个函数文件的名称即可包含其中的函数,以及常量
删除函数
使用unset func_name
二十、向脚本传递参数
使用shift命令
从参数列表的最左端取得$1参数,然后shift,即可使参数列表左移一个位置,原来的$2变为$1。这样的好处是在一个循环之中只要不断处理$1的值,即可获得整个参数列表。
while [ $# -ne 0 ]
do
echo $1
shift
done
特别地,使用shift `expr $# -2`可以获得最后一个参数。
使用getopts命令获得参数
getopts函数将输入脚本的,带有-的参数看作是选项,并进行处理:
while getopts aghv OPTION
do
case $OPTION in
a) echo "a"
;;
g) echo "g"
;;
h) echo "h"
;;
v) echo "v"
;;
esac
done
使用形如 getopts :aghv: OPTION 的方式,可以强制要求给 -v 选项传递一个值。
如果不给它赋值,那么在case语句里面增加如下一项:
\?) echo "-v 必须赋值"
这样,如果调用者未给-v赋值,则会报错,提示信息就是上面的字符串。
传进来的值,使用变量$OPTARG来访问。
一个较完整的例子:
#! /bin/sh
# backups
QUIET=n
DEVICE=awa
LOGFILE=/tmp/logbackup
usage()
{
echo "Usage: `basename $0` -d [device] -l [logfile] -q"
exit 1
}
if [ $# -eq 0 ]
then
usage
fi
while getopts :ql:d: OPTION
do
case $OPTION in
q) QUIET=y
LOGFILE="/tmp/backup.log"
;;
d) DEVICE=$OPTARG
;;
l) LOGFILE=$OPTARG
;;
\?) usage
;;
esac
done
echo "Your choices are"
echo "QUIET= $QIUET $DEVICE $LOGFILE"
对于选项的字母,还有一些命名的约定俗成的方式,可以查询相关的内容。
二十一、创建屏幕输出
使用tput命令
首先,tput init 初始化一下
字符串输出:
数字输出:
cols 列数
it tab宽度
lines 屏幕行数
布尔输出:
chts 光标不可见
hs 具有状态行
二十二、创建屏幕输入
一句话,对于用户输入的各种验证
二十三、调试脚本
set -x 打开,在脚本的每行输出都显示命令及其参数
set +x 关闭
添加大量echo语句跟踪执行过程
二十四、Shell的嵌入命令
set命令,在脚本内部使用
set arg1 arg2
这样的指令。相当于从外部调用者传入了arg1和arg2两个参数。
type 检查一个命令是否存在,存在的话给出其位置。
wait 使用
wait pid
等待指定pid的进程结束,再执行下一条指令。单一个wait表示等待所有子进程运行完毕。