3.9. Internal Commands and Builtins

A builtin is a command contained in the bash tool set, literally built in.

getopts

This powerful tool parses command line arguments passed to the script. This is the bash analog of the getopt library function familiar to C programmers. It permits passing and concatenating multiple flags[1] and options to a script (for example scriptname -abc -e /usr/local).

The getopts construct uses two implicit variables. $OPTIND is the argument pointer (OPTion INDex) and $OPTARG (OPTion ARGument) the (optional) argument attached to a flag. A colon following the flag name in the declaration tags that flag as having an option.

A getopts construct usually comes packaged in a while loop, which processes the flags and options one at a time, then decrements the implicit $OPTIND variable to step to the next.

Note:

  1. The arguments must be passed from the command line to the script preceded by a minus (-) or a plus (+), else getopts will not process them, and will, in fact, terminate option processing at the first argument encountered lacking these modifiers.

  2. The getopts template differs slightly from the standard while loop, in that it lacks condition brackets.

  3. The getopts construct replaces the obsolete getopt command.

while getopts ":abcde:fg" Option
# Initial declaration.
# a, b, c, d, e, f, and g are the flags expected.
# The : after flag 'e' shows it will have an option passed with it.
do
  case $Option in
    a ) # Do something with variable 'a'.
    b ) # Do something with variable 'b'.
    ...
    e)  # Do something with 'e', and also with $OPTARG,
        # which is the associated argument passed with 'e'.
    ...
    g ) # Do something with variable 'g'.
  esac
done
shift $(($OPTIND - 1))
# Move argument pointer to next.

# All this is not nearly as complicated as it looks <grin>.
	      

Example 3-35. Using getopts to read the flags/options passed to a script

#!/bin/bash

# 'getopts' processes command line args to script.

# Usage: scriptname -options
# Note: dash (-) necessary

# Try invoking this script with
# 'scriptname -mn'
# 'scriptname -oq qOption'
# (qOption can be some arbitrary string.)

OPTERROR=33

if [ -z $1 ]
# Exit and complain if no argument(s) given.
then
  echo "Usage: `basename $0` options (-mnopqrs)"
  exit $OPTERROR
fi  

while getopts ":mnopq:rs" Option
do
  case $Option in
    m     ) echo "Scenario #1: option -m-";;
    n | o ) echo "Scenario #2: option -$Option-";;
    p     ) echo "Scenario #3: option -p-";;
    q     ) echo "Scenario #4: option -q-, with argument \"$OPTARG\"";;
    # Note that option 'q' must have an additional argument,
    # otherwise nothing happens.
    r | s ) echo "Scenario #5: option -$Option-"'';;
    *     ) echo "Unimplemented option chosen.";;
  esac
done

shift $(($OPTIND - 1))
# Decrements the argument pointer
# so it points to next argument.

exit 0
exit

Unconditionally terminates a script. The exit command may optionally take an integer argument, which is returned to the shell as the exit status of the script. It is a good practice to end all but the simplest scripts with an exit 0, indicating a successful run.

set

The set command changes the value of internal script variables. One use for this is to toggle option flags which help determine the behavior of the script (see Section 3.25). Another application for it is to reset the positional parameters that a script sees as the result of a command (set `command`). The script can then parse the fields of the command output.

Example 3-36. Using set with positional parameters

#!/bin/bash

# script "set-test"

# Invoke this script with three command line parameters,
# for example, "./set-test one two three".

echo
echo "Positional parameters before  set \`uname -a\` :"
echo "Command-line argument #1 = $1"
echo "Command-line argument #2 = $2"
echo "Command-line argument #3 = $3"

echo

set `uname -a`
# Sets the positional parameters to the output
# of the command `uname -a`

echo "Positional parameters after  set \`uname -a\` :"
# $1, $2, $3, etc. reinitialized to result of `uname -a`
echo "Field #1 of 'uname -a' = $1"
echo "Field #2 of 'uname -a' = $2"
echo "Field #3 of 'uname -a' = $3"
echo

