Search     or:     and:
  Краткое описание
 W. R. Стивенс TCP 
 W. R. Стивенс IPC 
 K. Bauer 
 Gary V. Vaughan 
 Д Вилер 
 В. Сталлинг 
 Pramode C.E. 
 Steve Pate 
 William Gropp 
 С Бекман 
 Р Стивенс 
 Mendel Cooper 
 М Перри 
 C.S. Rodriguez 
 Robert Love 
 Daniel Bovet 
 Д Джеф 
 G. Kroah-Hartman 
 B. Hansen 
Последние статьи :
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
  SQL 30.07   
  JFS 10.06   
  B-trees 01.06   
TOP 20
 2.0-> Linux IP Networking...777 
 Go Web ...684 
 Stein-MacEachern-> Час...392 
 Steve Pate 3...353 
 Secure Programming for Li...344 
 Steve Pate 1...319 
 Mod_perl 1...308 
 Стивенс 9...300 
 Stein-MacEachern-> Час...297 
 Rodriguez 6...293 
 Mike Perry...282 
 Daniel Bovet 4...275 
 Gary V.Vaughan-> Makefile...265 
 Part 4...263 
 Gary V.Vaughan-> Libtool...260 
 Rubni-Corbet -> Глав...259 
 libpcap->Package capture...258 
 Advanced Bash Scripting G...250 
 Kamran Husain...250 
  01.03.2018 : 2428415 посещений


