找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 6358|回复: 0

Linux Shell常用技巧(十二)

[复制链接]
发表于 2012-4-21 10:23:51 | 显示全部楼层 |阅读模式
  1. 二十三. Bash Shell编程:
  2.     1.  读取用户变量:
  3.     read命令是用于从终端或者文件中读取输入的内建命令,read命令读取整行输入,每行末尾的换行符不被读入。在read命令后面,如果没有指定变量名,读取的数据将被自动赋值给特定的变量REPLY。下面的列表给出了read命令的常用方式:
  4. 命令格式        描述
  5. read answer        从标准输入读取输入并赋值给变量answer。
  6. read first last        从标准输入读取输入到第一个空格或者回车,将输入的第一个单词放到变量first中,并将该行其他的输入放在变量last中。
  7. read        从标准输入读取一行并赋值给特定变量REPLY。
  8. read -a arrayname        把单词清单读入arrayname的数组里。
  9. read -p prompt        打印提示,等待输入,并将输入存储在REPLY中。
  10. read -r line        允许输入包含反斜杠。
  11.     见下面的示例(绿色高亮部分的文本为控制台手工输入信息):
  12.     /> read answer        #等待读取输入,直到回车后表示输入完毕,并将输入赋值给变量answer
  13.     Hello                       #控制台输入Hello
  14.     /> echo $answer      #打印变量
  15.     Hello
  16.     #等待一组输入,每个单词之间使用空格隔开,直到回车结束,并分别将单词依次赋值给这三个读入变量。
  17.     /> read one two three
  18.     1 2 3                      #在控制台输入1 2 3,它们之间用空格隔开。
  19.     /> echo "one = $one, two = $two, three = $three"
  20.     one = 1, two = 2, three = 3
  21.     /> read                  #等待控制台输入,并将结果赋值给特定内置变量REPLY。
  22.     This is REPLY          #在控制台输入该行。
  23.     /> echo $REPLY      #打印输出特定内置变量REPLY,以确认是否被正确赋值。
  24.     This is REPLY
  25.     /> read -p "Enter your name: "    #输出"Enter your name: "文本提示,同时等待输入,并将结果赋值给REPLY。
  26.     Enter you name: stephen            #在提示文本之后输入stephen
  27.     /> echo $REPLY
  28.     stephen
  29.     #等待控制台输入,并将输入信息视为数组,赋值给数组变量friends,输入信息用空格隔开数组的每个元素
  30.     /> read -a friends
  31.     Tim Tom Helen
  32.     /> echo "I have ${#friends} friends"
  33.     I have 3 friends
  34.     /> echo "They are ${friends[0]}, ${friends[1]} and ${friends[2]}."
  35.     They are Tim, Tom and Helen.
  36.    2.  状态判断:
  37.     test是Shell中提供的内置命令,主要用于状态的检验,如果结果为0,表示成功,否则表示失败。见如下示例:
  38.     /> name=stephen
  39.     /> test $name != stephen
  40.     /> echo $?
  41.     1
  42.     需要注意的是test命令不支持Shell中提供的各种通配符,如:
  43.     /> test $name = [Ss]tephen
  44.     /> echo $?
  45.     1
  46.     test命令还可以中括号予以替换,其语义保持不变,如:
  47.     /> [ $name = stephen ]
  48.     /> echo $?
  49.     0   
  50.     在Shell中还提供了另外一种用于状态判断的方式:[[ expr ]],和test不同的是,该方式中的表达式支持通配符,如:
  51.     /> name=stephen
  52.     /> [[ $name == [Ss]tephen ]]
  53.     /> echo $?
  54.     0
  55.     #在[[ expression ]]中,expression可以包含&&(逻辑与)和||(逻辑或)。
  56.     /> [[ $name == [Ss]tephen && $friend == "Jose" ]]
  57.     /> echo $?
  58.     1
  59.     /> shopt -s extglob   #打开Shell的扩展匹配模式。
  60.     /> name=Tommy
  61.     # "[Tt]o+(m)y"的含义为,以T或t开头,后面跟着一个o,再跟着一个或者多个m,最后以一个y结尾。
  62.     /> [[ $name == [Tt]o+(m)y ]]
  63.     /> echo $?
  64.     0
  65.     在Shell中还提供了let命令的判断方式: (( expr )),该方式的expr部分,和C语言提供的表达式规则一致,如:
  66.     /> x=2
  67.     /> y=3
  68.     /> (( x > 2 ))
  69.     /> echo $?
  70.     1
  71.     /> (( x < 2 ))
  72.     /> echo $?
  73.     0  
  74.     /> (( x == 2 && y == 3 ))
  75.     /> echo $?
  76.     0
  77.     /> (( x > 2 || y < 3 ))
  78.     /> echo $?
  79.     1
  80.     下面的表格是test命令支持的操作符:
  81. 判断操作符        判断为真的条件
  82. 字符串判断         
  83. [ stringA=stringB ]        stringA等于stringB
  84. [ stringA==stringB ]        stringA等于stringB
  85. [ stringA!=stringB ]        stringA不等于stringB
  86. [ string ]        string不为空
  87. [ -z string ]        string长度为0
  88. [ -n string ]        string长度不为0
  89. 逻辑判断         
  90. [ stringA -a stringB ]        stringA和stringB都是真
  91. [ stringA -o stringB ]        stringA或stringB是真
  92. [ !string ]        string不为真
  93. 逻辑判断(复合判断)         
  94. [[ pattern1 && pattern2 ]]        pattern1和pattern2都是真
  95. [[ pattern1 || pattern2 ]        pattern1或pattern2是真
  96. [[ !pattern ]]        pattern不为真
  97. 整数判断         
  98. [ intA -eq intB ]        intA等于intB
  99. [ intA -ne intB ]        intA不等于intB
  100. [ intA -gt intB ]        intA大于intB
  101. [ intA -ge intB ]        intA大于等于intB
  102. [ intA -lt intB ]        intA小于intB
  103. [ intA -le intB ]        intA小于等于intB
  104. 文件判断中的二进制操作         
  105. [ fileA -nt fileB ]        fileA比fileB新
  106. [ fileA -ot fileB ]        fileA比fileB旧
  107. [ fileA -ef fileB ]        fileA和fileB有相同的设备或者inode值
  108. 文件检验         
  109. [ -d $file ] or [[ -d $file ]]        file为目录且存在时为真
  110. [ -e $file ] or [[ -e $file ]]        file为文件且存在时为真
  111. [ -f $file ] or [[ -f $file ]]        file为非目录普通文件存在时为真
  112. [ -s $file ] or [[ -s $file ]]        file文件存在, 且长度不为0时为真
  113. [ -L $file ] or [[ -L $file ]]        file为链接符且存在时为真
  114. [ -r $file ] or [[ -r $file ]]        file文件存在且可读时为真
  115. [ -w $file ] or [[ -w $file ]]        file文件存在且可写时为真
  116. [ -x $file ] or [[ -x $file ]]        file文件存在且可执行时为真
  117.     注:在逻辑判断(复合判读中),pattern可以包含元字符,在字符串的判断中,pattern2必须被包含在引号中。
  118.     let命令支持的操作符和C语言中支持的操作符完全相同,如:
  119.     +,-,*,/,%            加,减,乘,除,去模
  120.     >>,<<                右移和左移
  121.     >=,<=,==,!=      大于等于,小于等于,等于,不等于
  122.     &,|,^                  按位与,或,非
  123.     &&,||,!                逻辑与,逻辑或和取反
  124.     还有其含义和C语言等同的快捷操作符,如=,*=,/=,%=,+=,-=,<<=,>>=,&=,|=,^=。
  125.     3.  流程控制语句:
  126.     if语句格式如下:
  127.     #if语句的后面是Shell命令,如果该命令执行成功返回0,则执行then后面的命令。
  128.     if command        
  129.     then
  130.         command
  131.         command
  132.     fi
  133.     #用test命令测试其后面expression的结果,如果为真,则执行then后面的命令。
  134.     if test expression
  135.     then
  136.         command
  137.     fi
  138.     #下面的格式和test expression等同
  139.     if [ string/numeric expression ]
  140.     then
  141.         command
  142.     fi
  143.     #下面的两种格式也可以用于判断语句的条件表达式,而且它们也是目前比较常用的两种。
  144.     if [[ string expression ]]
  145.     then
  146.         command
  147.     fi
  148.     if (( numeric expression ))           #let表达式
  149.     then
  150.         command
  151.     fi
  152.     见如下示例:
  153.     /> cat > test1.sh                       #从命令行直接编辑test1.sh文件。
  154.     echo -e "Are you OK(y/n)? \c"
  155.     read answer
  156.     #这里的$answer变量必须要用双引号扩住,否则判断将失败。当变量$answer等于y或Y时,支持下面的echo命令。
  157.     if [ "$answer" = y -o "$answer" = Y ]   
  158.     then
  159.         echo "Glad to see it."
  160.     fi
  161.     CTRL+D  
  162.     /> . ./test1.sh
  163.     Are you OK(y/n)? y
  164.     Glad to see it.
  165.     上面的判断还可以替换为:
  166.     /> cat > test2.sh
  167.     echo -e "Are you OK(y/n or Maybe)? \c"
  168.     read answer
  169.     # [[ ]]复合命令操作符允许其中的表达式包含元字符,这里输入以y或Y开头的任意单词,或Maybe都执行then后面的echo。
  170.     if [[ $answer == [yY]* || $answer = Maybe ]]  
  171.     then
  172.         echo "Glad to hear it.
  173.     fi
  174.     CTRL+D
  175.     /> . ./test2.sh
  176.     Are you OK(y/n or Maybe)? yes
  177.     Glad to hear it.
  178.     下面的例子将使用Shell中的扩展通配模式。
  179.     /> shopt -s extglob        #打开该扩展模式
  180.     /> answer="not really"
  181.     /> if [[ $answer = [Nn]o?( way |t really) ]]
  182.     > then
  183.     >    echo "I am sorry."
  184.     > fi
  185.     I am sorry.
  186.     对于本示例中的扩展通配符,这里需要给出一个具体的解释。[Nn]o匹配No或no,?( way|t really)则表示0个或1个( way或t really),因此answer变量匹配的字符串为No、no、Not really、not really、No way、no way。
  187.     下面的示例使用了let命令操作符,如:
  188.     /> cat > test3.sh
  189.     if (( $# != 2 ))                    #等同于 [ $# -ne 2 ]
  190.     then
  191.         echo "Usage: $0 arg1 arg2" 1>&2
  192.         exit 1                         #exit退出值为0-255之间,只有0表示成功。
  193.     fi
  194.     if (( $1 < 0 || $1 > 30 ))      #等同于 [ $1 -lt 0 -o $1 -gt 30 ]
  195.     then
  196.         echo "arg1 is out of range."
  197.         exit 2
  198.     fi
  199.     if (( $2 <= 20 ))                  #等同于 [ $2 -le 20 ]
  200.     then
  201.         echo "arg2 is out of range."
  202.     fi
  203.     CTRL+D
  204.     /> sh ./test3.sh
  205.     Usage: ./test3.sh arg1 arg2
  206.     /> echo $?                          #Shell脚本的退出值为exit的参数值。
  207.     1
  208.     /> sh ./test3.sh 40 30
  209.     arg1 is out of range.
  210.     /> echo $?
  211.     2
  212.     下面的示例为如何在if的条件表达式中检验空变量:
  213.     /> cat > test4.sh
  214.     if [ "$name" = "" ]                #双引号就表示空字符串。
  215.     then
  216.         echo "name is null."
  217.     fi
  218.     CTRL+D
  219.     /> . ./test4.sh
  220.     name is null.
  221.     if/elif/else语句的使用方式和if语句极为相似,相信有编程经验的人都不会陌生,这里就不在赘述了,其格式如下:
  222.     if command
  223.     then
  224.         command
  225.     elif command
  226.     then
  227.         command
  228.     else
  229.         command
  230.     fi
  231.     见如下示例脚本:
  232.     /> cat > test5.sh
  233.     echo -e "How old are you? \c"
  234.     read age
  235.     if [ $age -lt 0 -o $age -gt 120 ]                #等同于 (( age < 0 || age > 120 ))
  236.     then
  237.         echo "You are so old."
  238.     elif [ $age -ge 0 -a $age -le 12 ]               #等同于 (( age >= 0 && age <= 12 ))
  239.     then
  240.         echo "You are child."
  241.     elif [ $age -ge 13 -a $age -le 19 ]             #等同于 (( age >= 13 && age <= 19 ))
  242.     then
  243.         echo "You are 13--19 years old."
  244.     elif [ $age -ge 20 -a $age -le 29 ]             #等同于 (( age >= 20 && age <= 29 ))
  245.     then
  246.         echo "You are 20--29 years old."
  247.     elif [ $age -ge 30 -a $age -le 39 ]             #等同于 (( age >= 30 && age <= 39 ))
  248.     then
  249.         echo "You are 30--39 years old."
  250.     else
  251.         echo "You are above 40."
  252.     fi
  253.     CTRL+D
  254.     /> . ./test5.sh
  255.     How old are you? 50
  256.     You are above 40.
  257.     case语句格式如下:
  258.     case variable in
  259.     value1)
  260.         command
  261.         ;;            #相同于C语言中case语句内的break。
  262.     value2)
  263.         command
  264.         ;;
  265.     *)                #相同于C语言中switch语句内的default
  266.        command
  267.         ;;
  268.     esac
  269.     见如下示例脚本:
  270.     /> cat > test6.sh
  271.     #!/bin/sh
  272.     echo -n "Choose a color: "
  273.     read color
  274.     case "$color" in
  275.     [Bb]l??)
  276.         echo "you select blue color."
  277.         ;;
  278.     [Gg]ree*)
  279.         echo "you select green color."
  280.         ;;
  281.     red|orange)
  282.         echo "you select red or orange."
  283.         ;;
  284.     *)
  285.         echo "you select other color."
  286.         ;;
  287.     esac
  288.     echo "Out of case command."
  289.     /> . ./test6.sh
  290.     Choose a color: green
  291.     you select green color.
  292.     Out of case command.
  293.    4.  循环语句:
  294.     Bash Shell中主要提供了三种循环方式:for、while和until。
  295.     for循环声明格式:
  296.     for variable in word_list
  297.     do
  298.         command
  299.     done
  300.     见如下示例脚本:
  301.     /> cat > test7.sh
  302.     for score in math english physics chemist   #for将循环读取in后面的单词列表,类似于Java的for-each。
  303.     do
  304.         echo "score = $score"
  305.     done
  306.     echo "out of for loop"
  307.     CTRL+D
  308.     /> . ./test7.sh
  309.     score = math
  310.     score = english
  311.     score = physics
  312.     score = chemist
  313.     out of for loop
  314.     /> cat > mylist   #构造数据文件
  315.     tom
  316.     patty
  317.     ann
  318.     jake
  319.     CTRL+D
  320.     /> cat > test8.sh
  321.     #!/bin/sh
  322.     for person in $(cat mylist)                 #for将循环读取cat mylist命令的执行结果。
  323.     do
  324.         echo "person = $person"
  325.     done
  326.     echo "out of for loop."
  327.     CTRL+D
  328.     /> . ./test8.sh
  329.     person = tom
  330.     person = patty
  331.     person = ann
  332.     person = jake
  333.     out of for loop.
  334.     /> cat > test9.sh
  335.     for file in test[1-8].sh                        #for将读取test1-test8,后缀为.sh的文件
  336.     do
  337.         if [ -f $file ]                              #判断文件在当前目录是否存在。
  338.         then
  339.             echo "$file exists."
  340.         fi
  341.     done
  342.     CTRL+D
  343.     /> . ./test9.sh
  344.     test2.sh exists.
  345.     test3.sh exists.
  346.     test4.sh exists.
  347.     test5.sh exists.
  348.     test6.sh exists.
  349.     test7.sh exists.
  350.     test8.sh exists.
  351.     /> cat > test10.sh
  352.     for name in $*                                  #读取脚本的命令行参数数组,还可以写成for name的简化形式。
  353.     do
  354.         echo "Hi, $name"
  355.     done
  356.     CTRL+D
  357.     /> . ./test10.sh stephen ann
  358.     Hi, stephen
  359.     Hi, ann
  360.     while循环声明格式:
  361.     while command  #如果command命令的执行结果为0,或条件判断为真时,执行循环体内的命令。
  362.     do
  363.         command
  364.     done
  365.     见如下示例脚本:
  366.     /> cat > test1.sh  
  367.     num=0
  368.     while (( num < 10 ))               #等同于 [ $num -lt 10 ]
  369.     do
  370.         echo -n "$num "
  371.         let num+=1
  372.     done
  373.     echo -e "\nHere's out of loop."
  374.     CTRL+D
  375.     /> . ./test1.sh
  376.     0 1 2 3 4 5 6 7 8 9
  377.     Here's out of loop.
  378.     /> cat > test2.sh
  379.     go=start
  380.     echo Type q to quit.
  381.     while [[ -n $go ]]                     #等同于[ -n "$go" ],如使用该风格,$go需要被双引号括起。
  382.     do
  383.         echo -n How are you.
  384.         read word
  385.         if [[ $word == [Qq] ]]      #等同于[ "$word" = Q -o "$word" = q ]
  386.         then
  387.             echo Bye.
  388.             go=                        #将go变量的值置空。
  389.         fi
  390.     done
  391.     CTRL+D
  392.     /> . ./test2.sh
  393.     How are you. Hi
  394.     How are you. q
  395.     Bye.
  396.     until循环声明格式:
  397.     until command                         #其判断条件和while正好相反,即command返回非0,或条件为假时执行循环体内的命令。
  398.     do
  399.         command
  400.     done
  401.     见如下示例脚本:
  402.     /> cat > test3.sh
  403.     until who | grep stephen           #循环体内的命令将被执行,直到stephen登录,即grep命令的返回值为0时才退出循环。
  404.     do
  405.         sleep 1
  406.         echo "Stephen still doesn't login."
  407.     done
  408.     CTRL+D
  409.     shift命令声明格式:shift [n]
  410.     shift命令用来把脚本的位置参数列表向左移动指定的位数(n),如果shift没有参数,则将参数列表向左移动一位。一旦移位发生,被移出列表的参数就被永远删除了。通常在while循环中,shift用来读取列表中的参数变量。
  411.     见如下示例脚本:
  412.     /> set stephen ann sheryl mark #设置4个参数变量。
  413.     /> shift                                    #向左移动参数列表一次,将stephen移出参数列表。
  414.     /> echo $*
  415.     ann sheryl mark
  416.     /> shift 2                                 #继续向左移动两位,将sheryl和ann移出参数列表
  417.     /> echo $*
  418.     mark
  419.     /> shift 2                                 #继续向左移动两位,由于参数列表中只有mark了,因此本次移动失败。
  420.     /> echo $*
  421.     mark
  422.     /> cat > test4.sh
  423.     while (( $# > 0 ))                    #等同于 [ $# -gt 0 ]
  424.     do
  425.         echo $*
  426.         shift
  427.     done
  428.     CTRL+D
  429.     /> . ./test4.sh a b c d e
  430.     a b c d e
  431.     b c d e
  432.     c d e
  433.     d e
  434.     e        
  435.     break命令声明格式:break [n]
  436.     和C语言不同的是,Shell中break命令携带一个参数,即可以指定退出循环的层数。如果没有指定,其行为和C语言一样,即退出最内层循环。如果指定循环的层数,则退出指定层数的循环体。如果有3层嵌套循环,其中最外层的为1,中间的为2,最里面的是3。
  437.     见如下示例脚本:
  438.     /> cat > test5.sh
  439.     while true
  440.     do
  441.         echo -n "Are you ready to move on?"
  442.         read answer
  443.         if [[ $answer == [Yy] ]]
  444.         then
  445.             break
  446.         else
  447.             echo "Come on."
  448.         fi
  449.     done
  450.     echo "Here we are."
  451.     CTRL+D
  452.     /> . ./test5.sh
  453.     Are you ready to move on? y
  454.     Here we are
  455.     continue命令声明格式:continue [n]
  456.     和C语言不同的是,Shell中continue命令携带一个参数,即可以跳转到指定层级的循环顶部。如果没有指定,其行为和C语言一样,即跳转到最内层循环的顶部。如果指定循环的层数,则跳转到指定层级循环的顶部。如果有3层嵌套循环,其中最外层的为3,中间的为2,最里面的是1。
  457.     /> cat  maillist                       #测试数据文件maillist的内容为以下信息。
  458.     stephen
  459.     ann
  460.     sheryl
  461.     mark
  462.     /> cat > test6.sh
  463.     for name in $(cat maillist)
  464.     do
  465.         if [[ $name == stephen ]]; then
  466.             continue
  467.         else
  468.             echo "Hello, $name."
  469.         fi
  470.     done
  471.     CTRL+D
  472.     /> . ./test6.sh
  473.     Hello, ann.
  474.     Hello, sheryl.
  475.     Hello, mark.
  476.     I/O重新定向和子Shell:
  477.     文件中的输入可以通过管道重新定向给一个循环,输出也可以通过管道重新定向给一个文件。Shell启动一个子Shell来处理I/O重新定向和管道。在循环终止时,循环内部定义的任何变量对于脚本的其他部分来说都是不看见的。
  478.     /> cat > demodata                        #为下面的脚本构造册数数据
  479.     abc
  480.     def
  481.     ghi
  482.     CRTL+D
  483.     /> cat > test7.sh
  484.     if (( $# < 1 ))                                #如果脚本参数的数量小于1,则给出错误提示后退出。
  485.     then
  486.         echo "Usage: $0 filename " >&2
  487.         exit 1
  488.     fi
  489.     count=1
  490.     cat $1 | while read line                   #参数一中的文件被cat命令输出后,通过管道逐行输出给while read line。
  491.     do
  492.         let $((count == 1)) && echo "Processing file $1..." > /dev/tty  #该行的echo将输出到当前终端窗口。
  493.         echo -e "$count\t$line"              #将输出行号和文件中该行的内容,中间用制表符隔开。
  494.         let count+=1
  495.     done > outfile                               #将while循环中所有的输出,除了>/dev/tty之外,其它的全部输出到outfile文件。
  496.     CTRL+D
  497.     /> . ./test7.sh demodata                #只有一行输出,其余的都输出到outfile中了。
  498.     Processing file demodata...
  499.     /> cat outfile
  500.     1       abc
  501.     2       def
  502.     3       ghi
  503.     /> cat > test8.sh
  504.     for i in 9 7 2 3 5 4
  505.     do
  506.         echo $i
  507.     done | sort -n                                #直接将echo的输出通过管道重定向sort命令。
  508.     CTRL+D
  509.     /> . ./test8.sh
  510.     2
  511.     3
  512.     4
  513.     5
  514.     7
  515.     9
  516.     5.  IFS和循环:
  517.     Shell的内部域分隔符可以是空格、制表符和换行符。它可以作为命令的分隔符用在例如read、set和for等命令中。如果在列表中使用不同的分隔符,用户可以自己定义这个符号。在修改之前将IFS原始符号的值保存在另外一个变量中,这样在需要的时候还可以还原。
  518.     见如下示例脚本:
  519.     /> cat > test9.sh
  520.     names=Stephen:Ann:Sheryl:John   #names变量包含的值用冒号分隔。
  521.     oldifs=$IFS                                   #保留原有IFS到oldifs变量,便于后面的还原。
  522.     IFS=":"                           
  523.     for friends in $names                     #这是遍历以冒号分隔的names变量值。   
  524.     do
  525.         echo Hi $friends
  526.     done
  527.     IFS=$oldifs                                   #将IFS还原为原有的值。
  528.     set Jerry Tom Angela
  529.     for classmates in $*                      #再以原有IFS的值变量参数列表。
  530.     do
  531.         echo Hello $classmates
  532.     done
  533.     CTRL+D
  534.     /> . ./test9.sh
  535.     Hi Stephen
  536.     Hi Ann
  537.     Hi Sheryl
  538.     Hi John
  539.     Hello Jerry
  540.     Hello Tom
  541.     Hello Angela
  542.     6.  函数:
  543.     Shell中函数的职能以及优势和C语言或其它开发语言基本相同,只是语法格式上的一些差异。下面是Shell中使用函数的一些基本规则:
  544.     1) 函数在使用前必须定义。
  545.     2) 函数在当前环境下运行,它和调用它的脚本共享变量,并通过位置参量传递参数。而该位置参量将仅限于该函数,不会影响到脚本的其它地方。
  546.     3) 通过local函数可以在函数内建立本地变量,该变量在出了函数的作用域之后将不在有效。
  547.     4) 函数中调用exit,也将退出整个脚本。
  548.     5) 函数中的return命令返回函数中最后一个命令的退出状态或给定的参数值,该参数值的范围是0-256之间。如果没有return命令,函数将返回最后一个Shell的退出值。
  549.     6) 如果函数保存在其它文件中,就必须通过source或dot命令把它们装入当前脚本。
  550.     7) 函数可以递归。
  551.     8) 将函数从Shell中清空需要执行:unset -f function_name。
  552.     9) 将函数输出到子Shell需要执行:export -f function_name。
  553.     10) 可以像捕捉Shell命令的返回值一样获取函数的返回值,如$(function_name)。
  554.     Shell中函数的声明格式如下:
  555.     function function_name { command; command; }
  556.     见如下示例脚本:
  557.     /> cat > test1.sh
  558.     function increment() {            #定义函数increment。
  559.         local sum                           #定义本地变量sum。
  560.         let "sum=$1+1"   
  561.         return $sum                      #返回值是sum的值。
  562.     }
  563.     echo -n "The num is "
  564.     increment 5                          #increment函数调用。
  565.     echo $?                                #输出increment函数的返回值。
  566.     CTRL+D
  567.     /> . ./test1.sh
  568.     The num is 6
  569.     7.  陷阱信号(trap):
  570.     在Shell程序运行的时候,可能收到各种信号,有的来自于操作系统,有的来自于键盘,而该Shell在收到信号后就立刻终止运行。但是在有些时候,你可能并不希望在信号到达时,程序就立刻停止运行并退出。而是他能希望忽略这个信号而一直在运行,或者在退出前作一些清除操作。trap命令就允许你控制你的程序在收到信号以后的行为。
  571.     其格式如下:
  572.     trap 'command; command' signal-number
  573.     trap 'command; command' signal-name
  574.     trap signal-number  
  575.     trap signal-name
  576.     后面的两种形式主要用于信号复位,即恢复处理该信号的缺省行为。还需要说明的是,如果trap后面的命令是使用单引号括起来的,那么该命令只有在捕获到指定信号时才被执行。如果是双引号,则是在trap设置时就可以执行变量和命令替换了。
  577.     下面是系统给出的信号数字和信号名称的对照表:
  578.     1)SIGHUP 2)SIGINT 3)SIGQUIT 4)SIGILL 5)SIGTRAP 6)SIGABRT 7)SIGBUS 8)SIGFPE
  579.     9)SIGKILL 10) SIGUSR1 11)SIGEGV 12)SIGUSR2 13)SIGPIPE 14)SIGALRM 15)SIGTERM 17)SIGCHLD
  580.     18)SIGCONT 19)SIGSTOP ... ...
  581.     见如下示例脚本:
  582.     /> trap 'rm tmp*;exit 1' 1 2 15      #该命令表示在收到信号1、2和15时,该脚本将先执行rm tmp*,然后exit 1退出脚本。
  583.     /> trap 2                                      #当收到信号2时,将恢复为以前的动作,即退出。
  584.     /> trap " " 1 2                              #当收到信号1和2时,将忽略这两个信号。
  585.     /> trap -                                      #表示恢复所有信号处理的原始值。
  586.     /> trap 'trap 2' 2                           #在第一次收到信号2时,执行trap 2,这时将信号2的处理恢复为缺省模式。在收到信号2时,Shell程序退出。
  587.     /> cat > test2.sh
  588.     trap 'echo "Control+C will not terminate $0."' 2   #捕获信号2,即在键盘上按CTRL+C。
  589.     trap 'echo "Control+\ will not terminate $0."' 3   #捕获信号3,即在键盘上按CTRL+\。
  590.     echo "Enter stop to quit shell."
  591.     while true                                                        #无限循环。
  592.     do
  593.         echo -n "Go Go...."
  594.         read
  595.         if [[ $REPLY == [Ss]top ]]                            #直到输入stop或Stop才退出循环和脚本。
  596.        then
  597.             break
  598.         fi
  599.     done
  600.     CTRL+D
  601.     /> . ./test2.sh
  602.     Enter stop to quit shell.
  603.     Go Go....^CControl+C will not terminate -bash.
  604.     ^\Control+\ will not terminate -bash.
  605.     stop
  606.     8.  用getopts处理命令行选项:
  607.     这里的getopts命令和C语言中的getopt几乎是一致的,因为脚本的位置参量在有些时候是失效的,如ls -lrt等。这时候-ltr都会被保存在$1中,而我们实际需要的则是三个展开的选项,即-l、-r和-t。见如下带有getopts的示例脚本:
  608.     /> cat > test3.sh
  609.     #!/bin/sh
  610.     while getopts xy options                           #x和y是合法的选项,并且将-x读入到变量options中,读入时会将x前面的横线去掉。
  611.     do
  612.         case $options in
  613.         x) echo "you entered -x as an option" ;;      
  614.         y) echo "you entered -y as an option" ;;
  615.         esac
  616.     done
  617.     /> ./test3.sh -xy
  618.     you entered -x as an option
  619.     you entered -y as an option
  620.     /> ./test3.sh -x
  621.     you entered -x as an option
  622.     /> ./test3.sh -b                                       #如果输入非法选项,getopts会把错误信息输出到标准错误。
  623.     ./test3.sh: illegal option -- b
  624.     /> ./test3.sh b                                        #该命令不会有执行结果,因为b的前面有没横线,因此是非法选项,将会导致getopts停止处理并退出。
  625.     /> cat > test4.sh
  626.     #!/bin/sh
  627.     while getopts xy options 2>/dev/null         #如果再出现选项错误的情况,该重定向会将错误输出到/dev/null。
  628.     do
  629.         case $options in
  630.         x) echo "you entered -x as an option" ;;
  631.         y) echo "you entered -y as an option" ;;
  632.         \?) echo "Only -x and -y are valid options" 1>&2 # ?表示所有错误的选项,即非-x和-y的选项。
  633.     esac
  634.     done
  635.     /> . ./test4.sh -g                                     #遇到错误的选项将直接执行\?)内的代码。
  636.     Only -x and -y are valid options
  637.     /> . ./test4.sh -xg
  638.     you entered -x as an option
  639.     Only -x and -y are valid options
  640.     /> cat > test5.sh
  641.     #!/bin/sh
  642.     while getopts xyz: arguments 2>/dev/null #z选项后面的冒号用于提示getopts,z选项后面必须有一个参数。
  643.     do
  644.         case $arguments in
  645.         x) echo "you entered -x as an option." ;;
  646.         y) echo "you entered -y as an option." ;;
  647.         z) echo "you entered -z as an option."  #z的后面会紧跟一个参数,该参数保存在内置变量OPTARG中。
  648.             echo "\$OPTARG is $OPTARG.";
  649.            ;;
  650.         \?) echo "Usage opts4 [-xy] [-z argument]"
  651.             exit 1 ;;
  652.         esac
  653.     done
  654.     echo "The number of arguments passed was $(( $OPTIND - 1 ))" #OPTIND保存一下将被处理的选项的位置,他是永远比实际命令行参数多1的数。
  655.     /> ./test5.sh -xyz foo
  656.     you entered -x as an option.
  657.     you entered -y as an option.
  658.     you entered -z as an option.
  659.     $OPTARG is foo.
  660.     The number of arguments passed was 2
  661.     /> ./test5.sh -x -y -z boo
  662.     you entered -x as an option.
  663.     you entered -y as an option.
  664.     you entered -z as an option.
  665.     $OPTARG is boo.
  666.     The number of arguments passed was 4
  667.     9.  eval命令与命令行解析:
  668.     eval命令可以对命令行求值,做Shell替换,并执行命令行,通常在普通命令行解析不能满足要求时使用。
  669.     /> set a b c d
  670.     /> echo The last argument is \$#
  671.     The last argument is $4
  672.     /> eval echo The last argument is \$#    #eval命令先进行了变量替换,之后再执行echo命令。
  673.     The last argument is d
