Shell编程基础 part 1/2
SHELL编程基础
shell脚本, 必须在开始的第一行输入:
#!/bin/bash>#! 会告诉系统执行该脚本的程序, 例如bash;
Note 最好使用"!/bin/bash"而不是"!/bin/sh", 如果使用tc shell改为tcsh,其他类似.
保存文件后, 想要执行脚本, 必须先使它可执行:
1chmod +x filename>输入 ./filename就可以执行
1 变量赋值和引用
变量无需事先声明, 变量的命名规则:
1) 首字符必须为字母(a-z, A-Z)或者下划线_; 2) 中间不能有空格, 可以有下划线; 3) 不能使用其他标点符号;
赋值表达式: 变量名=值; [没空格]
取得变量的值, 需要在变量前加'$'
#!/bin/bash# 对变量赋值:a="hello world" #等号两边均不能有空格存在# 打印变量a的值:echo "A is:" $a>取得变量的值, 前面加 $;
Note 注意空格; 给变量赋值, 等号两边不能有空格;
区分文字和变量:
12num=2echo "this is the ${num}nd">输出: 'this is the 2nd'num
>不加花括号的话, shell会搜索 '&numnd' 变量, 发现没有值; 输出: 'this is the '
注意花括号的位置:
num=2echo "this is the {$num}nd">输出: 'this is the {2}nd'
Note shell的默认赋值是字符串赋值;
var=1var=$var+1echo $var>输出不是2而是 1+1;
为了达到数字效果:
123456let "var+=1"var="$[$var+1]"((var++))var=$(($var+1))var="$(expr "$var" + 1)" #不建议使用var="`expr "$var" + 1`" #强烈不建议使用,注意加号两边的空格,否则还是按照字符串的方式赋值,`为Esc下方的`,而不是单引号'。Note 前2种方式在bash下有效, 在sh下会出错;
>let表示数学运算, expr用于整数值运算, 每一项用空格分开, $[]将中括号内的表达式作为数学运算先计算结果再输出;
shell脚本有许多便利是系统自动设定的, 除了只在脚本内有效的普通shell便另外, 还有环境变量, 即由export关键字处理过的变量, 一般只在登录脚本中用到;
2 Shell里的流程控制
if语句
if 表达式为真, 则执行 then 后的部分
if ....; then ....elif ....; then ....else ....fi多数情况下, 可以使用测试命令来对条件进行测试, 比如可以比较字符串, 判断文件是否存在以及是否可读等等...通常用" [ ] "来表示条件测试;
Note 要注意中括号前后的空格, 确保存在;
[ -f "somefile" ] :判断是否是一个文件
[ -x "/bin/ls" ] :判断/bin/ls是否存在并有可执行权限
[ -n "$var" ] :判断$var变量是否有值
[ "$a" = "$b" ] :判断$a和$b是否相等
Note 执行 man test 可以查看所有测试表的式可以比较和判断的类型;
e.g.
if [ ${SHELL} = "/bin/bash" ]; then echo "your login shell is the bash (bourne again shell)"else echo "your login shell is not bash but ${SHELL}"fi>变量$SHELL包含有登录shell的名称, 和/bin/hash比较来判断当前shell是否是bash;
&&和||操作符
类似C语言:
[ -f "/etc/shadow" ] && echo "This computer uses shadow passwords"&&是一个快捷操作符, 如果左边的表达式为真则执行右边的语句, 可以看作逻辑运算中的与操作;
>如果/etc/shadow文件存在, 则打印文字;
或操作 || 类似:
1234mailfolder=/var/spool/mail/james[ -r "$mailfolder" ] || { echo "Can not read $mailfolder" ; exit 1; }echo "$mailfolder has mail from:"grep "^From " $mailfolder>先判断mailfolder是否可读, 可读则打印文件中的 From 行; [可读为true则不执行后面的条件]; 不可读则或操作生效, 打印错误信息, 脚本退出;
使用花括号以匿名函数的形式将两个命令放到一起作为一个命令, 以 ; 分割; 使用与, 或操作符会使脚本更便利;
case语句
case表达式可以用来匹配一个给定字符串, 而不是数字; (和C语言的switch...case不同)
1234case ... in ...) do something here ;;esacfile命令 可以辨别一个给定文件的文件类型; e.g. file lf.gz --> lf.gz: gzip compressed data, ...
利用file命令写smartzip脚本, 自动解压bzip2, gzip, zip不同类项的压缩文件:
12345678910ftype="$(file "$1")" case "$ftype" in "$1: Zip archive"*) unzip "$1" ;; "$1: gzip compressed"*) gunzip "$1" ;; "$1: bzip2 compressed"*) bunzip2 "$1" ;; *) echo "File $1 can not be uncompressed with smartzip";; esacNote 特殊变量$1, 包含传递给脚本的第一个参数值; e.g. 运行 smartzip sample.zip 时, $1就是字符串sample.zip;
select语句
select表达式是bash的扩展应用, 用于交互式场合;
select var in ... ; do break;done.... now $var can be used ....e.g.
echo "What is your favourite OS?"select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do break;doneecho "You have selected $var">自动按照1)...2)...的方式输出选项;
while/for 循环
只要测试条件为真, while循环将一直运行; 关键字break跳出循环, continue跳过一个循环;
123while ...; do ....donefor循环会查看字符串列表(字符串用空格分隔), 将其赋给一个变量:
123for var in ....; do ....donee.g.
123for var in A B C ; do echo "var is $var"donee.g. 脚本打印RPM包统计信息: showrpm
# list a content summary of a number of RPM packages# USAGE: showrpm rpmfile1 rpmfile2 ...# EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpmfor rpmpackage in "$@"; do if [ -r "$rpmpackage" ];then echo "=============== $rpmpackage ==============" rpm -qi -p $rpmpackage else echo "ERROR: cannot read file $rpmpackage" fidoneNote 特殊变量$@, 包含输入的所有命令行参数值; e.g. showrpm ssh.rpm web.rpm, 那么"$@"(必须有引号)就会出现2个字符串: ssh.rpm, web.rpm;
$* 有类似功能, 但是只有一个字符串, 如果不加引号, 带空格的参数会被截断;
3 Shell里的特殊符号
引号
在向程序传递任何参数之前, 程序会扩展通配符和变量; 这里的扩展是指程序会把通配符(比如*)替换成适当的文件名, 变量替换成变量值; 使用引号可以防止这种扩展;
e.g. 假设目录下有两个jpg, mail.jpg和tux.jpg;
1echo *.jpg>输出两个文件名;
单引号和双引号可以防止通配符的扩展:
echo "*.jpg"echo '*.jpg'>输出都是 *.jpg ;
单引号更严格, 可以防止任何变量扩展; 双引号则是防止通配符扩展但允许变量扩展:
echo $SHELLecho "$SHELL"echo '$SHELL'>输出 /bin/hash 和 /bin/bash 和 $SHELL;
还有一种防止扩展的方法, 使用转义字符--反斜杠 \ ;
echo \*.jpgecho \$SHELL>输出 *.jpg 和 $SHELL ;
4 Here Document
需要将几行文字传递给一个命令时, here document是不错的选择; 对每个脚本写一段帮助性的文字, 如果使用here document就不必用echo函数一行行输出; here document用 << 开头, 后面接一个字符串, 字符串必须出现在here document的末尾;
e.g. 对多个文件重命名, 使用here document打印帮助:
123456789101112131415161718192021222324252627282930# we have less than 3 arguments. Print the help text:if [ $# -lt 3 ] ; thencat << HELP ren -- renames a number of files using sed regular expressions USAGE: ren 'regexp' 'replacement' files... EXAMPLE: rename all *.HTM files in *.html: ren 'HTM$' 'html' *.HTM HELP #这里HELP要顶格写,前面不能有空格或者TAB制表符。如果cat一行写成cat << -HELP,前边可以带TAB. exit 0fiOLD="$1"NEW="$2"# The shift command removes one argument from the list of# command line arguments.shiftshift# $@ contains now all the files:for file in "$@"; do if [ -f "$file" ] ; then newfile=`echo "$file" | sed "s/${OLD}/${NEW}/g"` if [ -f "$newfile" ]; then echo "ERROR: $newfile exists already" else echo "renaming $file to $newfile ..." mv "$file" "$newfile" fi fidone>第一个if表达式判断输入命令行参数是否小于3个; 如果小于, 则将帮助文字传递给cat命令, 打印到屏幕上, 然后退出;
Note 特殊变量 $# 表示包含参数的个数; $2, $3 ...代表第二, 第三个参数;
>如果输入参数>=3个, 将第一个参数赋值给变量OLD, 第二个赋值给变量NEW; shift命令将第一个和第二个参数从参数列表删除, 原来的第三个参数变成参数列表$*的第一个参数;
>进入循环, 命令行参数列表被赋给$file, 判断文件是否存在, 存在则通过sed命令搜索和替换来产生新文件名; 然后将反斜线内命令结果赋值给newfile; 最后得到了旧文件名和新文件名, mv命令进行重命名;
[HELP不是关键字, 可以被其他字符替换(EOF), 加上单引号/双引号防止扩展]
refer to http://www.serverwatch.com/columns/article.php/3860446/Shell-Scripts-and-Here-Documents.htm
12345cat <<- 'EOF' test word text will be printed with out tabsEOF