Can a script recursively call itself? Indeed.

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

 #  Can a script recursively call itself?
 #  Yes, but is this of any practical use?
 #  (See the following.)
 let "i %= $RANGE"  # Generate a random number between 0 and $RANGE - 1.
 if [ "$i" -lt "$MAXVAL" ]
   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 33-9. A (useful) script that recursively calls itself

 # phone book
 # Written by Rick Boivie, and used with permission.
 # Modifications by ABS Guide author.
 MINARGS=1     #  Script needs at least one argument.
               #  A data file in current working directory
               #+ named "phonebook" must exist.
 E_NOARGS=70   #  No arguments error.
 if [ $# -lt $MINARGS ]; then
       echo "Usage: "$PROGNAME" data"
       exit $E_NOARGS
 if [ $# -eq $MINARGS ]; then
       grep $1 "$DATAFILE"
       # 'grep' prints an error message if $DATAFILE not present.
       ( shift; "$PROGNAME" $* ) | grep $1
       # Script recursively calls itself.
 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 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 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 33-10. Another (useful) script that recursively calls itself

 #, written by Anthony Richardson
 # Used with permission.
 # usage:
 # 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" ]
    mntusr=$(id -u) grpusr=$(id -g) sudo $0 $*
    exit 0
 # 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.

Assorted Tips

  • To keep a record of which user scripts have run during a particular session or over a number of sessions, add the following lines to each script you want to keep track of. This will keep a continuing file record of the script names and invocation times.

    # Append (>>) following to end of each script tracked.
     whoami>> $SAVE_FILE    # User invoking the script.
     echo $0>> $SAVE_FILE   # Script name.
     date>> $SAVE_FILE      # Date and time.
     echo>> $SAVE_FILE      # Blank line as separator.
     #  Of course, SAVE_FILE defined and exported as environmental variable in ~/.bashrc
     #+ (something like ~/.scripts-run)

  • The >> operator appends lines to a file. What if you wish to prepend a line to an existing file, that is, to paste it in at the beginning?

     title="***This is the title line of data text file***"
     echo $title | cat - $file >$
     # "cat -" concatenates stdout to $file.
     #  End result is
     #+ to write a new file with $title appended at *beginning*.

    This is a simplified variant of the Example 17-13 script given earlier. And, of course, sed can also do this.

  • A shell script may act as an embedded command inside another shell script, a Tcl or wish script, or even a Makefile. It can be invoked as an external shell command in a C program using the system() call, i.e., system("script_name");.

  • Setting a variable to the contents of an embedded sed or awk script increases the readability of the surrounding shell wrapper. See Example A-1 and Example 11-18.

  • Put together files containing your favorite and most useful definitions and functions. As necessary, "include" one or more of these "library files" in scripts with either the dot (.) or source command.

     # ------ -------
     # Note:
     # No "#!" here.
     # No "live code" either.
     # Useful variable definitions
     ROOT_UID=0             # Root has $UID 0.
     E_NOTROOT=101          # Not root user error. 
     MAXRETVAL=255          # Maximum (positive) return value of a function.
     # Functions
     Usage ()               # "Usage:" message.
       if [ -z "$1" ]       # No arg passed.
       echo "Usage: `basename $0` "$msg""
     Check_if_root ()       # Check if root running script.
     {                      # From "" example.
       if [ "$UID" -ne "$ROOT_UID" ]
         echo "Must be root to run this script."
         exit $E_NOTROOT
     CreateTempfileName ()  # Creates a "unique" temp filename.
     {                      # From "" example.
       suffix=`eval date +%s`
     isalpha2 ()            # Tests whether *entire string* is alphabetic.
     {                      # From "" example.
       [ $# -eq 1 ] || return $FAILURE
       case $1 in
       *[!a-zA-Z]*|"") return $FAILURE;;
       *) return $SUCCESS;;
       esac                 # Thanks, S.C.
     abs ()                           # Absolute value.
     {                                # Caution: Max return value = 255.
       if [ -z "$1" ]                 # Need arg passed.
         return $E_ARGERR             # Obvious error value returned.
       if [ "$1" -ge 0 ]              # If non-negative,
       then                           #
         absval=$1                    # stays as-is.
       else                           # Otherwise,
         let "absval = (( 0 - $1 ))"  # change sign.
       return $absval
     tolower ()             #  Converts string(s) passed as argument(s)
     {                      #+ to lowercase.
       if [ -z "$1" ]       #  If no argument(s) passed,
       then                 #+ send error message
         echo "(null)"      #+ (C-style void-pointer error message)
         return             #+ and return from function.
       echo "$@" | tr A-Z a-z
       # Translate all passed arguments ($@).
     # Use command substitution to set a variable to function output.
     # For example:
     #    oldvar="A seT of miXed-caSe LEtTerS"
     #    newvar=`tolower "$oldvar"`
     #    echo "$newvar"    # a set of mixed-case letters
     # Exercise: Rewrite this function to change lowercase passed argument(s)
     #           to uppercase ... toupper()  [easy].

  • Use special-purpose comment headers to increase clarity and legibility in scripts.

    ## Caution.
     rm -rf *.zzy   ##  The "-rf" options to "rm" are very dangerous,
                    ##+ especially with wildcards.
     #+ Line continuation.
     #  This is line 1
     #+ of a multi-line comment,
     #+ and this is the final line.
     #* Note.
     #o List item.
     #> Another point of view.
     while [ "$var1" != "end" ]    #> while test "$var1" != "end"

  • A particularly clever use of if-test constructs is commenting out blocks of code.

     #  Try setting the above variable to some value
     #+ for an unpleasant surprise.
     if [ $COMMENT_BLOCK ]; then
     Comment block --
     This is a comment line.
     This is another comment line.
     This is yet another comment line.
     echo "This will not echo."
     Comment blocks are error-free! Whee!
     echo "No more comments, please."
     exit 0

    Compare this with using here documents to comment out code blocks.

  • Using the $? exit status variable, a script may test if a parameter contains only digits, so it can be treated as an integer.

     test "$1" -ne 0 -o "$1" -eq 0 2>/dev/null
     # An integer is either equal to 0 or not equal to 0.
     # 2>/dev/null suppresses error message.
     if [ $? -ne "$SUCCESS" ]
       echo "Usage: `basename $0` integer-input"
       exit $E_BADINPUT
     let "sum = $1 + 25"             # Would give error if $1 not integer.
     echo "Sum = $sum"
     # Any variable, not just a command line parameter, can be tested this way.
     exit 0

  • The 0 - 255 range for function return values is a severe limitation. Global variables and other workarounds are often problematic. An alternative method for a function to communicate a value back to the main body of the script is to have the function write to stdout (usually with echo) the "return value," and assign this to a variable. This is actually a variant of command substitution.

    Example 33-15. Return value trickery

     multiply ()                     # Multiplies params passed.
     {                               # Will accept a variable number of args.
       local product=1
       until [ -z "$1" ]             # Until uses up arguments passed...
         let "product *= $1"
       echo $product                 #  Will not echo to stdout,
     }                               #+ since this will be assigned to a variable.
     mult1=15383; mult2=25211
     val1=`multiply $mult1 $mult2`
     echo "$mult1 X $mult2 = $val1"
                                     # 387820813
     mult1=25; mult2=5; mult3=20
     val2=`multiply $mult1 $mult2 $mult3`
     echo "$mult1 X $mult2 X $mult3 = $val2"
                                     # 2500
     mult1=188; mult2=37; mult3=25; mult4=47
     val3=`multiply $mult1 $mult2 $mult3 $mult4`
     echo "$mult1 X $mult2 X $mult3 X $mult4 = $val3"
                                     # 8173300
     exit 0

    The same technique also works for alphanumeric strings. This means that a function can "return" a non-numeric value.

    capitalize_ichar ()          #  Capitalizes initial character
     {                            #+ of argument string(s) passed.
       string0="$@"               # Accepts multiple arguments.
       firstchar=${string0:0:1}   # First character.
       string1=${string0:1}       # Rest of string(s).
       FirstChar=`echo "$firstchar" | tr a-z A-Z`
                                  # Capitalize first character.
       echo "$FirstChar$string1"  # Output to stdout.
     newstring=`capitalize_ichar "every sentence should start with a capital letter."`
     echo "$newstring"          # Every sentence should start with a capital letter.

    It is even possible for a function to "return" multiple values with this method.

    Example 33-16. Even more return value trickery

     # A function may "return" more than one value.
     sum_and_product ()   # Calculates both sum and product of passed args.
       echo $(( $1 + $2 )) $(( $1 * $2 ))
     # Echoes to stdout each calculated value, separated by space.
     echo "Enter first number "
     read first
     echo "Enter second number "
     read second
     retval=`sum_and_product $first $second`      # Assigns output of function.
     sum=`echo "$retval" | awk '{print $1}'`      # Assigns first field.
     product=`echo "$retval" | awk '{print $2}'`  # Assigns second field.
     echo "$first + $second = $sum"
     echo "$first * $second = $product"
     exit 0
  • Next in our bag of trick are techniques for passing an array to a function, then "returning" an array back to the main body of the script.

    Passing an array involves loading the space-separated elements of the array into a variable with command substitution. Getting an array back as the "return value" from a function uses the previously mentioned strategem of echoing the array in the function, then invoking command substitution and the ( ... ) operator to assign it to an array.

    Example 33-17. Passing and returning arrays

     # Passing an array to a function and...
     #                   "returning" an array from a function
     Pass_Array ()
       local passed_array   # Local variable.
       passed_array=( `echo "$1"` )
       echo "${passed_array[@]}"
       #  List all the elements of the new array
       #+ declared and set within the function.
     original_array=( element1 element2 element3 element4 element5 )
     echo "original_array = ${original_array[@]}"
     #                      List all elements of original array.
     # This is the trick that permits passing an array to a function.
     # **********************************
     argument=`echo ${original_array[@]}`
     # **********************************
     #  Pack a variable
     #+ with all the space-separated elements of the original array.
     # Note that attempting to just pass the array itself will not work.
     # This is the trick that allows grabbing an array as a "return value".
     # *****************************************
     returned_array=( `Pass_Array "$argument"` )
     # *****************************************
     # Assign 'echoed' output of function to array variable.
     echo "returned_array = ${returned_array[@]}"
     echo "============================================================="
     #  Now, try it again,
     #+ attempting to access (list) the array from outside the function.
     Pass_Array "$argument"
     # The function itself lists the array, but...
     #+ accessing the array from outside the function is forbidden.
     echo "Passed array (within function) = ${passed_array[@]}"
     # NULL VALUE since this is a variable local to the function.
     exit 0

    For a more elaborate example of passing arrays to functions, see Example A-10.

  • Using the double parentheses construct, it is possible to use C-like syntax for setting and incrementing variables and in for and while loops. See Example 10-12 and Example 10-17.

  • Setting the path and umask at the beginning of a script makes it more "portable" -- more likely to run on a "foreign" machine whose user may have bollixed up the $PATH and umask.
     PATH=/bin:/usr/bin:/usr/local/bin ; export PATH
     umask 022   # Files that the script creates will have 755 permission.
     # Thanks to Ian D. Allen, for this tip.

  • A useful scripting technique is to repeatedly feed the output of a filter (by piping) back to the same filter, but with a different set of arguments and/or options. Especially suitable for this are tr and grep.

    # From "" example.
     wlist=`strings "$1" | tr A-Z a-z | tr '[:space:]' Z | \
     tr -cs '[:alpha:]' Z | tr -s '\173-\377' Z | tr Z ' '`

    Example 33-18. Fun with anagrams

     # Playing games with anagrams.
     # Find anagrams of...
     FILTER='.......'       # How many letters minimum?
     #       1234567
     anagram "$LETTERSET" | # Find all anagrams of the letterset...
     grep "$FILTER" |       # With at least 7 letters,
     grep '^is' |           # starting with 'is'
     grep -v 's$' |         # no plurals
     grep -v 'ed$'          # no past tense verbs
     # Possible to add many combinations of conditions and filters.
     #  Uses "anagram" utility
     #+ that is part of the author's "yawl" word list package.
     exit 0                 # End of code.
     bash$ sh
     #  Exercises:
     #  ---------
     #  Modify this script to take the LETTERSET as a command-line parameter.
     #  Parameterize the filters in lines 11 - 13 (as with $FILTER),
     #+ so that they can be specified by passing arguments to a function.
     #  For a slightly different approach to anagramming,
     #+ see the script.

    See also Example 27-3, Example 12-22, and Example A-9.

  • Use "anonymous here documents" to comment out blocks of code, to save having to individually comment out each line with a #. See Example 17-11.

  • Running a script on a machine that relies on a command that might not be installed is dangerous. Use whatis to avoid potential problems with this.

    CMD=command1                 # First choice.
     PlanB=command2               # Fallback option.
     command_test=$(whatis "$CMD" | grep 'nothing appropriate')
     #  If 'command1' not found on system , 'whatis' will return
     #+ "command1: nothing appropriate."
     #  A safer alternative is:
     #     command_test=$(whereis "$CMD" | grep \/)
     #  But then the sense of the following test would have to be reversed,
     #+ since the $command_test variable holds content only if
     #+ the $CMD exists on the system.
     #     (Thanks, bojster.)
     if [[ -z "$command_test" ]]  # Check whether command present.
       $CMD option1 option2       #  Run command1 with options.
     else                         #  Otherwise,
       $PlanB                     #+ run command2. 

  • An if-grep test may not return expected results in an error case, when text is output to stderr, rather that stdout.
    if ls -l nonexistent_filename | grep -q 'No such file or directory'
       then echo "File \"nonexistent_filename\" does not exist."

    Redirecting stderr to stdout fixes this.
    if ls -l nonexistent_filename 2>&1 | grep -q 'No such file or directory'
     #                             ^^^^
       then echo "File \"nonexistent_filename\" does not exist."
     # Thanks, Chris Martin, for pointing this out.

  • The run-parts command is handy for running a set of command scripts in sequence, particularly in combination with cron or at.

  • It would be nice to be able to invoke X-Windows widgets from a shell script. There happen to exist several packages that purport to do so, namely Xscript, Xmenu, and widtools. The first two of these no longer seem to be maintained. Fortunately, it is still possible to obtain widtools here.


    The widtools (widget tools) package requires the XForms library to be installed. Additionally, the Makefile needs some judicious editing before the package will build on a typical Linux system. Finally, three of the six widgets offered do not work (and, in fact, segfault).

    The dialog family of tools offers a method of calling "dialog" widgets from a shell script. The original dialog utility works in a text console, but its successors, gdialog, Xdialog, and kdialog use X-Windows-based widget sets.

    Example 33-19. Widgets invoked from a shell script

     # Using 'gdialog' widgets.
     # Must have 'gdialog' installed on your system to run this script.
     # Version 1.1 (corrected 04/05/05)
     # This script was inspired by the following article.
     #     "Scripting for X Productivity," by Marco Fioretti,
     #      LINUX JOURNAL, Issue 113, September 2003, pp. 86-9.
     # Thank you, all you good people at LJ.
     # Input error in dialog box.
     # Dimensions of display, input widgets.
     # Output file name (constructed out of script name).
     # Display this script in a text widget.
     gdialog --title "Displaying: $0" --textbox $0 $HEIGHT $WIDTH
     # Now, we'll try saving input in a file.
     echo -n "VARIABLE=" > $OUTFILE
     gdialog --title "User Input" --inputbox "Enter variable, please:" \
     if [ "$?" -eq 0 ]
     # It's good practice to check exit status.
       echo "Executed \"dialog box\" without errors."
       echo "Error(s) in \"dialog box\" execution."
             # Or, clicked on "Cancel", instead of "OK" button.
       rm $OUTFILE
       exit $E_INPUT
     # Now, we'll retrieve and display the saved variable.
     . $OUTFILE   # 'Source' the saved file.
     echo "The variable input in the \"input box\" was: "$VARIABLE""
     rm $OUTFILE  # Clean up by removing the temp file.
                  # Some applications may need to retain this file.
     exit $?

    Bash, version2

    The current version of Bash, the one you have running on your machine, is version 2.xx.y or 3.xx.y.
    bash$ echo $BASH_VERSION
    The version 2 update of the classic Bash scripting language added array variables, [1] string and parameter expansion, and a better method of indirect variable references, among other features.

    Example 34-1. String expansion

     # String expansion.
     # Introduced with version 2 of Bash.
     #  Strings of the form $'xxx'
     #+ have the standard escaped characters interpreted. 
     echo $'Ringing bell 3 times \a \a \a'
          # May only ring once with certain terminals.
     echo $'Three form feeds \f \f \f'
     echo $'10 newlines \n\n\n\n\n\n\n\n\n\n'
     echo $'\102\141\163\150'   # Bash
                                # Octal equivalent of characters.
     exit 0

    Example 34-2. Indirect variable references - the new way

     # Indirect variable referencing.
     # This has a few of the attributes of references in C++.
     echo "a = $a"           # Direct reference.
     echo "Now a = ${!a}"    # Indirect reference.
     # The ${!variable} notation is greatly superior to the old "eval var1=\$$var2"
     echo "t = ${!t}"                      # t = 24
     echo "Value of t changed to ${!t}"    # 387
     #  This is useful for referencing members of an array or table,
     #+ or for simulating a multi-dimensional array.
     #  An indexing option (analogous to pointer arithmetic)
     #+ would have been nice. Sigh.
     exit 0

    Example 34-3. Simple database application, using indirect variable referencing

     # Simple database application using indirect variable referencing.
     # ============================================================== #
     # Data
     B1723_value=470                                   # Ohms
     B1723_powerdissip=.25                             # Watts
     B1723_colorcode="yellow-violet-brown"             # Color bands
     B1723_loc=173                                     # Where they are
     B1723_inventory=78                                # How many
     # ============================================================== #
     PS3='Enter catalog number: '
     select catalog_number in "B1723" "B1724" "B1725"
       echo "Catalog number $catalog_number:"
       echo "There are ${!Inv} of [${!Val} ohm / ${!Pdissip} watt] resistors in stock."
       echo "These are located in bin # ${!Loc}."
       echo "Their color code is \"${!Ccode}\"."
     echo; echo
     # Exercises:
     # ---------
     # 1) Rewrite this script to read its data from an external file.
     # 2) Rewrite this script to use arrays,
     #+   rather than indirect variable referencing.
     #    Which method is more straightforward and intuitive?
     # Notes:
     # -----
     #  Shell scripts are inappropriate for anything except the most simple
     #+ database applications, and even then it involves workarounds and kludges.
     #  Much better is to use a language with native support for data structures,
     #+ such as C++ or Java (or even Perl).
     exit 0

    Example 34-4. Using arrays and other miscellaneous trickery to deal four random hands from a deck of cards

     # Cards:
     # Deals four random hands from a deck of cards.
     declare -a Deck
     declare -a Suits
     declare -a Cards
     #  It would have been easier to implement and more intuitive
     #+ with a single, 3-dimensional array.
     #  Perhaps a future version of Bash will support multidimensional arrays.
     initialize_Deck ()
     until [ "$i" -gt $UPPER_LIMIT ]
       Deck[i]=$UNPICKED   # Set each card of "Deck" as unpicked.
       let "i += 1"
     initialize_Suits ()
     Suits[0]=C #Clubs
     Suits[1]=D #Diamonds
     Suits[2]=H #Hearts
     Suits[3]=S #Spades
     initialize_Cards ()
     Cards=(2 3 4 5 6 7 8 9 10 J Q K A)
     # Alternate method of initializing an array.
     pick_a_card ()
     let "card_number %= $CARDS"
     if [ "${Deck[card_number]}" -eq $UNPICKED ]
       return $card_number
       return $DUPE_CARD
     parse_card ()
     let "suit_number = number / CARDS_IN_SUIT"
     echo -n "$suit-"
     let "card_no = number % CARDS_IN_SUIT"
     printf %-4s $Card
     # Print cards in neat columns.
     seed_random ()  # Seed random number generator.
     {               # What happens if you don't do this?
     seed=`eval date +%s`
     let "seed %= 32766"
     #  What are some other methods
     #+ of seeding the random number generator?
     deal_cards ()
     while [ "$cards_picked" -le $UPPER_LIMIT ]
       if [ "$t" -ne $DUPE_CARD ]
         parse_card $t
         # Change back to 1-based indexing (temporarily). Why?
         let "u %= $CARDS_IN_SUIT"
         if [ "$u" -eq 0 ]   # Nested if/then condition test.
         # Separate hands.
         let "cards_picked += 1"
     return 0
     # Structured programming:
     # Entire program logic modularized in functions.
     exit 0
     # Exercise 1:
     # Add comments to thoroughly document this script.
     # Exercise 2:
     # Add a routine (function) to print out each hand sorted in suits.
     # You may add other bells and whistles if you like.
     # Exercise 3:
     # Simplify and streamline the logic of the script.

    A Sample .bashrc File

    The ~/.bashrc file determines the behavior of interactive shells. A good look at this file can lead to a better understanding of Bash.

    Emmanuel Rouat contributed the following very elaborate .bashrc file, written for a Linux system. He welcomes reader feedback on it.

    Study the file carefully, and feel free to reuse code snippets and functions from it in your own .bashrc file or even in your scripts.

    Example K-1. Sample .bashrc file

     # PERSONAL $HOME/.bashrc FILE for bash-2.05a (or later)
     # Last modified: Tue Apr 15 20:32:34 CEST 2003
     # This file is read (normally) by interactive shells only.
     # Here is the place to define your aliases, functions and
     # other interactive features like your prompt.
     # This file was designed (originally) for Solaris but based 
     # on Redhat's default .bashrc file
     # --> Modified for Linux.
     # The majority of the code you'll find here is based on code found
     # on Usenet (or internet).
     # This bashrc file is a bit overcrowded - remember it is just
     # just an example. Tailor it to your needs
     # --> Comments added by HOWTO author.
     # --> And then edited again by ER :-)
     # Source global definitions (if any)
     if [ -f /etc/bashrc ]; then
             . /etc/bashrc   # --> Read /etc/bashrc, if present.
     # Automatic setting of $DISPLAY (if not set already)
     # This works for linux - your mileage may vary.... 
     # The problem is that different types of terminals give
     # different answers to 'who am i'......
     # I have not found a 'universal' method yet
     function get_xserver ()
         case $TERM in
     	xterm )
                 XSERVER=$(who am i | awk '{print $NF}' | tr -d ')''(' ) 
                 # Ane-Pieter Wieringa suggests the following alternative:
                 # I_AM=$(who am i)
                 # SERVER=${I_AM#*(}
                 # SERVER=${SERVER%*)}
     	aterm | rxvt)
      	# find some code that works here.....
     if [ -z ${DISPLAY:=""} ]; then
         if [[ -z ${XSERVER}  || ${XSERVER} == $(hostname) || ${XSERVER} == "unix" ]]; then 
     	DISPLAY=":0.0"		# Display on local host
     	DISPLAY=${XSERVER}:0.0	# Display on remote host
     export DISPLAY
     # Some settings
     ulimit -S -c 0		# Don't want any coredumps
     set -o notify
     set -o noclobber
     set -o ignoreeof
     set -o nounset
     #set -o xtrace          # useful for debuging
     # Enable options:
     shopt -s cdspell
     shopt -s cdable_vars
     shopt -s checkhash
     shopt -s checkwinsize
     shopt -s mailwarn
     shopt -s sourcepath
     shopt -s no_empty_cmd_completion  # bash>=2.04 only
     shopt -s cmdhist
     shopt -s histappend histreedit histverify
     shopt -s extglob	# necessary for programmable completion
     # Disable options:
     shopt -u mailwarn
     unset MAILCHECK		# I don't want my shell to warn me of incoming mail
     export TIMEFORMAT=$'\nreal %3R\tuser %3U\tsys %3S\tpcpu %P\n'
     export HISTIGNORE="&:bg:fg:ll:h"
     export HOSTFILE=$HOME/.hosts	# Put a list of remote hosts in ~/.hosts
     # Greeting, motd etc...
     # Define some colors first:
     NC='\e[0m'              # No Color
     # --> Nice. Has the same effect as using "ansi.sys" in DOS.
     # Looks best on a black background.....
     echo -e "${CYAN}This is BASH ${RED}${BASH_VERSION%.*}${CYAN} - DISPLAY on ${RED}$DISPLAY${NC}\n"
     if [ -x /usr/games/fortune ]; then
         /usr/games/fortune -s     # makes our day a bit more fun.... :-)
     function _exit()	# function to run upon exit of shell
         echo -e "${RED}Hasta la vista, baby${NC}"
     trap _exit EXIT
     # Shell Prompt
     if [[ "${DISPLAY#$HOST}" != ":0.0" &&  "${DISPLAY}" != ":0" ]]; then  
         HILIT=${red}   # remote machine: prompt will be partly red
         HILIT=${cyan}  # local machine: prompt will be partly cyan
     #  --> Replace instances of \W with \w in prompt functions below
     #+ --> to get display of full path name.
     function fastprompt()
         unset PROMPT_COMMAND
         case $TERM in
             *term | rxvt )
                 PS1="${HILIT}[\h]$NC \W > \[\033]0;\${TERM} [\u@\h] \w\007\]" ;;
     	linux )
     	    PS1="${HILIT}[\h]$NC \W > " ;;
                 PS1="[\h] \W > " ;;
     function powerprompt()
             LOAD=$(uptime|sed -e "s/.*: \([^,]*\).*/\1/" -e "s/ //g")
         case $TERM in
             *term | rxvt  )
                 PS1="${HILIT}[\A \$LOAD]$NC\n[\h \#] \W > \[\033]0;\${TERM} [\u@\h] \w\007\]" ;;
             linux )
                 PS1="${HILIT}[\A - \$LOAD]$NC\n[\h \#] \w > " ;;
             * )
                 PS1="[\A - \$LOAD]\n[\h \#] \w > " ;;
     powerprompt     # this is the default prompt - might be slow
                     # If too slow, use fastprompt instead....
     # Arguably, some functions defined here are quite big
     # (ie 'lowercase') but my workstation has 512Meg of RAM, so .....
     # If you want to make this file smaller, these functions can
     # be converted into scripts.
     # Many functions were taken (almost) straight from the bash-2.04
     # examples.
     # Personnal Aliases
     alias rm='rm -i'
     alias cp='cp -i'
     alias mv='mv -i'
     # -> Prevents accidentally clobbering files.
     alias mkdir='mkdir -p'
     alias h='history'
     alias j='jobs -l'
     alias r='rlogin'
     alias which='type -all'
     alias ..='cd ..'
     alias path='echo -e ${PATH//:/\\n}'
     alias print='/usr/bin/lp -o nobanner -d $LPDEST'   # Assumes LPDEST is defined
     alias pjet='enscript -h -G -fCourier9 -d $LPDEST'  # Pretty-print using enscript
     alias background='xv -root -quit -max -rmode 5'    # Put a picture in the background
     alias du='du -kh'
     alias df='df -kTh'
     # The 'ls' family (this assumes you use the GNU ls)
     alias la='ls -Al'               # show hidden files
     alias ls='ls -hF --color'	# add colors for filetype recognition
     alias lx='ls -lXB'              # sort by extension
     alias lk='ls -lSr'              # sort by size
     alias lc='ls -lcr'		# sort by change time  
     alias lu='ls -lur'		# sort by access time   
     alias lr='ls -lR'               # recursive ls
     alias lt='ls -ltr'              # sort by date
     alias lm='ls -al |more'         # pipe through 'more'
     alias tree='tree -Csu'		# nice alternative to 'ls'
     # tailoring 'less'
     alias more='less'
     export PAGER=less
     export LESSCHARSET='latin1'
     export LESSOPEN='|/usr/bin/ %s 2>&-' # Use this if exists
     export LESS='-i -N -w  -z-4 -g -e -M -X -F -R -P%t?f%f \
     :stdin .?pb%pb\%:?lbLine %lb:?bbByte %bb:-...'
     # spelling typos - highly personnal :-)
     alias xs='cd'
     alias vf='cd'
     alias moer='more'
     alias moew='more'
     alias kk='ll'
     # a few fun ones
     function xtitle ()
         case "$TERM" in
             *term | rxvt)
                 echo -n -e "\033]0;$*\007" ;;
     # aliases...
     alias top='xtitle Processes on $HOST && top'
     alias make='xtitle Making $(basename $PWD) ; make'
     alias ncftp="xtitle ncFTP ; ncftp"
     # .. and functions
     function man ()
         for i ; do
     	xtitle The $(basename $1|tr -d .[:digit:]) manual
     	command man -F -a "$i"
     function ll(){ ls -l "$@"| egrep "^d" ; ls -lXB "$@" 2>&-| egrep -v "^d|total "; }
     function te()  # wrapper around xemacs/gnuserv
         if [ "$(gnuclient -batch -eval t 2>&-)" == "t" ]; then
             gnuclient -q "$@";
             ( xemacs "$@" &);
     # File & strings related functions:
     # Find a file with a pattern in name:
     function ff() { find . -type f -iname '*'$*'*' -ls ; }
     # Find a file with pattern $1 in name and Execute $2 on it:
     function fe() { find . -type f -iname '*'$1'*' -exec "${2:-file}" {} \;  ; }
     # find pattern in a set of filesand highlight them:
     function fstr()
         local case=""
         local usage="fstr: find string in files.
     Usage: fstr [-i] \"pattern\" [\"filename pattern\"] "
         while getopts :it opt
             case "$opt" in
             i) case="-i " ;;
             *) echo "$usage"; return;;
         shift $(( $OPTIND - 1 ))
         if [ "$#" -lt 1 ]; then
             echo "$usage"
         local SMSO=$(tput smso)
         local RMSO=$(tput rmso)
         find . -type f -name "${2:-*}" -print0 | xargs -0 grep -sn ${case} "$1" 2>&- | \
     sed "s/$1/${SMSO}\0${RMSO}/gI" | more
     function cuttail() # cut last n lines in file, 10 by default
         sed -n -e :a -e "1,${nlines}!{P;N;D;};N;ba" $1
     function lowercase()  # move filenames to lowercase
         for file ; do
             case "$filename" in
             */*) dirname==${file%/*} ;;
             *) dirname=.;;
             nf=$(echo $filename | tr A-Z a-z)
             if [ "$nf" != "$filename" ]; then
                 mv "$file" "$newname"
                 echo "lowercase: $file --> $newname"
                 echo "lowercase: $file not changed."
     function swap()         # swap 2 filenames around
         local TMPFILE=tmp.$$
         mv "$1" $TMPFILE
         mv "$2" "$1"
         mv $TMPFILE "$2"
     # Process/system related functions:
     function my_ps() { ps $@ -u $USER -o pid,%cpu,%mem,bsdtime,command ; }
     function pp() { my_ps f | awk '!/awk/ && $0~var' var=${1:-".*"} ; }
     # This function is roughly the same as 'killall' on linux
     # but has no equivalent (that I know of) on Solaris
     function killps()   # kill by process name
         local pid pname sig="-TERM"   # default signal
         if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then
             echo "Usage: killps [-SIGNAL] pattern"
         if [ $# = 2 ]; then sig=$1 ; fi
         for pid in $(my_ps| awk '!/awk/ && $0~pat { print $1 }' pat=${!#} ) ; do
             pname=$(my_ps | awk '$1~var { print $5 }' var=$pid )
             if ask "Kill process $pid <$pname> with signal $sig?"
                 then kill $sig $pid
     function my_ip() # get IP adresses
         MY_IP=$(/sbin/ifconfig ppp0 | awk '/inet/ { print $2 } ' | sed -e s/addr://)
         MY_ISP=$(/sbin/ifconfig ppp0 | awk '/P-t-P/ { print $3 } ' | sed -e s/P-t-P://)
     function ii()   # get current host related info
         echo -e "\nYou are logged on ${RED}$HOST"
         echo -e "\nAdditionnal information:$NC " ; uname -a
         echo -e "\n${RED}Users logged on:$NC " ; w -h
         echo -e "\n${RED}Current date :$NC " ; date
         echo -e "\n${RED}Machine stats :$NC " ; uptime
         echo -e "\n${RED}Memory stats :$NC " ; free
         my_ip 2>&- ;
         echo -e "\n${RED}Local IP Address :$NC" ; echo ${MY_IP:-"Not connected"}
         echo -e "\n${RED}ISP Address :$NC" ; echo ${MY_ISP:-"Not connected"}
     # Misc utilities:
     function repeat()       # repeat n times command
         local i max
         max=$1; shift;
         for ((i=1; i <= max ; i++)); do  # --> C-like syntax
             eval "$@";
     function ask()
         echo -n "$@" '[y/n] ' ; read ans
         case "$ans" in
             y*|Y*) return 0 ;;
             *) return 1 ;;
     # Most are taken from the bash 2.05 documentation and from Ian McDonalds
     # 'Bash completion' package (
     # You will in fact need bash-2.05a for some features
     if [ "${BASH_VERSION%.*}" \< "2.05" ]; then
         echo "You will need to upgrade to version 2.05 for programmable completion"
     shopt -s extglob        # necessary
     set +o nounset          # otherwise some completions will fail
     complete -A hostname   rsh rcp telnet rlogin r ftp ping disk
     complete -A export     printenv
     complete -A variable   export local readonly unset
     complete -A enabled    builtin
     complete -A alias      alias unalias
     complete -A function   function
     complete -A user       su mail finger
     complete -A helptopic  help     # currently same as builtins
     complete -A shopt      shopt
     complete -A stopped -P '%' bg
     complete -A job -P '%'     fg jobs disown
     complete -A directory  mkdir rmdir
     complete -A directory   -o default cd
     # Compression
     complete -f -o default -X '*.+(zip|ZIP)'  zip
     complete -f -o default -X '!*.+(zip|ZIP)' unzip
     complete -f -o default -X '*.+(z|Z)'      compress
     complete -f -o default -X '!*.+(z|Z)'     uncompress
     complete -f -o default -X '*.+(gz|GZ)'    gzip
     complete -f -o default -X '!*.+(gz|GZ)'   gunzip
     complete -f -o default -X '*.+(bz2|BZ2)'  bzip2
     complete -f -o default -X '!*.+(bz2|BZ2)' bunzip2
     # Postscript,pdf,dvi.....
     complete -f -o default -X '!*.ps'  gs ghostview ps2pdf ps2ascii
     complete -f -o default -X '!*.dvi' dvips dvipdf xdvi dviselect dvitype
     complete -f -o default -X '!*.pdf' acroread pdf2ps
     complete -f -o default -X '!*.+(pdf|ps)' gv
     complete -f -o default -X '!*.texi*' makeinfo texi2dvi texi2html texi2pdf
     complete -f -o default -X '!*.tex' tex latex slitex
     complete -f -o default -X '!*.lyx' lyx
     complete -f -o default -X '!*.+(htm*|HTM*)' lynx html2ps
     # Multimedia
     complete -f -o default -X '!*.+(jp*g|gif|xpm|png|bmp)' xv gimp
     complete -f -o default -X '!*.+(mp3|MP3)' mpg123 mpg321
     complete -f -o default -X '!*.+(ogg|OGG)' ogg123
     complete -f -o default -X '!*.pl'  perl perl5
     # This is a 'universal' completion function - it works when commands have
     # a so-called 'long options' mode , ie: 'ls --all' instead of 'ls -a'
     _get_longopts () 
         $1 --help | sed  -e '/--/!d' -e 's/.*--\([^[:space:].,]*\).*/--\1/'| \
     grep ^"$2" |sort -u ;
     _longopts_func ()
         case "${2:-*}" in
     	-*)	;;
     	*)	return ;;
         case "$1" in
     	\~*)	eval cmd="$1" ;;
     	*)	cmd="$1" ;;
         COMPREPLY=( $(_get_longopts ${1} ${2} ) )
     complete  -o default -F _longopts_func configure bash
     complete  -o default -F _longopts_func wget id info a2ps ls recode
     _make_targets ()
         local mdef makef gcmd cur prev i
         # if prev argument is -f, return possible filename completions.
         # we could be a little smarter here and return matches against
         # `makefile Makefile *.mk', whatever exists
         case "$prev" in
             -*f)    COMPREPLY=( $(compgen -f $cur ) ); return 0;;
         # if we want an option, return the possible posix options
         case "$cur" in
             -)      COMPREPLY=(-e -f -i -k -n -p -q -r -S -s -t); return 0;;
         # make reads `makefile' before `Makefile'
         if [ -f makefile ]; then
         elif [ -f Makefile ]; then
             mdef=*.mk               # local convention
         # before we scan for targets, see if a makefile name was specified
         # with -f
         for (( i=0; i < ${#COMP_WORDS[@]}; i++ )); do
             if [[ ${COMP_WORDS[i]} == -*f ]]; then
                 eval makef=${COMP_WORDS[i+1]}       # eval for tilde expansion
             [ -z "$makef" ] && makef=$mdef
         # if we have a partial word to complete, restrict completions to
         # matches of that word
         if [ -n "$2" ]; then gcmd='grep "^$2"' ; else gcmd=cat ; fi
         # if we don't want to use *.mk, we can take out the cat and use
         # test -f $makef and input redirection
         COMPREPLY=( $(cat $makef 2>/dev/null | awk 'BEGIN {FS=":"} /^[^.#   ][^=]*:/ {print $1}' | tr -s ' ' '\012' | sort -u | eval $gcmd ) )
     complete -F _make_targets -X '+($*|*.[cho])' make gmake pmake
     # cvs(1) completion
     _cvs ()
         local cur prev
         if [ $COMP_CWORD -eq 1 ] || [ "${prev:0:1}" = "-" ]; then
             COMPREPLY=( $( compgen -W 'add admin checkout commit diff \
             export history import log rdiff release remove rtag status \
             tag update' $cur ))
             COMPREPLY=( $( compgen -f $cur ))
         return 0
     complete -F _cvs cvs
     _killall ()
         local cur prev
         # get a list of processes (the first sed evaluation
         # takes care of swapped out processes, the second
         # takes care of getting the basename of the process)
         COMPREPLY=( $( /usr/bin/ps -u $USER -o comm  | \
             sed -e '1,1d' -e 's#[]\[]##g' -e 's#^.*/##'| \
             awk '{if ($0 ~ /^'$cur'/) print $0}' ))
         return 0
     complete -F _killall killall killps
     # A meta-command completion function for commands like sudo(8), which need to
     # first complete on a command, then complete according to that command's own
     # completion definition - currently not quite foolproof (e.g. mount and umount
     # don't work properly), but still quite useful - By Ian McDonald, modified by me.
         local cur func cline cspec
         if [ $COMP_CWORD = 1 ]; then
     	COMPREPLY=( $( compgen -c $cur ) )
         elif complete -p ${COMP_WORDS[1]} &>/dev/null; then
     	cspec=$( complete -p ${COMP_WORDS[1]} )
     	if [ "${cspec%%-F *}" != "${cspec}" ]; then
     	    # complete -F <function>
     	    # COMP_CWORD and COMP_WORDS() are not read-only,
     	    # so we can set them before handing off to regular
     	    # completion routine
     	    # set current token number to 1 less than now
     	    COMP_CWORD=$(( $COMP_CWORD - 1 ))
     	    # get function name
     	    func=${cspec#*-F }
     	    func=${func%% *}
     	    # get current command line minus initial command
     	    cline="${COMP_LINE#$1 }"
     	    # split current command line tokens into array
     		COMP_WORDS=( $cline )
     	    $func $cline
     	elif [ "${cspec#*-[abcdefgjkvu]}" != "" ]; then
     	    # complete -[abcdefgjkvu]
     	    #func=$( echo $cspec | sed -e 's/^.*\(-[abcdefgjkvu]\).*$/\1/' )
     	    func=$( echo $cspec | sed -e 's/^complete//' -e 's/[^ ]*$//' )
     	    COMPREPLY=( $( eval compgen $func $cur ) )
     	elif [ "${cspec#*-A}" != "$cspec" ]; then
     	    # complete -A <type>
     	    func=${cspec#*-A }
     	func=${func%% *}
     	COMPREPLY=( $( compgen -A $func $cur ) )
     	COMPREPLY=( $( compgen -f $cur ) )
     complete -o default -F _my_command nohup exec eval trace truss strace sotruss gdb
     complete -o default -F _my_command command type which man nice
     # Local Variables:
     # mode:shell-script
     # sh-shell:bash
     # End:
    Оставьте свой комментарий !

    Ваше имя:
    Оба поля являются обязательными

     Автор  Комментарий к данной статье