Advanced Bash-Scripting Guide:
Prev | Chapter 36. Miscellany | Next

# 36.4. Recursion: a script calling itself

Can a script recursively call itself? Indeed.

Example 36-8. A (useless) script that recursively calls itself

    #!/bin/bash
    # recurse.sh

    #  Can a script recursively call itself?
    #  Yes, but is this of any practical use?
    #  (See the following.)

    RANGE=10
    MAXVAL=9

    i=$RANDOM
    let "i %= $RANGE"  # Generate a random number between 0 and $RANGE - 1.

    if [ "$i" -lt "$MAXVAL" ]
    then
      echo "i = $i"
      ./$0             #  Script recursively spawns a new instance of itself.
    fi                 #  Each child script does the same, until
                       #+ a generated $i equals $MAXVAL.

    #  Using a "while" loop instead of an "if/then" test causes problems.
    #  Explain why.

    exit 0

    # Note:
    # ----
    # This script must have execute permission for it to work properly.
    # This is the case even if it is invoked by an "sh" command.
    # Explain why.

Example 36-9. A (useful) script that recursively calls itself

    #!/bin/bash
    # pb.sh: phone book

    # Written by Rick Boivie, and used with permission.
    # Modifications by ABS Guide author.

    MINARGS=1     #  Script needs at least one argument.
    DATAFILE=./phonebook
                  #  A data file in current working directory
                  #+ named "phonebook" must exist.
    PROGNAME=$0
    E_NOARGS=70   #  No arguments error.

    if [ $# -lt $MINARGS ]; then
          echo "Usage: "$PROGNAME" data-to-look-up"
          exit $E_NOARGS
    fi

    if [ $# -eq $MINARGS ]; then
          grep $1 "$DATAFILE"
          # 'grep' prints an error message if $DATAFILE not present.
    else
          ( shift; "$PROGNAME" $* ) | grep $1
          # Script recursively calls itself.
    fi

    exit 0        #  Script exits here.
                  #  Therefore, it's o.k. to put
                  #+ non-hashmarked comments and data after this point.

    # ------------------------------------------------------------------------
    Sample "phonebook" datafile:

    John Doe        1555 Main St., Baltimore, MD 21228          (410) 222-3333
    Mary Moe        9899 Jones Blvd., Warren, NH 03787          (603) 898-3232
    Richard Roe     856 E. 7th St., New York, NY 10009          (212) 333-4567
    Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678
    Zoe Zenobia     4481 N. Baker St., San Francisco, SF 94338  (415) 501-1631
    # ------------------------------------------------------------------------

    $bash pb.sh Roe
    Richard Roe     856 E. 7th St., New York, NY 10009          (212) 333-4567
    Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678

    $bash pb.sh Roe Sam
    Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678

    #  When more than one argument is passed to this script,
    #+ it prints *only* the line(s) containing all the arguments.

Example 36-10. Another (useful) script that recursively calls itself

    #!/bin/bash
    # usrmnt.sh, written by Anthony Richardson
    # Used with permission.

    # usage:       usrmnt.sh
    # description: mount device, invoking user must be listed in the
    #              MNTUSERS group in the /etc/sudoers file.

    # ----------------------------------------------------------
    #  This is a usermount script that reruns itself using sudo.
    #  A user with the proper permissions only has to type

    #   usermount /dev/fd0 /mnt/floppy

    # instead of

    #   sudo usermount /dev/fd0 /mnt/floppy

    #  I use this same technique for all of my
    #+ sudo scripts, because I find it convenient.
    # ----------------------------------------------------------

    #  If SUDO_COMMAND variable is not set we are not being run through
    #+ sudo, so rerun ourselves. Pass the user's real and group id . . .

    if [ -z "$SUDO_COMMAND" ]
    then
       mntusr=$(id -u) grpusr=$(id -g) sudo $0 $*
       exit 0
    fi

    # We will only get here if we are being run by sudo.
    /bin/mount $* -o uid=$mntusr,gid=$grpusr

    exit 0

    # Additional notes (from the author of this script):
    # -------------------------------------------------

    # 1) Linux allows the "users" option in the /etc/fstab
    #    file so that any user can mount removable media.
    #    But, on a server, I like to allow only a few
    #    individuals access to removable media.
    #    I find using sudo gives me more control.

    # 2) I also find sudo to be more convenient than
    #    accomplishing this task through groups.

    # 3) This method gives anyone with proper permissions
    #    root access to the mount command, so be careful
    #    about who you allow access.
    #    You can get finer control over which access can be mounted
    #    by using this same technique in separate mntfloppy, mntcdrom,
    #    and mntsamba scripts.

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

Prev | Home | Next
Tests and Comparisons: Alternatives | Up | "Colorizing" Scripts