exit 0
unset

The unset command deletes an internal script variable. It is a way of negating a previous set. Note that this command does not affect positional parameters.

export

The export command makes available variables to all child processes of the running script or shell. Unfortunately, there is no way to export variables back to the parent process, to the process that called or invoked the script or shell. One important use of export command is in startup files, to initialize and make accessible environmental variables to subsequent user processes (see Section 3.21).

readonly

Same as declare -r, sets a variable as read-only, or, in effect, as a constant. Attempts to change the variable fail with an error message. This is the shell analog of the C language const type qualifier.

basename

Strips the path information from a file name, printing only the file name. The construction basename $0 lets the script know its name, that is, the name it was invoked by. This can be used for "usage" messages if, for example a script is called with missing arguments:
echo "Usage: `basename $0` arg1 arg2 ... argn"

dirname

Strips the basename from a file name, printing only the path information.

Note: basename and dirname can operate on any arbitrary string. The filename given as an argument does not need to refer to an existing file.

Example 3-37. basename and dirname

#!/bin/bash

a=/home/heraclius/daily-journal.txt

echo "Basename of /home/heraclius/daily-journal.txt = `basename $a`"
echo "Dirname of /home/heraclius/daily-journal.txt = `dirname $a`"

exit 0
read

"Reads" the value of a variable from stdin, that is, interactively fetches input from the keyboard. The -a option lets read get array variables (see Example 3-72).

Example 3-38. Variable assignment, using read

#!/bin/bash

echo -n "Enter the value of variable 'var1': "
# -n option to echo suppresses newline

read var1
# Note no '$' in front of var1, since it is being set.

echo "var1 = $var1"


# Note that a single 'read' statement can set multiple variables.

echo

echo -n "Enter the values of variables 'var2' and 'var3' (separated by a space or tab): "
read var2 var3
echo "var2 = $var2      var3 = $var3"
# If you input only one value, the other variable(s) will remain unset (null).

exit 0

The read command may also "read" its variable value from a file redirected to stdin (see Section 3.13). If the file contains more than one line, only the first line is assigned to the variable. If there is more than one parameter to the read, then each variable gets assigned a successive whitespace delineated string. Caution!

read var1 <data-file
echo "var1 = $var1"
# var1 set to the entire first line of the input file "data-file"

read var2 var3 <data-file
echo "var2 = $var2   var3 = $var3"
# Note inconsistent behavior of "read" here.
# 1) Rewinds back to the beginning of input file.
# 2) Each variable is now set to a corresponding string, separated by whitespace,
#    rather than to an entire line of text.
# 3) The final variable gets the remainder of the line.
# 4) If there are more variables to be set than whitespace-terminated strings
#    on the first line of the file, then the excess variable remain unset.

true

A command that returns a successful (zero) exit status, but does nothing else.

# Endless loop
while true
# alias for :
do
   operation-1
   operation-2
   ...
   operation-n
   # Need a way to break out of loop.
done

false

A command that returns an unsuccessful exit status, but does nothing else.

# Null loop
while false
do
   # The following code will not execute.
   operation-1
   operation-2
   ...
   operation-n
   # Nothing happens!
done   

factor

Factor an integer into prime factors.

bash$ factor 27417
27417: 3 13 19 37
	      

hash [cmds]

Record the path name of specified commands (in the shell hash table), so the shell or script will not need to search the $PATH on subsequent calls to those commands. When hash is called with no arguments, it simply lists the commands that have been hashed.

pwd

Print Working Directory. This gives the user's (or script's) current directory.

pushd, popd, dirs

This command set is a mechanism for bookmarking working directories, a means of moving back and forth through directories in an orderly manner. A pushdown stack is used to keep track of directory names. Options allow various manipulations of the directory stack.

pushd dir-name pushes the path dir-name onto the directory stack and simultaneously changes the current working directory to dir-name

popd removes (pops) the top directory path name off the directory stack and simultaneously changes the current working directory to that directory popped from the stack.

