循环和分支

对代码块的操作是构造组织shell脚本的关键. 循环和分支结构为脚本编程提供了操作代码块的工具.

10.1. Loops

循环就是重复一些命令的代码块,如果条件不满足就退出循环.

for loops

for arg in [list]

这是一个基本的循环结构.它与C的for结构有很大不同.

forarg in [list]
do
   command(s)...
done

在循环的每次执行中,arg将顺序的存取list中列出的变量..

1 for arg  in "$var1" "$var2" "$var3" ...  "$varN"

2 # 在第1次循环中, arg =  $var1

3 # 在第2次循环中, arg =  $var2

4 # 在第3次循环中, arg =  $var3

5 # ...

6 # 在第N次循环中, arg =  $varN

7

8 # 在[list]中的参数加上双引号是为了防止单词被不合理地分割.

list中的参数允许包含通配符.

如果do和for想在同一行出现,那么在它们之间需要添加一个";".

forarg in [list] ; do


例子 10-1. 循环的一个简单例子

1 #!/bin/bash

2 # 列出所有的行星名称.

3

4 for  planet in Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto

5 do

6   echo $planet  # 每个行星被单独打印在一行上.

7 done

8

9 echo

10

11 for  planet in "Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune  Pluto"

12 # 所有的行星名称打印在同一行上.

13 # 整个'list'只有一个变量,用""封成一个变量.

14 do

15   echo $planet

16 done

17

18 exit 0


每个[list]中的元素都可能包含多个参数.在处理参数组时,这是非常有用的.在这种情况下,使用set命令(见例子  11-15)来强制解析每个[list]中的元素,并且分配每个解析出来的部分到一个位置参数中.


例子 10-2. 每个[list]元素带两个参数的for循环

1 #!/bin/bash

2 # 再访行星.

3

4 # 分配行星的名字和它距太阳的距离.

5

6 for  planet in "Mercury 36" "Venus 67" "Earth  93"  "Mars 142"  "Jupiter 483"

7 do

8   set -- $planet  # Parses variable "planet" and  sets positional parameters.

9   # "--" 将防止$planet为空,或者是以一个破折号开头.

10

11   # 可能需要保存原始的位置参数,因为它们被覆盖了.

12   # 一种方法就是使用数组,

13   #         original_params=("$@")

14

15   echo "$1                                                                                               $2,000,000 miles from the sun"

16   #-------two   tabs---把后边的0和$2连接起来

17 done

18

19 #  (Thanks, S.C., for additional clarification.)

20

21 exit 0


可以在for循环中的[list]位置放入一个变量.


例子 10-3. 文件信息: 对包含在变量中的文件列表进行操作

1 #!/bin/bash

2 #  fileinfo.sh

3

4 FILES="/usr/sbin/accept

5 /usr/sbin/pwck

6 /usr/sbin/chroot

7 /usr/bin/fakefile

8 /sbin/badblocks

9 /sbin/ypbind"     # 你关心的文件列表.

10                   # 扔进去一个假文件, /usr/bin/fakefile.

11

12 echo

13

14 for  file in $FILES

15 do

16

17   if [ ! -e "$file" ]       # 检查文件是否存在.

18   then

19     echo "$file does not exist.";  echo

20                                                                                     continue                # 继续下一个.

21    fi

22

23   ls -l $file | awk '{ print $9 "         file size: " $5 }'  # 打印2个域.

24   whatis `basename $file`   # 文件信息.

25   # 注意whatis数据库需要提前建立好.

26   # 要想达到这个目的, 以root身份运行/usr/bin/makewhatis.

27   echo

28 done

29

30 exit 0


如果在for循环的[list]中有通配符(*和?),那将会产生文件名扩展,也就是文件名扩展(globbing).


例子 10-4. for循环中操作文件

1 #!/bin/bash

2 #  list-

glob.sh:  在for循环中使用文件名扩展产生 [list]

3

4 echo

5

6 for  file in *

7 #           ^   在表达式中识别文件扩展符时,

8 #+             Bash 将执行文件名扩展.

9 do

10   ls -l "$file"  # Lists all files in $PWD (current  directory).

11   #  回想一下,通配符"*"能够匹配所有文件,

12   #+ 然而,在"文件扩展符"中,是不能匹配"."文件的.

13

14   #  如果没匹配到任何文件,那它将扩展成自己

15   #  为了不让这种情况发生,那就设置nullglob选项

16   #+    (shopt -s nullglob).

17   #   Thanks, S.C.

18 done

19

20 echo;  echo

21

22 for  file in [jx]*

23 do

24   rm -f $file    # 只删除当前目录下以"j"或"x"开头的文件.

25   echo "Removed file  \"$file\"".

26 done

27

28 echo

29

30 exit 0


在一个for循环中忽略in [list]部分的话,将会使循环操作$@(从命令行传递给脚本的参数列表).一个非常好的例子,见例子A-16.


例子 10-5. for循环中省略in [list]

1 #!/bin/bash

2

3 #  使用两种方法来调用这个脚本,一种是带参数的情况,另一种不带参数.

4 #+ 观察此脚本的行为各是什么样的?

5

6 for a

7 do

8  echo  -n "$a "

9 done

10

11 #  没有[list],所以循环将操作'$@'

12 #+ (包括空白的命令参数列表).

13

14 echo

15

16 exit 0


也可以使用命令替换(command substitution)来产生for循环的[list].具体见例子12-49, 例子10-10 和例子12-43.


例子10-6. 使用命令替换来产生for循环的[list]

1 #!/bin/bash

