专注各种脚本编程
Baidu
加入收藏夹
本站内容有下面分类知识,欢迎您的到来^_^
shell相关:指令篇 基础篇 脚本欣赏 编程实例 shell问问 shell视频教程 技巧篇 水平测试 E文资料 vi编辑器 高级Bash脚本编程指南
其他:mysql perl c语言 oracle
当前位置:| 主页>高级Bash脚本编程指南>

10.1. 循环(2)

百度收藏 QQ搜藏

下边是一个用命令替换来产生[list]的更复杂的例子.


--------------------------------------------------------------------------------

例子 10-7. 对于二进制文件的grep替换

   1 #!/bin/bash
   2 # bin-grep.sh: 在一个二进制文件中定位匹配字串.
   3
   4 # 对于二进制文件的一个grep替换
   5 # 与"grep -a"的效果相似
   6
   7 E_BADARGS=65
   8 E_NOFILE=66
   9
  10 if [ $# -ne 2 ]
  11 then
  12   echo "Usage: `basename $0` search_string filename"
  13   exit $E_BADARGS
  14 fi
  15
  16 if [ ! -f "$2" ]
  17 then
  18   echo "File \"$2\" does not exist."
  19   exit $E_NOFILE
  20 fi 
  21
  22
  23 IFS="\n"         # 由Paulo Marcel Coelho Aragao提出的建议.
  24 for word in $( strings "$2" | grep "$1" )
  25 # "strings" 命令列出二进制文件中的所有字符串.
  26 # 输出到管道交给"grep",然后由grep命令来过滤字符串.
  27 do
  28   echo $word
  29 done
  30
  31 # S.C. 指出, 行23 - 29 可以被下边的这行来代替,
  32 #    strings "$2" | grep "$1" | tr -s "$IFS" '[\n*]'
  33
  34
  35 # 试试用"./bin-grep.sh mem /bin/ls"来运行这个脚本.
  36
  37 exit 0
 

--------------------------------------------------------------------------------

大部分相同.


--------------------------------------------------------------------------------

例子 10-8. 列出系统上的所有用户

   1 #!/bin/bash
   2 # userlist.sh
   3
   4 PASSWORD_FILE=/etc/passwd
   5 n=1           # User number
   6
   7 for name in $(awk 'BEGIN{FS=":"}{print $1}' < "$PASSWORD_FILE" )
   8 # 域分隔   = :           ^^^^^^
   9 # 打印出第一个域                 ^^^^^^^^
  10 # 从password文件中取得输入                    ^^^^^^^^^^^^^^^^^
  11 do
  12   echo "USER #$n = $name"
  13   let "n += 1"
  14 done 
  15
  16
  17 # USER #1 = root
  18 # USER #2 = bin
  19 # USER #3 = daemon
  20 # ...
  21 # USER #30 = bozo
  22
  23 exit 0
  24
  25 #  练习:
  26 #  --------
  27 #  一个普通用户(或者是一个普通用户运行的脚本)
  28 #+ 怎么能读取/etc/password呢?
  29 #  这是否是一个安全漏洞? 为什么是?为什么不是?
 

--------------------------------------------------------------------------------

关于用命令替换来产生[list]的最后的例子.


--------------------------------------------------------------------------------

例子 10-9. 在目录的所有文件中查找源字串

   1 #!/bin/bash
   2 # findstring.sh:
   3 # 在一个指定目录的所有文件中查找一个特定的字符串.
   4
   5 directory=/usr/bin/
   6 fstring="Free Software Foundation"  # 查看那个文件中包含FSF.
   7
   8 for file in $( find $directory -type f -name '*' | sort )
   9 do
  10   strings -f $file | grep "$fstring" | sed -e "s%$directory%%"
  11   #  在"sed"表达式中,
  12   #+ 我们必须替换掉正常的替换分隔符"/",
  13   #+ 因为"/"碰巧是我们需要过滤的字串之一.
  14   #  如果不用"%"代替"/"作为分隔符,那么这个操作将失败,并给出一个错误消息.(试试)
  15 done 
  16
  17 exit 0
  18
  19 #  练习 (容易):
  20 #  ---------------
  21 #  将内部用的$directory和$fstring变量,用从
  22 #+ 命令行参数代替.
 

--------------------------------------------------------------------------------

for循环的输出也可以通过管道传递到一个或多个命令中.


--------------------------------------------------------------------------------

例子 10-10. 列出目录中所有的符号连接(symbolic links)

   1 #!/bin/bash
   2 # symlinks.sh: 列出目录中所有的符号连接文件.
   3
   4
   5 directory=${1-`pwd`}
   6 #  如果没有其他的特殊指定,
   7 #+ 默认为当前工作目录.
   8 #  下边的代码块,和上边这句等价.
   9 # ----------------------------------------------------------
  10 # ARGS=1                 # 需要一个命令行参数.
  11 #
  12 # if [ $# -ne "$ARGS" ]  # 如果不是一个参数的话...
  13 # then
  14 #   directory=`pwd`      # 当前工作目录
  15 # else
  16 #   directory=$1
  17 # fi
  18 # ----------------------------------------------------------
  19
  20 echo "symbolic links in directory \"$directory\""
  21
  22 for file in "$( find $directory -type l )"   # -type l 就是符号连接文件
  23 do
  24   echo "$file"
  25 done | sort                                  # 否则列出的文件将是未排序的
  26 #  严格上说,此处并不一定非要一个循环不可,
  27 #+ 因为"find"命令的结果将被扩展成一个单词.
  28 #  然而,这种方式很容易理解和说明.
  29
  30 #  Dominik 'Aeneas' Schnitzer 指出,
  31 #+ 如果没将 $( find $directory -type l )用""引用起来的话
  32 #+ 那么将会把一个带有空白部分的文件名拆成以空白分隔的两部分(文件名中允许有空白).
  33 #  即使这只将取出每个参数的第一个域.
  34
  35 exit 0
  36
  37
  38 # Jean Helou 建议使用下边的方法:
  39
  40 echo "symbolic links in directory \"$directory\""
  41 # 当前IFS的备份.要小心使用这个值.
  42 OLDIFS=$IFS
  43 IFS=:
  44
  45 for file in $(find $directory -type l -printf "%p$IFS")
  46 do     #                              ^^^^^^^^^^^^^^^^
  47        echo "$file"
  48        done|sort
 

--------------------------------------------------------------------------------

循环的输出可以重定向到文件中,我们对上边的例子做了一点修改.


--------------------------------------------------------------------------------

例子 10-11. 将目录中的符号连接文件名保存到一个文件中

   1 #!/bin/bash
   2 # symlinks.sh: 列出目录中所有的符号连接文件.
   3
   4 OUTFILE=symlinks.list                         # 保存的文件
   5
   6 directory=${1-`pwd`}
   7 #  如果没有其他的特殊指定,
   8 #+ 默认为当前工作目录.
   9
  10
  11 echo "symbolic links in directory \"$directory\"" > "$OUTFILE"
  12 echo "---------------------------" >> "$OUTFILE"
  13
  14 for file in "$( find $directory -type l )"    # -type l 为寻找类型为符号链接的文件
  15 do
  16   echo "$file"
  17 done | sort >> "$OUTFILE"                     # 循环的输出
  18 #           ^^^^^^^^^^^^^                       重定向到一个文件中.
  19
  20 exit 0
 

--------------------------------------------------------------------------------

有一种非常像C语言的for循环的语法形式.这需要使用(()).


--------------------------------------------------------------------------------

例子 10-12. 一个C风格的for循环

   1 #!/bin/bash
   2 # 两种循环到10的方法.
   3
   4 echo
   5
   6 # 标准语法.
   7 for a in 1 2 3 4 5 6 7 8 9 10
   8 do
   9   echo -n "$a "
  10 done 
  11
  12 echo; echo
  13
  14 # +==========================================+
  15
  16 # 现在, 让我们用C风格的语法做同样的事.
  17
  18 LIMIT=10
  19
  20 for ((a=1; a <= LIMIT ; a++))  # 双圆括号, 并且"LIMIT"变量前边没有 "$".
  21 do
  22   echo -n "$a "
  23 done                           # 这是一个借用'ksh93'的结构.
  24
  25 echo; echo
  26
  27 # +=========================================================================+
  28
  29 # 让我们使用C的逗号操作符,来同时增加两个变量的值.
  30
  31 for ((a=1, b=1; a <= LIMIT ; a++, b++))  # 逗号将同时进行2条操作.
  32 do
  33   echo -n "$a-$b "
  34 done
  35
  36 echo; echo
  37
  38 exit 0
 

--------------------------------------------------------------------------------

参考例子 26-15, 例子 26-16, 和 例子 A-6.

---

现在来一个现实生活中使用的for循环.


--------------------------------------------------------------------------------

例子 10-13. 在batch mode中使用efax

   1 #!/bin/bash
   2 # Faxing ('fax' 必须已经被安装过了).
   3
   4 EXPECTED_ARGS=2
   5 E_BADARGS=65
   6
   7 if [ $# -ne $EXPECTED_ARGS ]
   8 # 检查命令行参数的个数是否正确.
   9 then
  10    echo "Usage: `basename $0` phone# text-file"
  11    exit $E_BADARGS
  12 fi
  13
  14
  15 if [ ! -f "$2" ]
  16 then
  17   echo "File $2 is not a text file"
  18   exit $E_BADARGS
  19 fi
  20  
  21
  22 fax make $2              # 从文本文件中创建传真格式的文件.
  23
  24 for file in $(ls $2.0*)  # 连接转换过的文件.
  25                          # 在变量列表中使用通配符.
  26 do
  27   fil="$fil $file"
  28 done 
  29
  30 efax -d /dev/ttyS3 -o1 -t "T$1" $fil   # 干活的地方.
  31
  32
  33 # S.C. 指出, 通过下边的命令可以省去for循环.
  34 #    efax -d /dev/ttyS3 -o1 -t "T$1" $2.0*
  35 # 但这并不十分有讲解意义[嘿嘿].
  36
  37 exit 0
 

--------------------------------------------------------------------------------

while
这种结构在循环的开头判断条件是否满足,如果条件一直满足,那就一直循环下去(0为退出码[exit status]).与for 循环的区别是,这种结构适合用在循环次数未知的情况下.


while [condition]
do
  command...
done


和for循环一样,如果想把do和条件放到同一行上还是需要一个";".


while [condition] ; do


注意一下某种特定的while循环,比如getopts结构,好像和这里所介绍的模版有点脱节.


--------------------------------------------------------------------------------

例子 10-14. 简单的while循环

   1 #!/bin/bash
   2
   3 var0=0
   4 LIMIT=10
   5
   6 while [ "$var0" -lt "$LIMIT" ]
   7 do
   8   echo -n "$var0 "        # -n 将会阻止产生新行.
   9   #             ^           空格,数字之间的分隔.
  10
  11   var0=`expr $var0 + 1`   # var0=$(($var0+1))  也可以.
  12                           # var0=$((var0 + 1)) 也可以.
  13                           # let "var0 += 1"    也可以.
  14 done                      # 使用其他的方法也行.
  15
  16 echo
  17
  18 exit 0
 

--------------------------------------------------------------------------------


--------------------------------------------------------------------------------

例子 10-15. 另一个while循环

   1 #!/bin/bash
   2
   3 echo
   4                                # 等价于:
   5 while [ "$var1" != "end" ]     # while test "$var1" != "end"
   6 do
   7   echo "Input variable #1 (end to exit) "
   8   read var1                    # 为什么不使用'read $var1'?
   9   echo "variable #1 = $var1"   # 因为包含"#"字符,所以需要"" . . .
  10   # 如果输入为'end',那么就在这里打印.
  11   # 不在这里判断结束,在循环顶判断.
  12   echo
  13 done 
  14
  15 exit 0
 

--------------------------------------------------------------------------------
一个while循环可以有多个判断条件,但是只有最后一个才能决定是否退出循环.然而这需要一种有点不同的循环语法.
--------------------------------------------------------------------------------

例子 10-16. 多条件的while循环

   1 #!/bin/bash
   2
   3 var1=unset
   4 previous=$var1
   5
   6 while echo "previous-variable = $previous"
   7       echo
   8       previous=$var1
   9       [ "$var1" != end ] # 记录之前的$var1.
  10       # 这个"while"循环中有4个条件, 但是只有最后一个能控制循环.
  11       # 退出状态由第4个条件决定.
  12 do
  13 echo "Input variable #1 (end to exit) "
  14   read var1
  15   echo "variable #1 = $var1"
  16 done 
  17
  18 # 尝试理解这个脚本的运行过程.
  19 # 这里还是有点小技巧的.
  20
  21 exit 0
 

--------------------------------------------------------------------------------
与for循环一样,while循环也可通过(())来使用C风格语法.(见例子 9-30).
--------------------------------------------------------------------------------

例子 10-17. C风格的while循环

   1 #!/bin/bash
   2 # wh-loopc.sh: 循环10次的while循环.
   3
   4 LIMIT=10
   5 a=1
   6
   7 while [ "$a" -le $LIMIT ]
   8 do
   9   echo -n "$a "
  10   let "a+=1"
  11 done           # 到目前为止都没什么令人惊奇的地方.
  12
  13 echo; echo
  14
  15 # +=================================================================+
  16
  17 # 现在, 重复C风格的语法.
  18
  19 ((a = 1))      # a=1
  20 # 双圆括号允许赋值两边的空格,就像C语言一样.
  21
  22 while (( a <= LIMIT ))   # 双圆括号, 变量前边没有"$".
  23 do
  24   echo -n "$a "
  25   ((a += 1))   # let "a+=1"
  26   # Yes, 看到了吧.
  27   # 双圆括号允许像C风格的语法一样增加变量的值.
  28 done
  29
  30 echo
  31
  32 # 现在,C程序员可以在Bash中找到回家的感觉了吧.
  33
  34 exit 0
 --------------------------------------------------------------------------------

 while循环的stdin可以用<来重定向到文件.

whild循环的stdin支持管道.
 

until
这个结构在循环的顶部判断条件,并且如果条件一直为false那就一直循环下去.(与while相反).


until [condition-is-true]
do
  command...
done


注意: until循环的判断在循环的顶部,这与某些编程语言是不同的.

与for循环一样,如果想把do和条件放在一行里,就使用";".


until [condition-is-true] ; do

--------------------------------------------------------------------------------

例子 10-18. until循环

   1 #!/bin/bash
   2
   3 END_CONDITION=end
   4
   5 until [ "$var1" = "$END_CONDITION" ]
   6 # 在循环的顶部判断条件.
   7 do
   8   echo "Input variable #1 "
   9   echo "($END_CONDITION to exit)"
  10   read var1
  11   echo "variable #1 = $var1"
  12   echo
  13 done 
  14
  15 exit 0

上一篇:10.1. 循环(1) 下一篇:10.2. 嵌套循环

power by soyo123 2007-2008