34.4. Recursion

Can a script recursively call itself? Indeed.


Example 34-6. A (useless) script that recursively calls itself

   1 #!/bin/bash
   2 # recurse.sh
   3 
   4 #  Can a script recursively call itself?
   5 #  Yes, but is this of any practical use?
   6 #  (See the following.)
   7 
   8 RANGE=10
   9 MAXVAL=9
  10 
  11 i=$RANDOM
  12 let "i %= $RANGE"  # Generate a random number between 0 and $MAXVAL.
  13 
  14 if [ "$i" -lt "$MAXVAL" ]
  15 then
  16   echo "i = $i"
  17   ./$0             #  Script recursively spawns a new instance of itself.
  18 fi                 #  Each child script does the same, until
  19                    #+ a generated $i equals $MAXVAL.
  20 
  21 #  Using a "while" loop instead of an "if/then" test causes problems.
  22 #  Explain why.
  23 
  24 exit 0


Example 34-7. A (useful) script that recursively calls itself

   1 #!/bin/bash
   2 # pb.sh: phone book
   3 
   4 # Written by Rick Boivie, and used with permission.
   5 # Modifications by document author.
   6 
   7 MINARGS=1     # Script needs at least one argument.
   8 DATAFILE=./phonebook
   9 PROGNAME=$0
  10 E_NOARGS=70   # No arguments error.
  11 
  12 if [ $# -lt $MINARGS ]; then
  13       echo "Usage: "$PROGNAME" data"
  14       exit $E_NOARGS
  15 fi      
  16 
  17 
  18 if [ $# -eq $MINARGS ]; then
  19       grep $1 "$DATAFILE"
  20 else
  21       ( shift; "$PROGNAME" $* ) | grep $1
  22       # Script recursively calls itself.
  23 fi
  24 
  25 exit 0        #  Script exits here.
  26               #  It's o.k. to put non-hashmarked comments
  27               #+ and data after this point.
  28 
  29 # ------------------------------------------------------------------------
  30 # Sample "phonebook" datafile:
  31 
  32 John Doe        1555 Main St., Baltimore, MD 21228          (410) 222-3333
  33 Mary Moe        9899 Jones Blvd., Warren, NH 03787          (603) 898-3232
  34 Richard Roe     856 E. 7th St., New York, NY 10009          (212) 333-4567
  35 Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678
  36 Zoe Zenobia     4481 N. Baker St., San Francisco, SF 94338  (415) 501-1631
  37 # ------------------------------------------------------------------------
  38 
  39 $bash pb.sh Roe
  40 Richard Roe     856 E. 7th St., New York, NY 10009          (212) 333-4567
  41 Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678
  42 
  43 $bash pb.sh Roe Sam
  44 Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678
  45 
  46 #  When more than one argument passed to script,
  47 #+ prints *only* the line(s) containing all the arguments.


Example 34-8. Another (useful) script that recursively calls itself

   1 #!/bin/bash
   2 # usrmnt.sh, written by Anthony Richardson
   3 # Used with permission.
   4 
   5 # usage:       usrmnt.sh
   6 # description: mount device, invoking user must be listed in the
   7 #              MNTUSERS group in the /etc/sudoers file.
   8 
   9 # -----------------------------------------------------------------
  10 #  This is a usermount script that reruns
  11 #+ itself using sudo.  A user with the proper
  12 #+ permissions only has to type
  13 
  14 #   usermount /dev/fd0 /mnt/floppy
  15 
  16 # instead of
  17 
  18 #   sudo usermount /dev/fd0 /mnt/floppy
  19 
  20 #  I use this same technique for all of my
  21 #+ sudo scripts, because I find it convenient.
  22 # -----------------------------------------------------------------
  23 
  24 #  If SUDO_COMMAND variable is not set we are not being run through
  25 #+ sudo, so rerun ourselves. Pass the user's real and group id . . .
  26 
  27 if [ -z "$SUDO_COMMAND" ]
  28 then
  29    mntusr=$(id -u) grpusr=$(id -g) sudo $0 $*
  30    exit 0
  31 fi
  32 
  33 # We will only get here if we are being run by sudo
  34 /bin/mount $* -o uid=$mntusr,gid=$grpusr
  35 
  36 exit 0
  37 
  38 # Additional notes (from the author of this script): 
  39 # -------------------------------------------------
  40 
  41 # 1) Linux allows the users option in the /etc/fstab
  42 #    file so that any user can mount removable media.
  43 #    But, on a server, I like to allow only a few
  44 #    individuals access to removable media.  I find
  45 #    using sudo gives me more control.
  46 
  47 # 2) I also find sudo to be more convenient than
  48 #    accomplishing this task through groups.
  49 
  50 # 3) This method gives anyone with proper permissions
  51 #    root access to the mount command, so be careful
  52 #    about who you allow access.  You can get finer
  53 #    control over which access can be mounted by using this
  54 #    same technique in separate mntfloppy, mntcdrom,
  55 #    and mntsamba scripts.
  56 

Caution

Too many levels of recursion can exhaust the script's stack space, causing a segfault.