33.4. 递归

脚本是否能 递归地 调用自己本身? 当然可以.


例子 33-8. 递归调用自己本身的(无用)脚本

   1 #!/bin/bash
   2 # recurse.sh
   3 
   4 #  脚本能否递归地调用自己?
   5 #  是的, 但这有什么实际的用处吗?
   6 #  (看下面的.)
   7 
   8 RANGE=10
   9 MAXVAL=9
  10 
  11 i=$RANDOM
  12 let "i %= $RANGE"  # 产生一个从 0 到 $RANGE - 1 之间的随机数.
  13 
  14 if [ "$i" -lt "$MAXVAL" ]
  15 then
  16   echo "i = $i"
  17   ./$0             #  脚本递归地调用再生成一个和自己一样的实例.
  18 fi                 #  每个子脚本做的事都一样,
  19                    #+ 直到产生的变量 $i 和变量 $MAXVAL 相等.
  20 
  21 #  用"while"循环代替"if/then"测试会引起错误.
  22 #  解释为什么会这样.
  23 
  24 exit 0
  25 
  26 # 注:
  27 # ----
  28 # 脚本要正确地工作必须有执行权限.
  29 # 这是指用"sh"命令来调用这个脚本而没有设置正确权限导致的问题.
  30 # 请解释原因.


例子 33-9. 递归调用自己本身的(有用)脚本

   1 #!/bin/bash
   2 # pb.sh: 电话本(phone book)
   3 
   4 # 由Rick Boivie编写,已得到使用许可.
   5 # 由ABS文档作者修改.
   6 
   7 MINARGS=1     #  脚本需要至少一个参数.
   8 DATAFILE=./phonebook
   9               #  在当前目录下名为"phonebook"的数据文件必须存在
  10               #
  11 PROGNAME=$0
  12 E_NOARGS=70   #  没有参数的错误值.
  13 
  14 if [ $# -lt $MINARGS ]; then
  15       echo "Usage: "$PROGNAME" data"
  16       exit $E_NOARGS
  17 fi      
  18 
  19 
  20 if [ $# -eq $MINARGS ]; then
  21       grep $1 "$DATAFILE"
  22       #  如果$DATAFILE文件不存在,'grep' 会打印一个错误信息.
  23 else
  24       ( shift; "$PROGNAME" $* ) | grep $1
  25       # 脚本递归调用本身.
  26 fi
  27 
  28 exit 0        #  脚本在这儿退出.
  29               #  因此Therefore, 从这行开始可以写没有#开头的的注释行
  30 	      #
  31 
  32 # ------------------------------------------------------------------------
  33 "phonebook"文件的例子:
  34 
  35 John Doe        1555 Main St., Baltimore, MD 21228          (410) 222-3333
  36 Mary Moe        9899 Jones Blvd., Warren, NH 03787          (603) 898-3232
  37 Richard Roe     856 E. 7th St., New York, NY 10009          (212) 333-4567
  38 Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678
  39 Zoe Zenobia     4481 N. Baker St., San Francisco, SF 94338  (415) 501-1631
  40 # ------------------------------------------------------------------------
  41 
  42 $bash pb.sh Roe
  43 Richard Roe     856 E. 7th St., New York, NY 10009          (212) 333-4567
  44 Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678
  45 
  46 $bash pb.sh Roe Sam
  47 Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678
  48 
  49 #  当超过一个参数传给这个脚本时,
  50 #+ 它只打印包含所有参数的行.


例子 33-10. 另一个递归调用自己本身的(有用)脚本

   1 #!/bin/bash
   2 # usrmnt.sh, 由Anthony Richardson编写
   3 # 得到允许在此使用.
   4 
   5 # usage:       usrmnt.sh
   6 # 描述: 挂载设备, 调用者必须列在/etc/sudoers文件的MNTUSERS组里
   7 #
   8 
   9 # ----------------------------------------------------------
  10 #  这是一个用户挂载设备的脚本,它用sudo来调用自己.
  11 #  只有拥有合适权限的用户才能用
  12 
  13 #   usermount /dev/fd0 /mnt/floppy
  14 
  15 # 来代替
  16 
  17 #   sudo usermount /dev/fd0 /mnt/floppy
  18 
  19 #  我使用相同的技术来处理我所有的sudo脚本, 
  20 #+ 因为我觉得它很方便.
  21 # ----------------------------------------------------------
  22 
  23 #  如果 SUDO_COMMAND 变量没有设置,我们不能通过sudo来运行脚本本身.
  24 #+ 传递用户的真实ID和组ID . . .
  25 
  26 if [ -z "$SUDO_COMMAND" ]
  27 then
  28    mntusr=$(id -u) grpusr=$(id -g) sudo $0 $*
  29    exit 0
  30 fi
  31 
  32 # 如果我们以sudo来调用运行,就会运行这儿.
  33 /bin/mount $* -o uid=$mntusr,gid=$grpusr
  34 
  35 exit 0
  36 
  37 # 附注 (由脚本作者加注): 
  38 # -------------------------------------------------
  39 
  40 # 1) Linux允许在/etc/fstab文件中使用"users"选项 
  41 #    以使任何用户能挂载可移动的介质.
  42 #    但是, 在一个服务器上, 
  43 #    我只想有限的几个用户可以存取可移动介质.
  44 #    我发现使用sudo可以有更多的控制.
  45 
  46 # 2) 我也发现sudo能通过组更方便地达成目的.
  47 # 
  48 
  49 # 3) 这个方法使给予任何想给合适权限的人使用mount命令
  50 #    所以要小心使用.
  51 #    你也可以开发类似的脚本mntfloppy, mntcdrom,和 mntsamba来使mount命令得到更好的控制
  52 # 
  53 # 
  54 # 

过多层次的递归调用会耗尽脚本的堆栈空间,会引起段错误.