veket官方网站论坛

veket

 找回密码
 要注册
搜索
查看: 2288|回复: 1

Bourne shell编程入门及脚本测试[转帖]

[复制链接]
发表于 2010-4-21 22:27 | 显示全部楼层 |阅读模式
版权声明:
本文内容为大连理工大学LINUX选修课讲义,欢迎大家转载,但禁止使用本材料进行
任何商业性或赢利性活动。转载时请保留本版权声明。

作者:何斌武,hbwork@dlut.edu.cn,大连理工大学网络中心,April 1999.

URL: ftp://ftp.dlut.edu.cn/pub/PEOPLE/albin/
  1. ------------------------------------------------------------------------------
  2. Bourne Shell
  3.   
  4. 介绍:Bourne Shell 基础及其他很多有用的特性,shell编程及组织。
  5.   
  6. 主要内容:
  7. .shell基础      基本介绍,环境,选项,特殊字符
  8. .shell变量      用户定义变量,环境变量,位置变量(shell 参数)
  9. .shell script编程
  10.         条件测试,循环及重复控制
  11. .shell定制
  12.   
  13. 1.shell基础知识
  14.   作者:Stephen Bourne 在Bell实验室开发
  15.   建议:man sh  查看相关UNIX上的改进或特性
  16.   
  17. (1)shell提示符及其环境
  18.    /etc/passwd文件
  19.    提示符:$
  20.    /etc/profile $HOME/.profile
  21. (2)shell执行选项
  22.    -n   测试shell script语法结构,只读取shell script但不执行
  23.    -x   进入跟踪方式,显示所执行的每一条命令,用于调度
  24.    -a   Tag all variables for export
  25.    -c "string"  从strings中读取命令
  26.    -e   非交互方式
  27.    -f   关闭shell文件名产生功能
  28.    -h   locate and remember functions as defind
  29.    -i   交互方式
  30.    -k   从环境变量中读取命令的参数
  31.    -r   限制方式
  32.    -s   从标准输入读取命令
  33.    -t   执行命令后退出(shell exits)
  34.    -u   在替换中如使用未定义变量为错误
  35.    -v   verbose,显示shell输入行
  36.   
  37.    这些选项可以联合使用,但有些显然相互冲突,如-e和-i.
  38.   
  39. (3)受限制shell(Restircted Shell)
  40.     sh -r 或 /bin/rsh
  41.   
  42.     不能执行如下操作:cd, 更改PATH,指定全路径名,输出重定向,因此可以提供一个较
  43.     好的控制和安全机制。通常rsh用于应用型用户及拨号用户,这些用户通常是看不到提
  44.     示符的。通常受限制用户的主目录是不可写的。
  45.   
  46.     不足:如果用户可以调用sh,则rsh的限制将不在起作用,事实上如果用户在vi及more
  47.         程序中调用shell,而这时rsh的限制将不再起作用。
  48.   
  49. (4)用set改变 shell选项
  50.    用户可以在$提示符下用set命令来设置或取消shell的选项。使用-设置选项,+取消相应
  51.    选项,大多数UNIX系统允许a,e,f,h,k,n,u,v和x的开关设置/取消。
  52.   
  53.    set -xv
  54.         启动跟踪方式;显示所有的命令及替换,同样显示输入。
  55.    set -tu
  56.         关闭在替换时对未定义变量的检查。
  57.   
  58.    使用echo $-显示所有已设置的shell选项。
  59.   
  60.   
  61. (5)用户启动文件 .profile
  62.         PATH=$PATH:/usr/loacl/bin; export PATH
  63.   
  64. (6)shell环境变量
  65.         CDPATH  用于cd命令的查找路径
  66.         HOME    /etc/passwd文件中列出的用户主目录
  67.         IFS     Internal Field Separator,默认为空格,tab及换行符
  68.         MAIL    /var/mail/$USERNAME     mail等程序使用
  69.         PATH
  70.         PS1,PS2        默认提示符($)及换行提示符(>)
  71.         TERM    终端类型,常用的有vt100,ansi,vt200,xterm等
  72.   
  73.         示例:$PS1="test:";export PS1
  74.               test: PS1="\$";export PS1
  75.               $echo $MAIL
  76.               /var/mail/username
  77. (7)保留字符及其含义
  78. $      shell变量名的开始,如$var
  79.    |    管道,将标准输出转到下一个命令的标准输入
  80.    #    注释开始
  81.    &    在后台执行一个进程
  82.    ?   匹配一个字符
  83.    *    匹配0到多个字符(与DOS不同,可在文件名中间使用,并且含.)
  84.    $-   使用set及执行时传递给shell的标志位
  85.    $!   最后一个子进程的进程号
  86.    $#   传递给shell script的参数个数
  87.    $*   传递给shell script的参数
  88.    $@   所有参数,个别的用双引号括起来
  89.    $?   上一个命令的返回代码
  90.    $0   当前shell的名字
  91.    $n    (n:1-) 位置参数
  92.    $   进程标识号(Process Identifier Number, PID)
  93.    >file        输出重定向
  94.    <file        输入重定向
  95.    `command`    命令替换,如    filename=`basename /usr/local/bin/tcsh`
  96.    >>fiile      输出重定向,append
  97.   
  98.    转义符及单引号:
  99.         $echo "$HOME $PATH"
  100.         /home/hbwork /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:
  101.         $echo '$HOME $PATH'
  102.         $HOME $PATH
  103.         $echo \$HOME $PATH
  104.         $HOME /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/hbw
  105. ork/bin
  106.   
  107.    其他:
  108.         $dir=ls
  109.         $dir
  110.         $alias dir ls
  111.         $dir
  112.   
  113.         ls > filelist
  114.         ls >> filelist
  115.         wc -l < filelist
  116.         wc -l filelist
  117.         sleep 5; echo 5 seconds reaches; ls -l
  118.         ps ax |egrep inetd
  119.         find / -name core -exec rm {} \; &
  120.         filename=`date "+%Y%m%d"`.log
  121.   
  122. 2. shell变量
  123.   变量:代表某些值的符号,如$HOME,cd命令查找$HOME,在计算机语言中可以使用变量可以
  124.         进行多种运算和控制。
  125.   
  126.   Bourne Shell有如下四种变量:
  127.         .用户自定义变量
  128.         .位置变量即 shell script之参数
  129.         .预定义变量(特殊变量)
  130.         .环境变量(参考shell定制部分)
  131. (1)用户自定义变量(数据的存储)
  132.         $ COUNT=1
  133.         $ NAME="He Binwu"
  134.   
  135.      技巧:因为大部分UNIX命令使用小写字符,因此在shell编程中通常使用全大写变量,
  136.         当然这并不是强制性的,但使用大写字符可以在编程中方便地识别变量。
  137.   
  138.      变量的调用:在变量前加$
  139.         $ echo $HOME
  140.         /home/hbwork
  141.         $ WEEK=Satur
  142.         $ echo Today is $WEEKday
  143.         Today is
  144.         $echo Today is ${WEEK}day
  145.         Today is Saturday
  146.   
  147.      Shell变量赋值从右从左进行(Linux Shell/bash从左向右赋值!)
  148.      $ X=$Y Y=y
  149.      $ echo $X
  150.      y
  151.      $ Z=z Y=$Z
  152.      $ echo $Y
  153.   
  154.      $
  155.   
  156.      使用unset命令删除变量的赋值
  157.      $ Z=hello
  158.      $ echo $Z
  159.      hello
  160.      $ unset Z
  161.      $ echo $Z
  162.   
  163.      $
  164.   
  165.      有条件的命令替换
  166.         在Bourne Shell中可以使变量替换在特定条件下执行,即有条件的环境变量替换。
  167.         这种变量替换总是用大括号括起来的。
  168.   
  169.         .设置变量的默认值
  170.             在变量未赋值之前其值为空。Bourne Shell允许对变量设置默认值,其格式如
  171. 下:
  172.             ${variable:-defaultvalue}
  173.             例:
  174.                 $ echo Hello $UNAME
  175.                 Hello
  176.                 $ echo Hello ${UNAME:-there}
  177.                 Hello there
  178.                 $ echo $UNAME   #变量值并未发生变化
  179.   
  180.                 $ UNAME=hbwork
  181.                 $ echo Hello ${UNAME:-there}
  182.                 Hello hbwork
  183.                 $
  184.         .另一种情况:改变变量的值,格式如下:
  185.             ${variable:=value}
  186.   
  187.             例:
  188.                 $ echo Hello $UNAME
  189.                 Hello
  190.                 $ echo Hello ${UNAME:=there}
  191.                 Hello there
  192.                 $ echo $UNAME   #变量值并未发生变化
  193.                 there
  194.                 $
  195.         .变量替换中使用命令替换
  196.                 $USERDIR=${$MYDIR:-`pwd`}
  197.   
  198.         .在变量已赋值时进行替换  ${variable:+value}
  199.         .带有错误检查的有条件变量替换
  200.           ${variable:?value}
  201.           例:
  202.           $ UNAME=
  203.           $ echo ${UNAME:?"UNAME has not been set"}
  204.           UNAME: UNAME has not been set
  205.           $ echo ${UNAME:?}
  206.           UNAME: parameter null or not set
  207.   
  208.   (2)位置变量(Shell参数)
  209.   在shell script中位置参数可用$1..$9表示,$0表示内容通常为当前执行程序的文件名。
  210.   .防止变量值被替换 readonly variable
  211.   .使用export命令输出变量,使得变量对子shell可用,当shell执行一下程序时,shell
  212.    将为其设置一个新的环境让其执行,这称之了subshell.  在Bourne Shell中变量通常
  213.    被认为是本地变量,也就是说在对其赋值之外的shell环境之外是不认识此变量的。使
  214.    用export对subshell可用。
  215.   
  216.    例:对变量PS1的export操作,shell的提示符将发生变化。
  217.    $ PS1=`hostname`$
  218.         peony$sh
  219.         $ echo $PS1
  220.         $  <-输出结果
  221.         $ exit
  222.         peony$export PS1
  223.         peony$sh
  224.         peony$ echo $PS1
  225.         peony$  <-输出结果
  226.         peony$
  227.   
  228.   
  229. 3.Shell Script编程
  230. 目的:使用UNIX所提供的最常用工具来完成所需复杂任务的强大功能。
  231.   
  232. (1)最简单的Shell 编程
  233.    $ls -R / |grep myname |more
  234.   
  235.    每天数据的备份:
  236.    $ cd /usr/yourname; ls * |cpio -o > /dev/rmt/0h
  237.   
  238.    书写程序的目的是一次编程,多次使用(执行)!
  239.   
  240.    $ cat > backup.sh
  241.    cd /home/hbwork
  242.    ls * | cpio -o > /dev/rmt/0h
  243.    ^D
  244.   
  245.    执行:
  246.    $ sh backup.sh
  247.   
  248.    或:
  249.    $ chmod +x backup.sh
  250.    $ ./backup.sh
  251.   
  252.    技巧:在shell script中加入必要的注释,以便以后阅读及维护。
  253.   
  254. (2)shell是一个(编程)语言
  255.   同传统的编程语言一样,shell提供了很多特性,这些特性可以使你的shell script
  256.   编程更为有用,如:数据变量、参数传递、判断、流程控制、数据输入和输出,子
  257.   程序及以中断处理等。
  258.   
  259.   . 在shell编程中使用数据变量可以将程序变量更为通用,如在上面backup.sh中:
  260.     cd $WORKDIR
  261.     ls * | cpio -o > /dev/rmt/0h
  262.   
  263.   . Shell编程中的注释以#开头
  264.   . 对shell变量进行数字运算,使用expr命令
  265.         expr integer operator integer
  266.         其中operator为+ - * / %, 但对*的使用要用转义符\,如:
  267.         $expr 4 \* 5
  268.         20
  269.         $int=`expr 5 + 7`
  270.         $echo $int
  271.         12
  272.   
  273. (3)Shell编程的参数传递, 可通过命令行参数以及交互式输入变量(read)
  274.   
  275.      restoreall.sh 对backup.sh程序的备份磁带进行恢复
  276.         $cat > restoreall.sh
  277.         cd $WORKDIR
  278.         cpio -i < /dev/rmt/0h
  279.         ^D
  280.      restore1.sh:只能恢复一个文件
  281.         #restore1 --program to restore a single file
  282.         cd $WORKDIR
  283.         cpio -i $i < /dev/rmt/0h
  284.   
  285.         $restore1 file1
  286.   
  287.         恢复多个文件restoreany :
  288.         #restoreany --program to restore a single file
  289.         cd $WORKDIR
  290.         cpio -i $* < /dev/rmt/0h
  291.   
  292.         $ restoreany file1 file2 file3
  293.   
  294.   
  295. (4)条件判断
  296.    . if-then语句,格式如下:
  297.         if command_1
  298.         then
  299.                 command_2
  300.                 command_3
  301.         fi
  302.         command_4
  303.   
  304.    在if-then语句中使用了命令返回码$?,即当command_1执行成功时才执行command_2和
  305.    command_3,而command_4总是执行.
  306.   
  307.    示例程序unload: 在备份成功时删除原始文件,带有错误检查
  308.   
  309.         cd $1
  310.         #备份时未考虑不成功的情况!
  311.         ls -a | cpio -o > /dev/rmt/0h
  312.         rm -rf *
  313.   
  314.         改进如下:
  315.   
  316.         #当使用了管道命令时,管理命令的返回代码为最后一个命令的返回代码
  317.         if ls -a | cpio -o > /dev/rmt/0h
  318.         then
  319.                 rm -rf *
  320.         fi
  321.   
  322.    . if-then-else语句
  323.         if command_1
  324.         then
  325.                 command_2
  326.         else
  327.                 command_3
  328.         fi
  329.   
  330.         技巧: 由于shell对命令中的多余的空格不作任何处理,一个好的程序员会用这一特

  331.               对自己的程序采用统一的缩进格式,以增强自己程序的可读性.
  332.   
  333.     . 使用test命令进行进行条件测试
  334.       格式: test conditions
  335.   
  336.       test在以下四种情况下使用: a. 字符比较 b.两个整数值的比较
  337.                 c. 文件操作,如文件是否存在及文件的状态等
  338.                 d. 逻辑操作,可以进行and/or,与其他条件联合使用
  339.   
  340.       a. 测试字符数据: shell变量通常民政部下均作为字符变量
  341.         str1 = str2     二者相长,相同
  342.         str1 != str2    不同
  343.         -n string       string不为空(长度不为零)
  344.         -z string       string为空
  345.         string          string不为空
  346.   
  347.         例:
  348.                 $ str1=abcd     #在含有空格时必须用引号括起来
  349.                 $ test $str1=abcd
  350.                 $ echo $?
  351.                 0
  352.                 $ str1="abcd "
  353.                 $ test $str1=abcd
  354.                 $ echo $?
  355.                 1
  356.         Note: 在test处理含有空格的变量时最好用引号将变量括起来,否则会出现错误的
  357. 结果,
  358.               因为shell在处理命令行时将会去掉多余的空格,而用引号括起来则可以防止
  359.               shell去掉这些空格.
  360.               例:
  361.                 $ str1="    "
  362.                 $ test $str1
  363.                 $ echo $?
  364.                 1
  365.                 $ test "$str1"
  366.                 $ echo $?
  367.                 0
  368.                 $ test -n $str1
  369.                 test: argument expected
  370.                 $ test -n "$str1"
  371.                 $ echo $?
  372.                 0
  373.                 $
  374.   
  375.       b. 整数测试: test与expr相同,可以将字符型变量转换为整数进行操作,expr进行
  376.          整数的算术运算,而test则进行逻辑运算.
  377.   
  378.          表达式                 说明
  379.          ---------------------------------------
  380.          int1 -eq int2          相等?
  381.          int1 -ne int2          不等?
  382.          int1 -gt int2          int1 > int2 ?
  383.          int1 -ge int2          int1 >= int2 ?
  384.          int1 -lt int2          int1 < int2 ?
  385.          int1 -le int2          int1 <= int2 ?
  386.   
  387.          例:
  388.                 $ int1=1234
  389.                 $ int2=01234
  390.                 $ test $int1 -eq $int2
  391.                 $ echo $?
  392.                 0
  393.   
  394.       c. 文件测试:检查文件状态如存在及读写权限等
  395.   
  396.          -r filename     用户对文件filename有读权限?
  397.          -w filename     用户对文件filename有写权限?
  398.          -x filename     用户对文件filename有可执行权限?
  399.          -f filename     文件filename为普通文件?
  400.          -d filename     文件filename为目录?
  401.          -c filename     文件filename为字符设备文件?
  402.          -b filename     文件filename为块设备文件?
  403.          -s filename     文件filename大小不为零?
  404.          -t fnumb        与文件描述符fnumb(默认值为1)相关的设备是一个终端设备?
  405.   
  406.       d. 测试条件之否定,使用!
  407.         例:
  408.                 $ cat /dev/null > empty
  409.                 $ test -r empty
  410.                 $ echo $?
  411.                 0
  412.                 $ test -s empty
  413.                 1
  414.                 $ test ! -s empty
  415.                 $ echo $?
  416.                 0
  417.       e. 测试条件之逻辑运算
  418.         -a      And
  419.         -o      Or
  420.   
  421.         例: $ test -r empty -a -s empty
  422.             $ echo $?
  423.             1
  424.       f. 进行test测试的标准方法
  425.          因为test命令在 shell编程中占有很重要的地位,为了使shell能同其他编程语言
  426. 一样
  427.          便于阅读和组织, Bourne Shell在使用test测试时使用了另一种方法:用方括号将
  428. 整个
  429.          test测试括起来:
  430.   
  431.          $ int1=4
  432.          $ [ $int1 -gt 2 ]
  433.          $ echo $?
  434.          0
  435.   
  436.          例: 重写unload程序,使用test测试
  437.          #!/bin/sh
  438.          #unload - program to backup and remove files
  439.          #syntax: unload directory
  440.   
  441.          #check arguments
  442.          if [ $# -ne 1 ]
  443.          then
  444.                 echo "usage: $0 directory"
  445.                 exit 1
  446.          fi
  447.   
  448.          #check for valid directory name
  449.          if [ ! -d "$1" ]
  450.          then
  451.                 echo "$1 is not a directory"
  452.                 exit 2
  453.          fi
  454.   
  455.          cd $1
  456.   
  457.          ls -a | cpio -o > /dev/rmt/0h
  458.   
  459.          if [ $? -eq 0 ]
  460.          then
  461.                 rm -rf *
  462.          else
  463.                 echo "A problem has occured in creating backup"
  464.                 echo "The directory will not be ereased"
  465.                 echo "Please check the backup device"
  466.                 exit 3
  467.          fi
  468.          # end of unload
  469.   
  470.          在如上示例中出现了exit, exit有两个作用:一是停止程序中其他命令的执行,二

  471.          设置程序的退出状态
  472.   
  473.       g. if嵌套及elif结构
  474.         if command
  475.         then
  476.                 command
  477.         else
  478.                 if command
  479.                 then
  480.                         command
  481.                 else
  482.                         if command
  483.                         then
  484.                                 command
  485.                         fi

  486.                 fi
  487.         fi
  488.   
  489.         改进:使用elif结构
  490.         if command
  491.         then
  492.                 command
  493.         elif    command
  494.         then
  495.                 command
  496.         elif    command
  497.         then
  498.                 command
  499.         fi
  500.   
  501.         elif结构同if结构类似,但结构更清淅,其执行结果完全相同.
复制代码
 楼主| 发表于 2010-4-21 22:28 | 显示全部楼层
Bourne Shell及Shell编程(2)
  1. Bourne Shell及Shell编程(2)
  2.       h.交互式从键盘读入数据
  3.         使用read语句,其格式如下:
  4.   
  5.         read var1 var2 ... varn
  6.   
  7.         read将不作变量替换,但会删除多余的空格,直到遇到第一个换行符(回车),
  8.         并将输入值依次赋值给相应的变量。
  9.   
  10.         例:
  11.         $ read var1 var2 var3
  12.         Hello   my  friends
  13.         $ echo $var1 $var2 $var3
  14.         Hello my friends
  15.         $ echo $var1
  16.         Hello
  17.         $ read var1 var2 var3
  18.         Hello my dear friends
  19.         $ echo $var3
  20.         dear friends   <-输入多余变量时,输入值余下的内容赋给最后一个变量
  21.         $ read var1 var2 var3
  22.         Hello friends
  23.         $ echo $var3
  24.                         <- var3为空
  25.         $
  26.   
  27.         在shell script中可使用read语句进行交互操作:
  28.   
  29.         ...
  30.         #echo -n message 输出结果后不换行
  31.         echo -n "Do you want to continue: Y or N"
  32.         read ANSWER
  33.   
  34.         if [ $ANSWER=N -o $ANSWER=n ]
  35.         then
  36.                 exit
  37.         fi
  38.   
  39.       i. case结构:结构较elif-then结构更清楚
  40.   
  41.         比较if-then语句:
  42.   
  43.         if [ variable1 = value1 ]
  44.         then
  45.             command
  46.             command
  47.         elif [ variable1 = value2 ]
  48.         then
  49.             command
  50.             command
  51.         elif [ variable1 = value3 ]
  52.         then
  53.             command
  54.             command
  55.         fi
  56.   
  57.         相应的case结构:
  58.   
  59.         case value in
  60.             pattern1)
  61.                command
  62.                command;;
  63.             pattern2)
  64.                command
  65.                command;;
  66.             ...
  67.             patternn)
  68.                command;
  69.          esac
  70.   
  71.        * case语句只执行第一个匹配模式
  72.   
  73.        例:使用case语句建立一个菜单选择shell script
  74.   
  75.        #Display a menu
  76.        echo _
  77.        echo "1 Restore"
  78.        echo "2 Backup"
  79.        echo "3 Unload"
  80.        echo
  81.   
  82.        #Read and excute the user's selection
  83.        echo -n "Enter Choice:"
  84.        read CHOICE
  85.   
  86.        case "$CHOICE" in
  87.         1)      echo "Restore";;
  88.         2)      echo "Backup";;
  89.         3)      echo "Unload";;
  90.         *)      echo "Sorry $CHOICE is not a valid choice
  91.                 exit 1
  92.         esac
  93.   
  94.         在上例中,*指默认匹配动作。此外,case模式中也可以使用逻辑操作,如下所示

  95.   
  96.         pattern1 | pattern2 )   command
  97.                                 command ;;
  98.   
  99.         这样可以将上面示例程序中允许用户输入数字或每一个大写字母。
  100.   
  101.         case "$CHOICE" in
  102.                 1|R)    echo "Restore";;
  103.                 2|B)    echo "Backup";;
  104.                 3|U)    echo "Unload";;
  105.                 *)      echo "Sorry $CHOICE is not a valid choice
  106.                         exit 1
  107.         esac
  108.   
  109. (5)循环控制
  110.     <1> while循环:
  111.         格式:
  112.                 while   command
  113.                 do
  114.                         command
  115.                         command
  116.                         command
  117.                         ...
  118.                 done
  119.   
  120.         例: 计算1到5的平方
  121.         #!/bin/sh
  122.         #
  123.         #Filename: square.sh
  124.         int=1
  125.   
  126.         while [ $int -le 5 ]
  127.         do
  128.                 sq=`expr $int \* $int`
  129.                 echo $sq
  130.                 int=`expr $int + 1`
  131.         done
  132.         echo "Job completed"
  133.   
  134.         $ sh square.sh
  135.         1
  136.         4
  137.         9
  138.         16
  139.         25
  140.         Job completed
  141.   
  142.       <2> until循环结构:
  143.         格式:
  144.                 until command
  145.                 do
  146.                         command
  147.                         command
  148.                         ....
  149.                         command
  150.                 done
  151.   
  152.         示例:使用until结构计算1-5的平方
  153.         #!/bin/sh
  154.   
  155.         int=1
  156.   
  157.         until [ $int -gt 5 ]
  158.         do
  159.                 sq=`expr $int \* $int`
  160.                 echo $sq
  161.                 int=`expr $int + 1`
  162.         done
  163.         echo "Job completed"
  164.   
  165.      <3> 使用shift对不定长的参数进行处理
  166.         在以上的示例中我们总是假设命令行参数唯一或其个数固定,或者使用$*将整个命

  167.         行参数传递给shell script进行处理。对于参数个数不固定并且希望对每个命令参

  168.         进行单独处理时则需要shift命令。使用shift可以将命令行位置参数依次移动位置

  169.         即$2->$1, $3->$2. 在移位之前的第一个位置参数$1在移位后将不在存在。
  170.   
  171.         示例如下:
  172.   
  173.         #!/bin/sh
  174.         #
  175.         #Filename: shifter
  176.   
  177.          until [ $# -eq 0 ]
  178.          do
  179.             echo "Argument is $1 and `expr $# - 1` argument(s) remain"
  180.             shift
  181.          done
  182.   
  183.   
  184.          $ shifter 1 2 3 4
  185.          Argument is 1 and 3 argument(s) remain
  186.          Argument is 2 and 2 argument(s) remain
  187.          Argument is 3 and 1 argument(s) remain
  188.          Argument is 4 and 0 argument(s) remain
  189.          $
  190.   
  191.          使用shift时,每进行一次移位,$#减1,使用这一特性可以用until循环对每个参
  192.          数进行处理,如下示例中是一个求整数和的shell script:
  193.   
  194.         #!/bin/sh
  195.         # sumints - a program to sum a series of integers
  196.         #
  197.   
  198.          if [ $# -eq 0 ]
  199.          then
  200.             echo "Usage: sumints integer list"
  201.             exit 1
  202.          fi
  203.   
  204.          sum=0
  205.   
  206.          until [ $# -eq 0 ]
  207.          do
  208.             sum=`expr $sum + $1`
  209.             shift
  210.          done
  211.          echo $sum
  212.   
  213.   
  214.         $ sh sumints 324 34 34 12 34
  215.         438
  216.         $
  217.   
  218.         使用shift的另一个原因是Bourne Shell的位置参数变量为$1~$9, 因此通过位置变

  219.         只能访问前9个参数。但这并不等于在命令行上最多只能输入9个参数。此时如果想
  220. 访问
  221.         前9个参数之后的参数,就必须使用shift.
  222.   
  223.         另外shift后可加整数进行一次多个移位,如:
  224.   
  225.                 shift 3
  226.   
  227.   
  228.     <4>. for循环
  229.        格式:
  230.                 for var in arg1 arg2 ... argn
  231.                 do
  232.                         command
  233.                         ....
  234.                         command
  235.                 done
  236.   
  237.         示例:
  238.         $ for letter in a b c d e; do echo $letter;done
  239.         a
  240.         b
  241.         c
  242.         d
  243.         e
  244.   
  245.         对当前目录下的所有文件操作:
  246.         $ for i in *
  247.           do
  248.                 if [ -f $i ]
  249.                 then
  250.                         echo "$i is a file"
  251.                 elif    [ -d $i ]
  252.                         echo "$i is a directory"
  253.                 fi
  254.           done
  255.   
  256.         求命令行上所有整数之和:
  257.         #!/bin/sh
  258.   
  259.         sum=0
  260.   
  261.         for INT in $*
  262.         do
  263.                 sum=`expr $sum + $INT`
  264.         done
  265.   
  266.         echo $sum
  267.   
  268.   
  269.       <6> 从循环中退出: break和continue命令
  270.         break           立即退出循环
  271.         continue        忽略本循环中的其他命令,继续下一下循环
  272.   
  273.         在shell编程中有时我们要用到进行无限循环的技巧,也就是说这种循环一直执行

  274.         到break或continue命令。这种无限循环通常是使用true或false命令开始的。UNIX
  275.         系统中的true总是返加0值,而false则返回非零值。如下所示:
  276.   
  277.         #一直执行到程序执行了break或用户强行中断时才结束循环
  278.         while true
  279.         do
  280.                 command
  281.                 ....
  282.                 command
  283.         done
  284.   
  285.         上面所示的循环也可以使用until false, 如下:
  286.   
  287.         until false
  288.         do
  289.                 command
  290.                 ....
  291.                 command
  292.         done
  293.   
  294.         在如下shell script中同时使用了continue,break以及case语句中的正规表达式用
  295. 法:
  296.   
  297.          #!/bin/sh
  298.          # Interactive program to restore, backup, or unload
  299.          # a directory
  300.   
  301.          echo "Welcome to the menu driven Archive program"
  302.   
  303.          while true
  304.          do
  305.          # Display a Menu
  306.             echo
  307.             echo "Make a Choice from the Menu below"
  308.             echo _
  309.             echo "1  Restore Archive"
  310.             echo "2  Backup directory"
  311.             echo "3  Unload directory"
  312.             echo "4  Quit"
  313.             echo
  314.   
  315.          # Read the user's selection
  316.             echo -n "Enter Choice: "
  317.   
  318.             read CHOICE
  319.   
  320.             case $CHOICE in
  321.                [1-3] ) echo
  322.                        # Read and validate the name of the directory
  323.   
  324.                        echo -n "What directory do you want? "
  325.                        read WORKDIR
  326.   
  327.                        if [ ! -d "$WORKDIR" ]
  328.                        then
  329.                           echo "Sorry, $WORKDIR is not a directory"
  330.                           continue
  331.                        fi
  332.   
  333.                        # Make the directory the current working directory
  334.                        cd $WORKDIR;;
  335.   
  336.                     4) :;;    # :为空语句,不执行任何动作
  337.                     *) echo "Sorry, $CHOICE is not a valid choice"
  338.                        continue
  339.             esac
  340.   
  341.             case "$CHOICE" in
  342.                1) echo "Restoring..."
  343.                   cpio -i </dev/rmt/0h;;
  344.   
  345.                2) echo "Archiving..."
  346.                   ls | cpio -o >/dev/rmt/0h;;
  347.   
  348.                3) echo "Unloading..."
  349.                   ls | cpio -o >/dev/rmt/0h;;
  350.   
  351.                4) echo "Quitting"
  352.                   break;;
  353.             esac
  354.   
  355.          #Check for cpio errors
  356.   
  357.             if [ $? -ne 0 ]
  358.             then
  359.                echo "A problem has occurred during the process"
  360.                if [ $CHOICE = 3 ]
  361.                then
  362.                   echo "The directory will not be erased"
  363.                fi
  364.   
  365.                echo "Please check the device and try again"
  366.                continue
  367.             else
  368.                if [ $CHOICE = 3 ]
  369.                then
  370.                   rm *
  371.                fi
  372.             fi
  373.          done
  374.   
  375.   
  376. (6)结构化编程:定义函数
  377.    同其他高级语言一样,shell也提供了函数功能。函数通常也称之为子过程(subroutine)
  378. ,
  379.    其定义格式如下:
  380.   
  381.    funcname()
  382.    {
  383.         command
  384.         ...
  385.         command;  #分号
  386.    }
  387.   
  388.    定义函数之后,可以在shell中对此函数进行调用,使用函数定义可以将一个复杂的程序

  389.    为多个可管理的程序段,如下所示:
  390.   
  391.         # start program
  392.   
  393.          setup ()
  394.          {  command list ; }
  395.   
  396.          do_data ()
  397.          {  command list ; }
  398.   
  399.          cleanup ()
  400.          {  command list ; }
  401.   
  402.          errors ()
  403.          {  command list ; }
  404.   
  405.          setup
  406.          do_data
  407.          cleanup
  408.          # end program
  409.   
  410.      技巧:
  411.         . 在对函数命名时最好能使用有含义的名字,即函数名能够比较准确的描述函数所
  412. 完成
  413.           的任务。
  414.         . 为了程序的维护方便,请尽可能使用注释
  415.   
  416.   
  417.      使用函数的另一个好处就是可以在一个程序中的不同地方执行相同的命令序列(函数),
  418.      如下所示:
  419.   
  420.      iscontinue()
  421.      {
  422.         while true
  423.         do
  424.                 echo -n "Continue?(Y/N)"
  425.                 read ANSWER
  426.   
  427.                 case $ANSWER in
  428.                         [Yy])   return 0;;
  429.                         [Nn])   return 1;;
  430.                         *) echo "Answer Y or N";;
  431.                 esac
  432.         done
  433.      }
  434.   
  435.      这样可以在shell编程中调用iscontinue确定是否继续执行:
  436.   
  437.      if iscontinue
  438.      then
  439.         continue
  440.      else
  441.         break
  442.      fi
  443.   
  444.   
  445.   ** shell函数与shell程序非常相似,但二者有一个非常重要的差别:shell程序是由子sh
  446. ell
  447.      执行的,而shell函数则是作为当前shell的一部分被执行的,因此在当前shell中可以

  448.      变函数的定义。此外在任意shell(包括交互式的shell)中均可定义函数,如:
  449.   
  450.      $ dir
  451.      dir: not found
  452.      $ dir () { ls -l ;}
  453.      $ dir
  454.      total 5875
  455.      -rw-r--r--   1 hbwork        100 Nov 10 23:16 doc
  456.      -rw-r--r--   1 hbwork    2973806 Nov 10 23:47 ns40docs.zip
  457.      -rw-r--r--   1 hbwork    1715011 Nov 10 23:30 ns840usr.pdf
  458.      -rw-r--r--   1 hbwork    1273409 Sep 23  1998 radsol21b6.tar.Z
  459.      -rw-r--r--   1 hbwork       7526 Nov 10 23:47 wget-log
  460.      -rw-r--r--   1 hbwork       1748 Nov 13 21:51 wget-log.1
  461.      $ unset dir
  462.      $ dir () {
  463.      > echo "Permission  Link Owner Group  File_SZ   LastAccess FileName"
  464.      > echo "-----------------------------------------------------------"
  465.      > ls -l $*;
  466.      > }
  467.   
  468.         $ dir
  469.         Permission  Link Owner Group  File_SZ   LastAccess FileName
  470.         -----------------------------------------------------------
  471.         total 5875
  472.         -rw-r--r--   1 hbwork        100 Nov 10 23:16 doc
  473.         -rw-r--r--   1 hbwork    2973806 Nov 10 23:47 ns40docs.zip
  474.         -rw-r--r--   1 hbwork    1715011 Nov 10 23:30 ns840usr.pdf
  475.         -rw-r--r--   1 hbwork    1273409 Sep 23  1998 radsol21b6.tar.Z
  476.         -rw-r--r--   1 hbwork       7526 Nov 10 23:47 wget-log
  477.         -rw-r--r--   1 hbwork       1748 Nov 13 21:51 wget-log.1
  478.   
  479.      通常情况下,shell script是在子shell中执行的,困此在此子shell中对变量所作的
  480.      修改对父shell不起作用。点(.) 命令使用shell在不创建子shell而由当前shell读取
  481.      并执行一个shell script, 可以通过这种方式来定义函数及变量。此外点(.)命令最
  482.      常用的功能就是通过读取.profile来重新配置初始化login变量。如下所示:
  483.   
  484.      $ . .profile
  485.      (csh相对于.命令的是source命令).
  486.   
  487. (7)使用And/Or结构进行有条件的命令执行
  488. <1> And , 仅当第一个命令成功时才有执行后一个命令,如同在逻辑与表达式中如果前面的
  489.      结果为真时才有必要继续运算,否则结果肯定为假。
  490.   
  491.      格式如下:
  492.   
  493.      command1 && command2
  494.   
  495.      例:rm $TEMPDIR/* && echo "File successfully removed"
  496.   
  497.          等价于
  498.   
  499.          if rm $TEMPDIR/*
  500.          then
  501.                 echo "File successfully removed"
  502.          fi
  503.   
  504. <2>Or, 与AND相反,仅当前一个命令执行出错时才执行后一条命令
  505.   
  506.     例: rm $TEMPDIR/* || echo "File not removed"
  507.   
  508.          等价与:
  509.   
  510.          if rm $TEMPDIR/*
  511.          then
  512.                 command
  513.          else
  514.                 echo "File not removed"
  515.          fi
  516.   
  517. <3> 混合命令条件执行
  518.      command1 && command2 && command3
  519.         当command1, command2成功时才执行command3
  520.   
  521.      command1 && command2 || comamnd3
  522.          仅当command1成功,command2失败时才执行command3
  523.   
  524.      当然可以根据自己的需要进行多种条件命令的组合,在此不多讲述。
  525.   
  526.   
  527. (8) 使用getopts命令读取unix格式选项
  528.     UNIX格式选项指如下格式的命令行参数:
  529.     command -options parameters
  530.   
  531.     使用格式:
  532.     getopts option_string variable
  533.   
  534.     具体使用方法请参考getopts的在线文档(man getopts).
  535.   
  536.     示例如下:
  537.   
  538.          #newdate
  539.          if [ $# -lt 1 ]
  540.          then
  541.              date
  542.          else
  543.             while getopts mdyDHMSTjJwahr OPTION
  544.             do
  545.                case $OPTION
  546.                in
  547.                   m) date '+%m ';;  # Month of Year
  548.                   d) date '+%d ';;  # Day of Month
  549.                   y) date '+%y ';;  # Year
  550.                   D) date '+%D ';;  # MM/DD/YY
  551.                   H) date '+%H ';;  # Hour
  552.                   M) date '+%M ';;  # Minute
  553.                   S) date '+%S ';;  # Second
  554.                   T) date '+%T ';;  # HH:MM:SS
  555.                   j) date '+%j ';;  # day of year
  556.                   J) date '+%y%j ';;# 5 digit Julian date
  557.                   w) date '+%w ';;  # Day of the Week
  558.                   a) date '+%a ';;  # Day abbreviation
  559.                   h) date '+%h ';;  # Month abbreviation
  560.                   r) date '+%r ';;  # AM-PM time
  561.                   \?) echo "Invalid option $OPTION";;
  562.                esac
  563.             done
  564.          fi
  565.   
  566.          $ newdate -J
  567.          94031
  568.          $ newdate -a -h -d
  569.          Mon
  570.          Jan
  571.          31
  572.          $ newdate -ahd
  573.          Mon
  574.          Jan
  575.          31
  576.          $
  577.   
  578.   
  579.          示例程序:复制程序
  580.   
  581.          # Syntax: duplicate [-c integer] [-v] filename
  582.          #    where integer is the number of duplicate copies
  583.          #    and -v is the verbose option
  584.   
  585.          COPIES=1
  586.          VERBOSE=N
  587.   
  588.   
  589.          while getopts vc: OPTION
  590.          do
  591.             case $OPTION
  592.             in
  593.                c) COPIES=$OPTARG;;
  594.                v) VERBOSE=Y;;
  595.                \?) echo "Illegal Option"
  596.                    exit 1;;
  597.             esac
  598.          done
  599.   
  600.          if [ $OPTIND -gt $# ]
  601.          then
  602.             echo "No file name specified"
  603.             exit 2
  604.          fi
  605.   
  606.   
  607.          shift `expr $OPTIND -1`
  608.   
  609.          FILE=$1
  610.          COPY=0
  611.   
  612.          while [ $COPIES -gt $COPY ]
  613.          do
  614.             COPY=`expr $COPY + 1`
  615.             cp $FILE ${FILE}${COPY}
  616.             if [ VERBOSE = Y ]
  617.             then
  618.                echo ${FILE}${COPY}
  619.             fi
  620.          done
  621.   
  622.   
  623.          $ duplicate -v fileA
  624.          fileA1
  625.          $ duplicate -c 3 -v fileB
  626.          fileB1
  627.          fileB2
  628.          fileB3
  629.   
  630.   
  631. 4. Shell的定制
  632.    通常使用shell的定制来控制用户自己的环境,比如改变shell的外观(提示符)以及增强
  633.    自己的命令。
  634.   
  635. (1)通常环境变量来定制shell
  636.    通常改变环境变量可以定制shell的工作环境。shell在处理信息时会参考这些环境变量
  637.    ,改变环境变量的值在一定程度上改变shell的操作方式,比如改变命令行提示符。
  638.   
  639.    .使用IFS增加命令行分隔符
  640.      默认状态下shell的分隔符为空格、制表符及换行符,但可以通过改变IFS的值加入自

  641.      的分隔符。如下所示:
  642.   
  643.   
  644.      $ IFS=":"
  645.      $ echo:Hello:my:Friend
  646.      Hello my Friend
  647.   
  648. (2)加入自己的命令及函数
  649.    如下程序:
  650.    #Directory and Prompt change program
  651.    #Syntax: chdir directory
  652.   
  653.    if [ ! -d "$1" ]
  654.    then
  655.         echo "$1 is not a directory"
  656.         exit 1
  657.    fi
  658.   
  659.    cd $1
  660.    PS1=`pwd`$
  661.    export PS1
  662.   
  663.    $ chdir /usr/home/teresa
  664.    $
  665.   
  666.    但此程序在执行时系统提示符并不会改变,因为此程序是在子shell中执行的。因此其变

  667. 对当前shell并无影响,要想对当前shell起作用,最好是将此作为函数写在自己的.profile

  668. 或建立自己的个人函数文件.persfuncs
  669.   
  670.    #Personal function file persfuncs
  671.   
  672.    chdir()
  673.    {
  674.    #Directory and Prompt change program
  675.    #Syntax: chdir directory
  676.    if [ ! -d "$1" ]
  677.    then
  678.         echo "$1 is not a directory"
  679.         exit 1
  680.    fi
  681.   
  682.    cd $1
  683.    PS1=`pwd`$
  684.    export PS1;
  685.    }
  686.   
  687.    再执行:
  688.    $ . .persfuncs
  689.    $ chdir temp
  690.    /home/hbbwork/temp$
  691.   
  692.    也可在自己的.profile文件中用 . .persfuncs调用.persfuncs.
  693.   
  694.    说明:在bash/tcsh中已经使用别名,相对而言别名比此方法更为方便。
  695.   
  696.   
  697. 5. 有关shell的专门讨论
  698. (1)shell程序的调试
  699.    切记:程序员(人)总是会犯错误的,而计算机是不会错的。
  700.    使用-x进行跟踪执行,执行并显示每一条指令。
  701.   
  702. (2)命令组
  703.    用小括号将一组命令括起来,则这些命令会由子shell来完成;而{command_list;}则在

  704.    前shell中执行。这两者的主要区别在于其对shell变量的影响,子shell执行的命令不会
  705.    影响当前shell中的变量。
  706.   
  707.    $ NUMBER=2
  708.    $ (A=2;B=2;NUMBER=`expr $A + $B`; echo $NUMBER)
  709.    4
  710.    $ echo $NUMBER
  711.    2
  712.    $ { A=2;B=2;NUMBER=`expr $A + $B`; echo $NUMBER; }
  713.    4
  714.    $ echo $NUMBER
  715.    4
  716.   
  717.   
  718. 总结:
  719.    在本章中讲述了Bourne Shell的基本知识,使用shell变量,shell script基础。这些概

  720.    对于理解学习Korn Shell, csh以及其他script编程都是非常有用的。
  721.   
  722.    很多OS都有不少语言及一些script功能,但很少有象UNIX SHELL这样灵活强大的script

  723.    本语言能力。
  724.   
  725.    对于系统管理员或程序员来说,熟练地使用shell script将对日常工作(系统维护及管理
  726. )
  727.    非常有用,如果你想作一个合格的系统管理员,强烈建议你进一步深入的了解和使用
  728.    shell.
  729.   
  730.    另外,对于系统管理员来说,PERL也是一个必不可少的script编程语言,尤其是对于处

  731.    文本格式的各种文件,PERL具有shell, awk, sed, grep等的功能,但使用起来更为灵活

  732.    功能也更强大。大家可以参考“Perl By Examples"来学习和使用PERL。
  733.   
  734. --
复制代码
您需要登录后才可以回帖 登录 | 要注册

本版积分规则

QQ|手机版|Archiver|veket官方网站论坛 ( 粤ICP备 11052856 )

GMT+8, 2024-5-5 15:59

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表