2 #  for-loopcmd.sh: 带[list]的for循环

3 #+  [list]是由命令替换产生的.

4

5 NUMBERS="9 7 3 8 37.53"

6

7 for  number in `echo $NUMBERS`  # for number  in 9 7 3 8 37.53

8 do

9   echo -n "$number "

10 done

11

12 echo

13 exit 0


下边是一个用命令替换来产生[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. 列出目录中所有的符号连接(symboliclinks)

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为退出码[exitstatus]).与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

转自 shell在线中文手册

http://manual.51yip.com/shell/

转载于:https://blog.51cto.com/1362336072/1883295

shell循环和分支相关推荐

  1. shell高级编程笔记(第十章 循环和分支)

    第十章 循环和分支 对代码块进行操作是有组织的结构化的shell脚本的关键.为了达到这个目的,循环和分支提供帮助. 10.1 循环 循环就是重复一些命令的代码块,如果条件不满足就退出循环. for 循 ...

  2. Sed教程(二):基本语法、循环、分支

    sed使用简单,我们可以提供sed命令直接在命令行或具有sed命令的文本文件的形式.本教程讲解调用sed的例子,有这两种方法: Sed 命令行 以下是我们可以指定单引号在命令行sed命令的格式如下: ...

  3. YTU 2412: 帮警长数一数【循环、分支简单综合】

    2412: 帮警长数一数[循环.分支简单综合] 时间限制: 1 Sec  内存限制: 64 MB 提交: 323  解决: 169 题目描述 黑猫警长在犯罪现场发现了一些血迹,现已经委托检验机构确定了 ...

  4. 【汇编语言与计算机系统结构笔记14】循环和分支程序设计

    本次笔记内容: 18.循环程序设计-1 19.分支程序设计 注:我找到了对应内容的课件,请见我于GitHub的CS笔记仓库.因此,为了节省时间,我只记录老师上课强调的内容与对应ppt页码. 本节课对应 ...

  5. 【python第3课】顺序、循环、分支

    程序结构:三种结构:顺序.循环.分支 分支语句 1.主要讲分支结构: 1)基本语法 if 语句 2)条件表达式就是计算结果必须为布尔值的表达式为True执行if语句 3)条件表达式的冒号不可少 4)同 ...

  6. C/C++知识总结 四 循环与分支语句

    C/C++循环与分支语句 循环与分支语句的意义 关系运算符.逻辑运算符 for循环和嵌套for循环(基于范围for循环) while循环与do while循环 分支if语句.if else语句.if ...

  7. 顺序图-循环和分支-操作符的使用

    目录 目录 例子 表示循环和分支 1.alt和opt 2.loop 3.assert.consider.ignore (1)consider和assert操作符混用 (2)ignore和assert操 ...

  8. flac格式音频转wav|Linux 使用 Shell 循环遍历文件夹里的音频——简介

    FLAC (无损音频压缩编码)-百度百科简介 使用sox工具把 flac 格式转wav,命令如下: sox 1.flac 1.wav 如果你的 flac 通道数.采样率.量化进度和wav不一致的话,需 ...

  9. shell循环loop

    1. 循环 shell循环,不断执行某段程序,直到符合条件. 循环分为4种, while循环,until循环,for固定处理, for数值处理. 2. while循环 while循环是一直执行,直到条 ...

最新文章

  1. 使用eBPFBCC提取内核网络流量信息
  2. cv2.dnn读取模型报错
  3. 终极解决方式——找到占用U盘的程序
  4. python 飞机大战小游戏
  5. java 反射调用方法_java的反射机制,以及通过反射获取方法,变量等操作
  6. 牛客小白月赛9: div.2 A(线性筛)
  7. oracle asm 错误集
  8. xposed 修改手机定位
  9. 微信小程序中使用阿里(iconfont)字体图标
  10. win7下桌面IE快捷方式无法删除解决方法
  11. 利用批处理文件快速设置IP地址
  12. 74LVC2G14GW 封装 SOT363 栅极/逆变器芯片
  13. 重学Java设计模式-创建者模式-工厂方法模式
  14. NAS不够快?那就上SSD享受如火箭般的体验吧!
  15. qt 获取当前程序运行路径_Qt 程序获取程序所在路径、用户目录路径、临时文件夹等特殊路径的方法...
  16. Matlab中一球反弹的高度,matlab数学建模2乒乓球的弹跳和罗基斯帝模型.doc
  17. 程序员如何边工作边创业?
  18. npm使用国内淘宝镜像的方法(两种)
  19. 秒杀秘籍大曝光 教你以超低价买到正品货
  20. psp java gv_〖集中讨论楼〗 [PSP]Jジュエリック・ナイトメアJewelic Nightmare 集中讨论帖 第4页 单向版 下载 ダウンロード download 百度云 网盘...

热门文章

  1. 关于Tomcat双击startup.bat文件一闪而过问题
  2. uva 11491——Erasing and Winning
  3. 1346. 检查整数及其两倍数是否存在 golang
  4. C++起始(内联函数,宏的优缺点,const关键字,auto关键字(C++11)基于范围的for循环(C++11). 指针空值nullptr(C++11))
  5. 洛谷P1080-国王游戏-贪心+高精度
  6. Linux 环境 C语言 操作MySql 的接口范例
  7. (C++版)链表(三)——实现双向链表的创建、插入、删除等简单操作
  8. 【Linux系统编程学习】C库IO函数与系统IO函数的关系
  9. 【运筹与优化】单纯形法解线性规划问题(matlab实现)
  10. java开发属于后端吗,值得一读!