dirs lists the contents of the directory stack. A successful pushd or popd will automatically invoke dirs.

Scripts that require various changes to the current working directory without hard-coding the directory name changes can make good use of these commands. Note that the implicit DIRSTACK array variable, accessible from within a script, holds the contents of the directory stack.

Example 3-39. Changing the current working directory

#!/bin/bash

dir1=/usr/local
dir2=/var/spool

pushd $dir1
# Will do an automatic 'dirs'
# (list directory stack to stdout).
echo "Now in directory `pwd`."
# Uses back-quoted 'pwd'.
# Now, do some stuff in directory 'dir1'.
pushd $dir2
echo "Now in directory `pwd`."
# Now, do some stuff in directory 'dir2'.
echo "The top entry in the DIRSTACK array is $DIRSTACK."
popd
echo "Now back in directory `pwd`."
# Now, do some more stuff in directory 'dir1'.
popd
echo "Now back in original working directory `pwd`."

exit 0
source, . (dot command), dirs

This command, when invoked from the command line, executes a script. Within a script, a source file-name loads the file file-name. This is the shell scripting equivalent of a C/C++ #include directive. It is useful in situations when multiple scripts use a common data file or function library.

Example 3-40. "Including" a data file

#!/bin/bash

# Load a data file.
. data-file
# Same effect as "source data-file"

# Note that the file "data-file", given below
# must be present in working directory.

# Now, reference some data from that file.

echo "variable1 (from data-file) = $variable1"
echo "variable3 (from data-file) = $variable3"

let "sum = $variable2 + $variable4"
echo "Sum of variable2 + variable4 (from data-file) = $sum"
echo "message1 (from data-file) is \"$message1\""
# Note:                            escaped quotes

print_message This is the message-print function in the data-file.


exit 0

File data-file for Example 3-40, above. Must be present in same directory.

# This is a data file loaded by a script.
# Files of this type may contain variables, functions, etc.
# It may be loaded with a 'source' or '.' command by a shell script.

# Let's initialize some variables.

variable1=22
variable2=474
variable3=5
variable4=97

message1="Hello, how are you?"
message2="Enough for now. Goodbye."

print_message ()
{
# Echoes any message passed to it.

  if [ -z $1 ]
  then
    return 1
    # Error, if argument missing.
  fi

  echo

  until [ -z "$1" ]
  do
    # Step through arguments passed to function.
    echo -n "$1"
    # Echo args one at a time, suppressing line feeds.
    echo -n " "
    # Insert spaces between words.
    shift
    # Next one.
  done  

  echo

  return 0
}  

3.9.1. Job Control Commands

wait

Stop script execution until all jobs running in background have terminated, or until the job number specified as an option terminates. Sometimes used to prevent a script from exiting before a background job finishes executing (this would create a dreaded orphan process).

Example 3-41. Waiting for a process to finish before proceeding

#!/bin/bash

if [ -z $1 ]
then
  echo "Usage: `basename $0` find-string"
  exit 1
fi

echo "Updating 'locate' database..."
echo "This may take a while."
updatedb /usr &
# Must be run as root.

wait
# Don't run the rest of the script until 'updatedb' finished.
# You want the the database updated before looking up the file name.

locate $1

# Lacking the wait command, in the worse case scenario,
# the script would exit while 'updatedb' was still running,
# leaving it as an orphan process.

exit 0
suspend

This has the same effect as Control-Z, pausing a foreground job.

stop

This has the same effect as suspend, but for a background job.

disown

Remove job(s) from the shell's table of active jobs.

jobs

Lists the jobs running in the background, giving the job number. Not as useful as ps.

times

Gives statistics on the system time used in executing commands, in the following form:
0m0.020s 0m0.020s
This capability is of very limited value, since it is uncommon to profile and benchmark shell scripts.

kill

Forcibly terminate a process by sending it an appropriate terminate signal. Note that kill -l lists all the "signals". (See Section 3.24 for more detail on signals).

Notes

[1]

A flag is an argument that acts as a signal, switching script behaviors on or off.