复制代码
来自:http://www.cnblogs.com/stephen-l ... /12/19/2265416.html

目录:
Linux Shell常用技巧(一)
http://www.acejoy.com/thread-4312-1-1.html
一. 特殊文件: /dev/null和/dev/tty
二. 简单的命令跟踪
三. 正则表达式基本语法描述
四. 使用cut命令选定字段
五. 计算行数、字数以及字符数
六. 提取开头或结尾数行

Linux Shell常用技巧(二)
http://www.acejoy.com/thread-4313-1-1.html
七. grep家族

Linux Shell常用技巧(三)
http://www.acejoy.com/thread-4314-1-1.html
八. 流编辑器sed

Linux Shell常用技巧(四)
http://www.acejoy.com/thread-4315-1-1.html
九. awk实用功能
十. awk表达式功能

Linux Shell常用技巧(五)
http://www.acejoy.com/thread-4316-1-1.html
十一.awk编程

Linux Shell常用技巧(六)
http://www.acejoy.com/thread-4317-1-1.html
十二.行的排序命令sort
十三.删除重复行的命令uniq
十四.文件压缩解压命令tar
十五.大文件拆分命令split

Linux Shell常用技巧(七)
http://www.acejoy.com/thread-4318-1-1.html
十六.文件查找命令find
十七.xargs命令

Linux Shell常用技巧(八)
http://www.acejoy.com/thread-4319-1-1.html
十八.和系统运行状况相关的Shell命令

Linux Shell常用技巧(九)
http://www.acejoy.com/thread-4320-1-1.html
十九.和系统运行进程相关的Shell命令

Linux Shell常用技巧(十)
http://www.acejoy.com/thread-4321-1-1.html
二十.通过管道组合Shell命令获取系统运行数据
二十一.通过管道组合Shell命令进行系统管理

Linux Shell常用技巧(十一)
http://www.acejoy.com/thread-4322-1-1.html
二十二.交互式使用Bash Shell

Linux Shell常用技巧(十二)
http://www.acejoy.com/thread-4323-1-1.html
二十三.Bash Shell编程

您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-4-26 23:07 , Processed in 0.012740 second(s), 6 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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