Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

Cooper

11.1. Job Control Commands

Certain of the following job control commands take a "job identifier" as an argument. See the table at end of the chapter.

jobs

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

Note

It is all too easy to confuse jobs and processes. Certain builtins, such as kill, disown, and wait accept either a job number or a process number as an argument. The fg, bg and jobs commands accept only a job number.

bash$ sleep 100 &
 [1] 1384
 
 bash $ jobs
 [1]+  Running                 sleep 100 &

"1" is the job number (jobs are maintained by the current shell), and "1384" is the process number (processes are maintained by the system). To kill this job/process, either a kill %1 or a kill 1384 works.

Thanks, S.C.

disown

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

fg, bg

The fg command switches a job running in the background into the foreground. The bg command restarts a suspended job, and runs it in the background. If no job number is specified, then the fg or bg command acts upon the currently running job.

wait

Stop script execution until all jobs running in background have terminated, or until the job number or process ID specified as an option terminates. Returns the exit status of waited-for command.

You may use the wait command to prevent a script from exiting before a background job finishes executing (this would create a dreaded orphan process).

Example 11-24. Waiting for a process to finish before proceeding

#!/bin/bash
 
 ROOT_UID=0   # Only users with $UID 0 have root privileges.
 E_NOTROOT=65
 E_NOPARAMS=66
 
 if [ "$UID" -ne "$ROOT_UID" ]
 then
   echo "Must be root to run this script."
   # "Run along kid, it's past your bedtime."
   exit $E_NOTROOT
 fi  
 
 if [ -z "$1" ]
 then
   echo "Usage: `basename $0` find-string"
   exit $E_NOPARAMS
 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
 
 #  Without 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

Optionally, wait can take a job identifier as an argument, for example, wait%1 or wait $PPID. See the job id table.

Tip

Within a script, running a command in the background with an ampersand (&) may cause the script to hang until ENTER is hit. This seems to occur with commands that write to stdout. It can be a major annoyance.
#!/bin/bash
 # test.sh		  
 
 ls -l &
 echo "Done."
bash$ ./test.sh
 Done.
  [bozo@localhost test-scripts]$ total 1
  -rwxr-xr-x    1 bozo     bozo           34 Oct 11 15:09 test.sh
  _
                

Placing a wait after the background command seems to remedy this.
#!/bin/bash
 # test.sh		  
 
 ls -l &
 echo "Done."
 wait
bash$ ./test.sh
 Done.
  [bozo@localhost test-scripts]$ total 1
  -rwxr-xr-x    1 bozo     bozo           34 Oct 11 15:09 test.sh
                
Redirecting the output of the command to a file or even to /dev/null also takes care of this problem.

suspend

This has a similar effect to Control-Z, but it suspends the shell (the shell's parent process should resume it at an appropriate time).

logout

Exit a login shell, optionally specifying an exit status.

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 (see Example 13-6).

Example 11-25. A script that kills itself

#!/bin/bash
 # self-destruct.sh
 
 kill $$  # Script kills its own process here.
          # Recall that "$$" is the script's PID.
 
 echo "This line will not echo."
 # Instead, the shell sends a "Terminated" message to stdout.
 
 exit 0
 
 #  After this script terminates prematurely,
 #+ what exit status does it return?
 #
 # sh self-destruct.sh
 # echo $?
 # 143
 #
 # 143 = 128 + 15
 #             TERM signal

Note

kill -l lists all the signals. A kill -9 is a "sure kill", which will usually terminate a process that stubbornly refuses to die with a plain kill. Sometimes, a kill -15 works. A "zombie process," that is, a child process that has terminated, but that the parent process has not (yet) killed, cannot be killed by a logged-on user -- you can't kill something that is already dead -- but init will generally clean it up sooner or later.

command

The command COMMAND directive disables aliases and functions for the command "COMMAND".

Note

This is one of three shell directives that effect script command processing. The others are builtin and enable.

builtin

Invoking builtin BUILTIN_COMMAND runs the command "BUILTIN_COMMAND" as a shell builtin, temporarily disabling both functions and external system commands with the same name.

enable

This either enables or disables a shell builtin command. As an example, enable -n kill disables the shell builtin kill, so that when Bash subsequently encounters kill, it invokes /bin/kill.

The -a option to enable lists all the shell builtins, indicating whether or not they are enabled. The -f filename option lets enable load a builtin as a shared library (DLL) module from a properly compiled object file. [1].

autoload

This is a port to Bash of the ksh autoloader. With autoload in place, a function with an "autoload" declaration will load from an external file at its first invocation. [2] This saves system resources.

Note that autoload is not a part of the core Bash installation. It needs to be loaded in with enable -f (see above).

Table 11-1. Job identifiers

NotationMeaning
%NJob number [N]
%SInvocation (command line) of job begins with string S
%?SInvocation (command line) of job contains within it string S
%%"current" job (last job stopped in foreground or started in background)
%+"current" job (last job stopped in foreground or started in background)
%-Last job
$!Last background process

Notes



Options

Options are settings that change shell and/or script behavior.

The set command enables options within a script. At the point in the script where you want the options to take effect, use set -o option-name or, in short form, set -option-abbrev. These two forms are equivalent.

[1]

The C source for a number of loadable builtins is typically found in the /usr/share/doc/bash-?.??/functions directory.

Note that the -f option to enable is not portable to all systems.

      #!/bin/bash
 
       set -o verbose
       # Echoes all commands before executing.
       

      #!/bin/bash
 
       set -v
       # Exact same effect as above.
       

Note

To disable an option within a script, use set +o option-name or set +option-abbrev.

      #!/bin/bash
 
       set -o verbose
       # Command echoing on.
       command
       ...
       command
 
       set +o verbose
       # Command echoing off.
       command
       # Not echoed.
 
 
       set -v
       # Command echoing on.
       command
       ...
       command
 
       set +v
       # Command echoing off.
       command
 
       exit 0
       

An alternate method of enabling options in a script is to specify them immediately following the #! script header.

      #!/bin/bash -x
       #
       # Body of script follows.
       

It is also possible to enable script options from the command line. Some options that will not work with set are available this way. Among these are -i, force script to run interactive.

bash -v script-name

bash -o verbose script-name

The following is a listing of some useful options. They may be specified in either abbreviated form (preceded by a single dash) or by complete name (preceded by a double dash or by -o).

Table 30-1. Bash options

AbbreviationNameEffect
-CnoclobberPrevent overwriting of files by redirection (may be overridden by >|)
-D(none)List double-quoted strings prefixed by $, but do not execute commands in script
-aallexportExport all defined variables
-bnotifyNotify when jobs running in background terminate (not of much use in a script)
-c ...(none)Read commands from ...
-eerrexitAbort script at first error, when a command exits with non-zero status (except in until or while loops, if-tests, list constructs)
-fnoglobFilename expansion (globbing) disabled
-iinteractiveScript runs in interactive mode
-nnoexecRead commands in script, but do not execute them (syntax check)
-o Option-Name(none)Invoke the Option-Name option
-o posixPOSIXChange the behavior of Bash, or invoked script, to conform to POSIX standard.
-pprivilegedScript runs as "suid" (caution!)
-rrestrictedScript runs in restricted mode (see Chapter 21).
-sstdinRead commands from stdin
-t(none)Exit after first command
-unounsetAttempt to use undefined variable outputs error message, and forces an exit
-vverbosePrint each command to stdout before executing it
-xxtraceSimilar to -v, but expands commands
-(none)End of options flag. All other arguments are positional parameters.
--(none)Unset positional parameters. If arguments given (-- arg1 arg2), positional parameters set to arguments.

Converting DOS Batch Files to Shell Scripts

Quite a number of programmers learned scripting on a PC running DOS. Even the crippled DOS batch file language allowed writing some fairly powerful scripts and applications, though they often required extensive kludges and workarounds. Occasionally, the need still arises to convert an old DOS batch file to a UNIX shell script. This is generally not difficult, as DOS batch file operators are only a limited subset of the equivalent shell scripting ones.

Table L-1. Batch file keywords / variables / operators, and their shell equivalents

Batch File OperatorShell Script EquivalentMeaning
%$command-line parameter prefix
/-command option flag
\/directory path separator
===(equal-to) string comparison test
!==!!=(not equal-to) string comparison test
||pipe
@set +vdo not echo current command
**filename "wild card"
>>file redirection (overwrite)
>>>>file redirection (append)
<<redirect stdin
%VAR%$VARenvironmental variable
REM#comment
NOT!negate following test
NUL/dev/null"black hole" for burying command output
ECHOechoecho (many more option in Bash)
ECHO.echoecho blank line
ECHO OFFset +vdo not echo command(s) following
FOR %%VAR IN (LIST) DOfor var in [list]; do"for" loop
:LABELnone (unnecessary)label
GOTOnone (use a function)jump to another location in the script
PAUSEsleeppause or wait an interval
CHOICEcase or selectmenu choice
IFifif-test
IF EXIST FILENAMEif [ -e filename ]test if file exists
IF !%N==!if [ -z "$N" ]if replaceable parameter "N" not present
CALLsource or . (dot operator)"include" another script
COMMAND /Csource or . (dot operator)"include" another script (same as CALL)
SETexportset an environmental variable
SHIFTshiftleft shift command-line argument list
SGN-lt or -gtsign (of integer)
ERRORLEVEL$?exit status
CONstdin"console" (stdin)
PRN/dev/lp0(generic) printer device
LPT1/dev/lp0first printer device
COM1/dev/ttyS0first serial port

Batch files usually contain DOS commands. These must be translated into their UNIX equivalents in order to convert a batch file into a shell script.

Table L-2. DOS commands and their UNIX equivalents

DOS CommandUNIX EquivalentEffect
ASSIGNlnlink file or directory
ATTRIBchmodchange file permissions
CDcdchange directory
CHDIRcdchange directory
CLSclearclear screen
COMPdiff, comm, cmpfile compare
COPYcpfile copy
Ctl-CCtl-Cbreak (signal)
Ctl-ZCtl-DEOF (end-of-file)
DELrmdelete file(s)
DELTREErm -rfdelete directory recursively
DIRls -ldirectory listing
ERASErmdelete file(s)
EXITexitexit current process
FCcomm, cmpfile compare
FINDgrepfind strings in files
MDmkdirmake directory
MKDIRmkdirmake directory
MOREmoretext file paging filter
MOVEmvmove
PATH$PATHpath to executables
RENmvrename (move)
RENAMEmvrename (move)
RDrmdirremove directory
RMDIRrmdirremove directory
SORTsortsort file
TIMEdatedisplay system time
TYPEcatoutput file to stdout
XCOPYcp(extended) file copy

Note

Virtually all UNIX and shell operators and commands have many more options and enhancements than their DOS and batch file equivalents. Many DOS batch files rely on auxiliary utilities, such as ask.com, a crippled counterpart to read.

DOS supports a very limited and incompatible subset of filename wildcard expansion, recognizing only the * and ? characters.

Converting a DOS batch file into a shell script is generally straightforward, and the result ofttimes reads better than the original.

Example L-1. VIEWDATA.BAT: DOS Batch File

REM VIEWDATA
 
 REM INSPIRED BY AN EXAMPLE IN "DOS POWERTOOLS"
 REM                           BY PAUL SOMERSON
 
 
 @ECHO OFF
 
 IF !%1==! GOTO VIEWDATA
 REM  IF NO COMMAND-LINE ARG...
 FIND "%1" C:\BOZO\BOOKLIST.TXT
 GOTO EXIT0
 REM  PRINT LINE WITH STRING MATCH, THEN EXIT.
 
 :VIEWDATA
 TYPE C:\BOZO\BOOKLIST.TXT | MORE
 REM  SHOW ENTIRE FILE, 1 PAGE AT A TIME.
 
 :EXIT0

The script conversion is somewhat of an improvement.

Example L-2. viewdata.sh: Shell Script Conversion of VIEWDATA.BAT

#!/bin/bash
 # viewdata.sh
 # Conversion of VIEWDATA.BAT to shell script.
 
 DATAFILE=/home/bozo/datafiles/book-collection.data
 ARGNO=1
 
 # @ECHO OFF                 Command unnecessary here.
 
 if [ $# -lt "$ARGNO" ]    # IF !%1==! GOTO VIEWDATA
 then
   less $DATAFILE          # TYPE C:\MYDIR\BOOKLIST.TXT | MORE
 else
   grep "$1" $DATAFILE     # FIND "%1" C:\MYDIR\BOOKLIST.TXT
 fi  
 
 exit 0                    # :EXIT0
 
 #  GOTOs, labels, smoke-and-mirrors, and flimflam unnecessary.
 #  The converted script is short, sweet, and clean,
 #+ which is more than can be said for the original.

Ted Davis' Shell Scripts on the PC site has a set of comprehensive tutorials on the old-fashioned art of batch file programming. Certain of his ingenious techniques could conceivably have relevance for shell scripts.

Starting Off With a Sha-Bang

Shell programming is a 1950s juke box . . .

Larry Wall
Table of Contents
2.1. Invoking the script
2.2. Preliminary Exercises

In the simplest case, a script is nothing more than a list of system commands stored in a file. At the very least, this saves the effort of retyping that particular sequence of commands each time it is invoked.

Example 2-1. cleanup: A script to clean up the log files in /var/log

# Cleanup
 # Run as root, of course.
 
 cd /var/log
 cat /dev/null > messages
 cat /dev/null > wtmp
 echo "Logs cleaned up."

There is nothing unusual here, only a set of commands that could just as easily be invoked one by one from the command line on the console or in an xterm. The advantages of placing the commands in a script go beyond not having to retype them time and again. The script becomes a tool, and can easily be modified or customized for a particular application.

Example 2-2. cleanup: An improved clean-up script

#!/bin/bash
 # Proper header for a Bash script.
 
 # Cleanup, version 2
 
 # Run as root, of course.
 # Insert code here to print error message and exit if not root.
 
 LOG_DIR=/var/log
 # Variables are better than hard-coded values.
 cd $LOG_DIR
 
 cat /dev/null > messages
 cat /dev/null > wtmp
 
 
 echo "Logs cleaned up."
 
 exit # The right and proper method of "exiting" from a script.

Now that's beginning to look like a real script. But we can go even farther . . .

Example 2-3. cleanup: An enhanced and generalized version of above scripts.

#!/bin/bash
 # Cleanup, version 3
 
 #  Warning:
 #  -------
 #  This script uses quite a number of features that will be explained
 #+ later on.
 #  By the time you've finished the first half of the book,
 #+ there should be nothing mysterious about it.
 
 
 
 LOG_DIR=/var/log
 ROOT_UID=0     # Only users with $UID 0 have root privileges.
 LINES=50       # Default number of lines saved.
 E_XCD=66       # Can't change directory?
 E_NOTROOT=67   # Non-root exit error.
 
 
 # Run as root, of course.
 if [ "$UID" -ne "$ROOT_UID" ]
 then
   echo "Must be root to run this script."
   exit $E_NOTROOT
 fi  
 
 if [ -n "$1" ]
 # Test if command line argument present (non-empty).
 then
   lines=$1
 else  
   lines=$LINES # Default, if not specified on command line.
 fi  
 
 
 #  Stephane Chazelas suggests the following,
 #+ as a better way of checking command line arguments,
 #+ but this is still a bit advanced for this stage of the tutorial.
 #
 #    E_WRONGARGS=65  # Non-numerical argument (bad arg format)
 #
 #    case "$1" in
 #    ""      ) lines=50;;
 #    *[!0-9]*) echo "Usage: `basename $0` file-to-cleanup"; exit $E_WRONGARGS;;
 #    *       ) lines=$1;;
 #    esac
 #
 #* Skip ahead to "Loops" chapter to decipher all this.
 
 
 cd $LOG_DIR
 
 if [ `pwd` != "$LOG_DIR" ]  # or   if [ "$PWD" != "$LOG_DIR" ]
                             # Not in /var/log?
 then
   echo "Can't change to $LOG_DIR."
   exit $E_XCD
 fi  # Doublecheck if in right directory, before messing with log file.
 
 # far more efficient is:
 #
 # cd /var/log || {
 #   echo "Cannot change to necessary directory." >&2
 #   exit $E_XCD;
 # }
 
 
 
 
 tail -$lines messages > mesg.temp # Saves last section of message log file.
 mv mesg.temp messages             # Becomes new log directory.
 
 
 # cat /dev/null > messages
 #* No longer needed, as the above method is safer.
 
 cat /dev/null > wtmp  #  ': > wtmp' and '> wtmp'  have the same effect.
 echo "Logs cleaned up."
 
 exit 0
 #  A zero return value from the script upon exit
 #+ indicates success to the shell.

Since you may not wish to wipe out the entire system log, this version of the script keeps the last section of the message log intact. You will constantly discover ways of refining previously written scripts for increased effectiveness.

The sha-bang ( #!) at the head of a script tells your system that this file is a set of commands to be fed to the command interpreter indicated. The #! is actually a two-byte [1] magic number, a special marker that designates a file type, or in this case an executable shell script (type man magic for more details on this fascinating topic). Immediately following the sha-bang is a path name. This is the path to the program that interprets the commands in the script, whether it be a shell, a programming language, or a utility. This command interpreter then executes the commands in the script, starting at the top (line following the sha-bang line), ignoring comments. [2]

#!/bin/sh
 #!/bin/bash
 #!/usr/bin/perl
 #!/usr/bin/tcl
 #!/bin/sed -f
 #!/usr/awk -f

Each of the above script header lines calls a different command interpreter, be it /bin/sh, the default shell (bash in a Linux system) or otherwise. [3] Using #!/bin/sh, the default Bourne shell in most commercial variants of UNIX, makes the script portable to non-Linux machines, though you sacrifice Bash-specific features. The script will, however, conform to the POSIX [4] sh standard.

Note that the path given at the "sha-bang" must be correct, otherwise an error message -- usually "Command not found" -- will be the only result of running the script.

#! can be omitted if the script consists only of a set of generic system commands, using no internal shell directives. The second example, above, requires the initial #!, since the variable assignment line, lines=50, uses a shell-specific construct. Note again that #!/bin/sh invokes the default shell interpreter, which defaults to /bin/bash on a Linux machine.

Tip

This tutorial encourages a modular approach to constructing a script. Make note of and collect "boilerplate" code snippets that might be useful in future scripts. Eventually you can build quite an extensive library of nifty routines. As an example, the following script prolog tests whether the script has been invoked with the correct number of parameters.

E_WRONG_ARGS=65
 script_parameters="-a -h -m -z"
 #                  -a = all, -h = help, etc.
 
 if [ $# -ne $Number_of_expected_args ]
 then
   echo "Usage: `basename $0` $script_parameters"
   # `basename $0` is the script's filename.
   exit $E_WRONG_ARGS
 fi

Many times, you will write a script that carries out one particular task. The first script in this chapter is an example of this. Later, it might occur to you to generalize the script to do other, similar tasks. Replacing the literal ("hard-wired") constants by variables is a step in that direction, as is replacing repetitive code blocks by functions.

Notes

[1]

Some flavors of UNIX (those based on 4.2BSD) take a four-byte magic number, requiring a blank after the ! -- #! /bin/sh.

[2]

The #! line in a shell script will be the first thing the command interpreter (sh or bash) sees. Since this line begins with a #, it will be correctly interpreted as a comment when the command interpreter finally executes the script. The line has already served its purpose - calling the command interpreter.

If, in fact, the script includes an extra #! line, then bash will interpret it as a comment.
#!/bin/bash
 
 echo "Part 1 of script."
 a=1
 
 #!/bin/bash
 # This does *not* launch a new script.
 
 echo "Part 2 of script."
 echo $a  # Value of $a stays at 1.

[3]

This allows some cute tricks.

#!/bin/rm
 # Self-deleting script.
 
 # Nothing much seems to happen when you run this... except that the file disappears.
 
 WHATEVER=65
 
 echo "This line will never print (betcha!)."
 
 exit $WHATEVER  # Doesn't matter. The script will not exit here.

Also, try starting a README file with a #!/bin/more, and making it executable. The result is a self-listing documentation file. (A here document using cat is possibly a better alternative -- see Example 17-3).

[4]

Portable Operating System Interface, an attempt to standardize UNIX-like OSes. The POSIX specifications are listed on the Open Group site.

Special Characters

Special Characters Found In Scripts and Elsewhere

#

Comments. Lines beginning with a # (with the exception of #!) are comments.

# This line is a comment.

Comments may also occur following the end of a command.

echo "A comment will follow." # Comment here.
 #                            ^ Note whitespace before #

Comments may also follow whitespace at the beginning of a line.

	# A tab precedes this comment.

Caution

A command may not follow a comment on the same line. There is no method of terminating the comment, in order for "live code" to begin on the same line. Use a new line for the next command.

Note

Of course, an escaped # in an echo statement does not begin a comment. Likewise, a # appears in certain parameter substitution constructs and in numerical constant expressions.
echo "The # here does not begin a comment."
 echo 'The # here does not begin a comment.'
 echo The \# here does not begin a comment.
 echo The # here begins a comment.
 
 echo ${PATH#*:}       # Parameter substitution, not a comment.
 echo $(( 2#101011 ))  # Base conversion, not a comment.
 
 # Thanks, S.C.
The standard quoting and escape characters (" ' \) escape the #.

Certain pattern matching operations also use the #.

;

Command separator [semicolon]. Permits putting two or more commands on the same line.

echo hello; echo there
 
 
 if [ -x "$filename" ]; then    # Note that "if" and "then" need separation.
                                # Why?
   echo "File $filename exists."; cp $filename $filename.bak
 else
   echo "File $filename not found."; touch $filename
 fi; echo "File test complete."

Note that the ";" sometimes needs to be escaped.

;;

Terminator in a case option [double semicolon].

case "$variable" in
 abc)  echo "\$variable = abc" ;;
 xyz)  echo "\$variable = xyz" ;;
 esac

.

"dot" command [period]. Equivalent to source (see Example 11-20). This is a bash builtin.

.

"dot", as a component of a filename. When working with filenames, a dot is the prefix of a "hidden" file, a file that an ls will not normally show.
bash$ touch .hidden-file
 bash$ ls -l	      
 total 10
  -rw-r--r--    1 bozo      4034 Jul 18 22:04 data1.addressbook
  -rw-r--r--    1 bozo      4602 May 25 13:58 data1.addressbook.bak
  -rw-r--r--    1 bozo       877 Dec 17  2000 employment.addressbook
 
 
 bash$ ls -al	      
 total 14
  drwxrwxr-x    2 bozo  bozo      1024 Aug 29 20:54 ./
  drwx------   52 bozo  bozo      3072 Aug 29 20:51 ../
  -rw-r--r--    1 bozo  bozo      4034 Jul 18 22:04 data1.addressbook
  -rw-r--r--    1 bozo  bozo      4602 May 25 13:58 data1.addressbook.bak
  -rw-r--r--    1 bozo  bozo       877 Dec 17  2000 employment.addressbook
  -rw-rw-r--    1 bozo  bozo         0 Aug 29 20:54 .hidden-file
 	        

When considering directory names, a single dot represents the current working directory, and two dots denote the parent directory.

bash$ pwd
 /home/bozo/projects
 
 bash$ cd .
 bash$ pwd
 /home/bozo/projects
 
 bash$ cd ..
 bash$ pwd
 /home/bozo/
 	        

The dot often appears as the destination (directory) of a file movement command.

bash$ cp /home/bozo/current_work/junk/* .
 	        

.

"dot" character match. When matching characters, as part of a regular expression, a "dot" matches a single character.

"

partial quoting [double quote]. "STRING" preserves (from interpretation) most of the special characters within STRING. See also Chapter 5.

'

full quoting [single quote]. 'STRING' preserves all special characters within STRING. This is a stronger form of quoting than using ". See also Chapter 5.

,

comma operator. The comma operator links together a series of arithmetic operations. All are evaluated, but only the last one is returned.
let "t2 = ((a = 9, 15 / 3))"  # Set "a = 9" and "t2 = 15 / 3"

\

escape [backslash]. A quoting mechanism for single characters.

\X "escapes" the character X. This has the effect of "quoting" X, equivalent to 'X'. The \ may be used to quote " and ', so they are expressed literally.

See Chapter 5 for an in-depth explanation of escaped characters.

/

Filename path separator [forward slash]. Separates the components of a filename (as in /home/bozo/projects/Makefile).

This is also the division arithmetic operator.

`

command substitution. The `command` construct makes available the output of command for assignment to a variable. This is also known as backquotes or backticks.

:

null command [colon]. This is the shell equivalent of a "NOP" (no op, a do-nothing operation). It may be considered a synonym for the shell builtin true. The ":" command is a itself a Bash builtin, and its exit status is "true" (0).

:
 echo $?   # 0

Endless loop:

while :
 do
    operation-1
    operation-2
    ...
    operation-n
 done
 
 # Same as:
 #    while true
 #    do
 #      ...
 #    done

Placeholder in if/then test:

if condition
 then :   # Do nothing and branch ahead
 else
    take-some-action
 fi

Provide a placeholder where a binary operation is expected, see Example 8-2 and default parameters.

: ${username=`whoami`}
 # ${username=`whoami`}   Gives an error without the leading :
 #                        unless "username" is a command or builtin...

Provide a placeholder where a command is expected in a here document. See Example 17-10.

Evaluate string of variables using parameter substitution (as in Example 9-14).
: ${HOSTNAME?} ${USER?} ${MAIL?}
 #  Prints error message
 #+ if one or more of essential environmental variables not set.

Variable expansion / substring replacement.

In combination with the > redirection operator, truncates a file to zero length, without changing its permissions. If the file did not previously exist, creates it.
: > data.xxx   # File "data.xxx" now empty.	      
 
 # Same effect as   cat /dev/null >data.xxx
 # However, this does not fork a new process, since ":" is a builtin.
See also Example 12-14.

In combination with the >> redirection operator, has no effect on a pre-existing target file (: >> target_file). If the file did not previously exist, creates it.

Note

This applies to regular files, not pipes, symlinks, and certain special files.

May be used to begin a comment line, although this is not recommended. Using # for a comment turns off error checking for the remainder of that line, so almost anything may be appear in a comment. However, this is not the case with :.
: This is a comment that generates an error, ( if [ $x -eq 3] ).

The ":" also serves as a field separator, in /etc/passwd, and in the $PATH variable.
bash$ echo $PATH
 /usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games

!

reverse (or negate) the sense of a test or exit status [bang]. The ! operator inverts the exit status of the command to which it is applied (see Example 6-2). It also inverts the meaning of a test operator. This can, for example, change the sense of "equal" ( = ) to "not-equal" ( != ). The ! operator is a Bash keyword.

In a different context, the ! also appears in indirect variable references.

In yet another context, from the command line, the ! invokes the Bash history mechanism (see Appendix J). Note that within a script, the history mechanism is disabled.

*

wild card [asterisk]. The * character serves as a "wild card" for filename expansion in globbing. By itself, it matches every filename in a given directory.

bash$ echo *
 abs-book.sgml add-drive.sh agram.sh alias.sh
 	      

The * also represents any number (or zero) characters in a regular expression.

*

arithmetic operator. In the context of arithmetic operations, the * denotes multiplication.

A double asterisk, **, is the exponentiation operator.

?

test operator. Within certain expressions, the ? indicates a test for a condition.

In a double parentheses construct, the ? serves as a C-style trinary operator. See Example 9-30.

In a parameter substitution expression, the ? tests whether a variable has been set.

?

wild card. The ? character serves as a single-character "wild card" for filename expansion in globbing, as well as representing one character in an extended regular expression.

$

Variable substitution (contents of a variable).
var1=5
 var2=23skidoo
 
 echo $var1     # 5
 echo $var2     # 23skidoo

A $ prefixing a variable name indicates the value the variable holds.

$

end-of-line. In a regular expression, a "$" addresses the end of a line of text.

${}
$*, $@
$?

exit status variable. The $? variable holds the exit status of a command, a function, or of the script itself.

$$

process ID variable. The $$ variable holds the process ID of the script in which it appears.

()

command group.
(a=hello; echo $a)

Important

A listing of commands within parentheses starts a subshell.

Variables inside parentheses, within the subshell, are not visible to the rest of the script. The parent process, the script, cannot read variables created in the child process, the subshell.
a=123
 ( a=321; )	      
 
 echo "a = $a"   # a = 123
 # "a" within parentheses acts like a local variable.

array initialization.
Array=(element1 element2 element3)

{xxx,yyy,zzz,...}

Brace expansion.
grep Linux file*.{txt,htm*}
 # Finds all instances of the word "Linux"
 # in the files "fileA.txt", "file2.txt", "fileR.html", "file-87.htm", etc.

A command may act upon a comma-separated list of file specs within braces. [1] Filename expansion (globbing) applies to the file specs between the braces.

Caution

No spaces allowed within the braces unless the spaces are quoted or escaped.

echo {file1,file2}\ :{\ A," B",' C'}

file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C

{}

Block of code [curly brackets]. Also referred to as an "inline group", this construct, in effect, creates an anonymous function. However, unlike a function, the variables in a code block remain visible to the remainder of the script.

bash$ { local a;
 	      a=123; }
 bash: local: can only be used in a
 function
 	      

a=123
 { a=321; }
 echo "a = $a"   # a = 321   (value inside code block)
 
 # Thanks, S.C.

The code block enclosed in braces may have I/O redirected to and from it.

Example 3-1. Code blocks and I/O redirection

#!/bin/bash
 # Reading lines in /etc/fstab.
 
 File=/etc/fstab
 
 {
 read line1
 read line2
 } < $File
 
 echo "First line in $File is:"
 echo "$line1"
 echo
 echo "Second line in $File is:"
 echo "$line2"
 
 exit 0
 
 # Now, how do you parse the separate fields of each line?
 # Hint: use awk.

Example 3-2. Saving the results of a code block to a file

#!/bin/bash
 # rpm-check.sh
 
 # Queries an rpm file for description, listing, and whether it can be installed.
 # Saves output to a file.
 # 
 # This script illustrates using a code block.
 
 SUCCESS=0
 E_NOARGS=65
 
 if [ -z "$1" ]
 then
   echo "Usage: `basename $0` rpm-file"
   exit $E_NOARGS
 fi  
 
 { 
   echo
   echo "Archive Description:"
   rpm -qpi $1       # Query description.
   echo
   echo "Archive Listing:"
   rpm -qpl $1       # Query listing.
   echo
   rpm -i --test $1  # Query whether rpm file can be installed.
   if [ "$?" -eq $SUCCESS ]
   then
     echo "$1 can be installed."
   else
     echo "$1 cannot be installed."
   fi  
   echo
 } > "$1.test"       # Redirects output of everything in block to file.
 
 echo "Results of rpm test in file $1.test"
 
 # See rpm man page for explanation of options.
 
 exit 0

Note

Unlike a command group within (parentheses), as above, a code block enclosed by {braces} will not normally launch a subshell. [2]

{} \;

pathname. Mostly used in find constructs. This is not a shell builtin.

Note

The ";" ends the -exec option of a find command sequence. It needs to be escaped to protect it from interpretation by the shell.

[ ]

test.

Test expression between [ ]. Note that [ is part of the shell builtin test (and a synonym for it), not a link to the external command /usr/bin/test.

[[ ]]

test.

Test expression between [[ ]] (shell keyword).

See the discussion on the [[ ... ]] construct.

[ ]

array element.

In the context of an array, brackets set off the numbering of each element of that array.
Array[1]=slot_1
 echo ${Array[1]}

[ ]

range of characters.

As part of a regular expression, brackets delineate a range of characters to match.

(( ))

integer expansion.

Expand and evaluate integer expression between (( )).

See the discussion on the (( ... )) construct.

> &> >& >> <

scriptname >filename redirects the output of scriptname to file filename. Overwrite filename if it already exists.

command &>filename redirects both the stdout and the stderr of command to filename.

command >&2 redirects stdout of command to stderr.

scriptname >>filename appends the output of scriptname to file filename. If filename does not already exist, it will be created.

(command)>

<(command)

In a different context, the "<" and ">" characters act as string comparison operators.

In yet another context, the "<" and ">" characters act as integer comparison operators. See also Example 12-9.

<<

redirection used in a here document.

<<<

redirection used in a here string.

<, >

ASCII comparison.
veg1=carrots
 veg2=tomatoes
 
 if [[ "$veg1" < "$veg2" ]]
 then
   echo "Although $veg1 precede $veg2 in the dictionary,"
   echo "this implies nothing about my culinary preferences."
 else
   echo "What kind of dictionary are you using, anyhow?"
 fi

\<, \>

bash$ grep '\<the\>' textfile

|

pipe. Passes the output of previous command to the input of the next one, or to the shell. This is a method of chaining commands together.

echo ls -l | sh
 #  Passes the output of "echo ls -l" to the shell,
 #+ with the same result as a simple "ls -l".
 
 
 cat *.lst | sort | uniq
 # Merges and sorts all ".lst" files, then deletes duplicate lines.

The output of a command or commands may be piped to a script.
#!/bin/bash
 # uppercase.sh : Changes input to uppercase.
 
 tr 'a-z' 'A-Z'
 #  Letter ranges must be quoted
 #+ to prevent filename generation from single-letter filenames.
 
 exit 0
Now, let us pipe the output of ls -l to this script.
bash$ ls -l | ./uppercase.sh
 -RW-RW-R--    1 BOZO  BOZO       109 APR  7 19:49 1.TXT
  -RW-RW-R--    1 BOZO  BOZO       109 APR 14 16:48 2.TXT
  -RW-R--R--    1 BOZO  BOZO       725 APR 20 20:56 DATA-FILE
 	      

Note

The stdout of each process in a pipe must be read as the stdin of the next. If this is not the case, the data stream will block, and the pipe will not behave as expected.
cat file1 file2 | ls -l | sort
 # The output from "cat file1 file2" disappears.

A pipe runs as a child process, and therefore cannot alter script variables.
variable="initial_value"
 echo "new_value" | read variable
 echo "variable = $variable"     # variable = initial_value

If one of the commands in the pipe aborts, this prematurely terminates execution of the pipe. Called a broken pipe, this condition sends a SIGPIPE signal.

>|

force redirection (even if the noclobber option is set). This will forcibly overwrite an existing file.

||

OR logical operator. In a test construct, the || operator causes a return of 0 (success) if either of the linked test conditions is true.

&

Run job in background. A command followed by an & will run in the background.

bash$ sleep 10 &
 [1] 850
 [1]+  Done                    sleep 10
 	      

Within a script, commands and even loops may run in the background.

Example 3-3. Running a loop in the background

#!/bin/bash
 # background-loop.sh
 
 for i in 1 2 3 4 5 6 7 8 9 10            # First loop.
 do
   echo -n "$i "
 done & # Run this loop in background.
        # Will sometimes execute after second loop.
 
 echo   # This 'echo' sometimes will not display.
 
 for i in 11 12 13 14 15 16 17 18 19 20   # Second loop.
 do
   echo -n "$i "
 done  
 
 echo   # This 'echo' sometimes will not display.
 
 # ======================================================
 
 # The expected output from the script:
 # 1 2 3 4 5 6 7 8 9 10 
 # 11 12 13 14 15 16 17 18 19 20 
 
 # Sometimes, though, you get:
 # 11 12 13 14 15 16 17 18 19 20 
 # 1 2 3 4 5 6 7 8 9 10 bozo $
 # (The second 'echo' doesn't execute. Why?)
 
 # Occasionally also:
 # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 # (The first 'echo' doesn't execute. Why?)
 
 # Very rarely something like:
 # 11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20 
 # The foreground loop preempts the background one.
 
 exit 0
 
 #  Nasimuddin Ansari suggests adding    sleep 1
 #+ after the   echo -n "$i"   in lines 6 and 14,
 #+ for some real fun.

Caution

A command run in the background within a script may cause the script to hang, waiting for a keystroke. Fortunately, there is a remedy for this.

&&

AND logical operator. In a test construct, the && operator causes a return of 0 (success) only if both the linked test conditions are true.

-

option, prefix. Option flag for a command or filter. Prefix for an operator.

COMMAND -[Option1][Option2][...]

ls -al

sort -dfu $filename

set -- $variable

if [ $file1 -ot $file2 ]
 then
   echo "File $file1 is older than $file2."
 fi
 
 if [ "$a" -eq "$b" ]
 then
   echo "$a is equal to $b."
 fi
 
 if [ "$c" -eq 24 -a "$d" -eq 47 ]
 then
   echo "$c equals 24 and $d equals 47."
 fi

-

redirection from/to stdin or stdout [dash].

(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
 # Move entire file tree from one directory to another
 # [courtesy Alan Cox <a.cox@swansea.ac.uk>, with a minor change]
 
 # 1) cd /source/directory    Source directory, where the files to be moved are.
 # 2) &&                     "And-list": if the 'cd' operation successful, then execute the next command.
 # 3) tar cf - .              The 'c' option 'tar' archiving command creates a new archive,
 #                            the 'f' (file) option, followed by '-' designates the target file as stdout,
 #                            and do it in current directory tree ('.').
 # 4) |                       Piped to...
 # 5) ( ... )                 a subshell
 # 6) cd /dest/directory      Change to the destination directory.
 # 7) &&                     "And-list", as above
 # 8) tar xpvf -              Unarchive ('x'), preserve ownership and file permissions ('p'),
 #                            and send verbose messages to stdout ('v'),
 #                            reading data from stdin ('f' followed by '-').
 #
 #                            Note that 'x' is a command, and 'p', 'v', 'f' are options.
 # Whew!
 
 
 
 # More elegant than, but equivalent to:
 #   cd source/directory
 #   tar cf - . | (cd ../dest/directory; tar xpvf -)
 #
 # cp -a /source/directory /dest/directory     also has same effect.

bunzip2 linux-2.4.3.tar.bz2 | tar xvf -
 # --uncompress tar file--    | --then pass it to "tar"--
 # If "tar" has not been patched to handle "bunzip2",
 # this needs to be done in two discrete steps, using a pipe.
 # The purpose of the exercise is to unarchive "bzipped" kernel source.

Note that in this context the "-" is not itself a Bash operator, but rather an option recognized by certain UNIX utilities that write to stdout, such as tar, cat, etc.

bash$ echo "whatever" | cat -
 whatever 

Where a filename is expected, - redirects output to stdout (sometimes seen with tar cf), or accepts input from stdin, rather than from a file. This is a method of using a file-oriented utility as a filter in a pipe.

bash$ file
 Usage: file [-bciknvzL] [-f namefile] [-m magicfiles] file...
 	      
By itself on the command line, file fails with an error message.

Add a "-" for a more useful result. This causes the shell to await user input.
bash$ file -
 abc
 standard input:              ASCII text
 
 
 
 bash$ file -
 #!/bin/bash
 standard input:              Bourne-Again shell script text executable
 	      
Now the command accepts input from stdin and analyzes it.

The "-" can be used to pipe stdout to other commands. This permits such stunts as prepending lines to a file.

Using diff to compare a file with a section of another:

grep Linux file1 | diff file2 -

Finally, a real-world example using - with tar.

Example 3-4. Backup of all files changed in last day

#!/bin/bash
 
 #  Backs up all files in current directory modified within last 24 hours
 #+ in a "tarball" (tarred and gzipped file).
 
 BACKUPFILE=backup-$(date +%m-%d-%Y)
 #                 Embeds date in backup filename.
 #                 Thanks, Joshua Tschida, for the idea.
 archive=${1:-$BACKUPFILE}
 #  If no backup-archive filename specified on command line,
 #+ it will default to "backup-MM-DD-YYYY.tar.gz."
 
 tar cvf - `find . -mtime -1 -type f -print` > $archive.tar
 gzip $archive.tar
 echo "Directory $PWD backed up in archive file \"$archive.tar.gz\"."
 
 
 #  Stephane Chazelas points out that the above code will fail
 #+ if there are too many files found
 #+ or if any filenames contain blank characters.
 
 # He suggests the following alternatives:
 # -------------------------------------------------------------------
 #   find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar"
 #      using the GNU version of "find".
 
 
 #   find . -mtime -1 -type f -exec tar rvf "$archive.tar" '{}' \;
 #         portable to other UNIX flavors, but much slower.
 # -------------------------------------------------------------------
 
 
 exit 0

Caution

Filenames beginning with "-" may cause problems when coupled with the "-" redirection operator. A script should check for this and add an appropriate prefix to such filenames, for example ./-FILENAME, $PWD/-FILENAME, or $PATHNAME/-FILENAME.

If the value of a variable begins with a -, this may likewise create problems.
var="-n"
 echo $var		
 # Has the effect of "echo -n", and outputs nothing.

-

previous working directory. A cd - command changes to the previous working directory. This uses the $OLDPWD environmental variable.

Caution

Do not confuse the "-" used in this sense with the "-" redirection operator just discussed. The interpretation of the "-" depends on the context in which it appears.

-

Minus. Minus sign in an arithmetic operation.

=

Equals. Assignment operator
a=28
 echo $a   # 28

In a different context, the "=" is a string comparison operator.

+

Plus. Addition arithmetic operator.

In a different context, the + is a Regular Expression operator.

+

Option. Option flag for a command or filter.

Certain commands and builtins use the + to enable certain options and the - to disable them.

%

modulo. Modulo (remainder of a division) arithmetic operation.

In a different context, the % is a pattern matching operator.

~

home directory [tilde]. This corresponds to the $HOME internal variable. ~bozo is bozo's home directory, and ls ~bozo lists the contents of it. ~/ is the current user's home directory, and ls ~/ lists the contents of it.
bash$ echo ~bozo
 /home/bozo
 
 bash$ echo ~
 /home/bozo
 
 bash$ echo ~/
 /home/bozo/
 
 bash$ echo ~:
 /home/bozo:
 
 bash$ echo ~nonexistent-user
 ~nonexistent-user
 	      

~+

current working directory. This corresponds to the $PWD internal variable.

~-

previous working directory. This corresponds to the $OLDPWD internal variable.

=~

regular expression match. This operator was introduced with version 3 of Bash.

^

beginning-of-line. In a regular expression, a "^" addresses the beginning of a line of text.

Control Characters

change the behavior of the terminal or text display. A control character is a CONTROL + key combination.

Control characters are not normally useful inside a script.

  • Ctl-B

    Backspace (nondestructive).

  • Ctl-C

    Break. Terminate a foreground job.

  • Ctl-D

    Log out from a shell (similar to exit).

    "EOF" (end of file). This also terminates input from stdin.

    When typing text on the console or in an xterm window, Ctl-D erases the character under the cursor. When there are no characters present, Ctl-D logs out of the session, as expected. In an xterm window, this has the effect of closing the window.

  • Ctl-G

    "BEL" (beep). On some old-time teletype terminals, this would actually ring a bell.

  • Ctl-H

    "Rubout" (destructive backspace). Erases characters the cursor backs over while backspacing.

    #!/bin/bash
     # Embedding Ctl-H in a string.
     
     a="^H^H"                  # Two Ctl-H's (backspaces).
     echo "abcdef"             # abcdef
     echo -n "abcdef$a "       # abcd f
     #  Space at end  ^              ^ Backspaces twice.
     echo -n "abcdef$a"        # abcdef
     #  No space at end                Doesn't backspace (why?).
                               # Results may not be quite as expected.
     echo; echo

  • Ctl-I

    Horizontal tab.

  • Ctl-J

    Newline (line feed).

  • Ctl-K

    Vertical tab.

    When typing text on the console or in an xterm window, Ctl-K erases from the character under the cursor to end of line.

  • Ctl-L

    Formfeed (clear the terminal screen). This has the same effect as the clear command.

  • Ctl-M

    Carriage return.

    #!/bin/bash
     # Thank you, Lee Maschmeyer, for this example.
     
     read -n 1 -s -p $'Control-M leaves cursor at beginning of this line. Press Enter. \x0d'
                                       # Of course, '0d' is the hex equivalent of Control-M.
     echo >&2   #  The '-s' makes anything typed silent,
                #+ so it is necessary to go to new line explicitly.
     
     read -n 1 -s -p $'Control-J leaves cursor on next line. \x0a'
     echo >&2   #  Control-J is linefeed.
     
     ###
     
     read -n 1 -s -p $'And Control-K\x0bgoes straight down.'
     echo >&2   #  Control-K is vertical tab.
     
     # A better example of the effect of a vertical tab is:
     
     var=$'\x0aThis is the bottom line\x0bThis is the top line\x0a'
     echo "$var"
     #  This works the same way as the above example. However:
     echo "$var" | col
     #  This causes the right end of the line to be higher than the left end.
     #  It also explains why we started and ended with a line feed --
     #+ to avoid a garbled screen.
     
     # As Lee Maschmeyer explains:
     # --------------------------
     #  In the [first vertical tab example] . . . the vertical tab
     #+ makes the printing go straight down without a carriage return.
     #  This is true only on devices, such as the Linux console,
     #+ that can't go "backward."
     #  The real purpose of VT is to go straight UP, not down.
     #  It can be used to print superscripts on a printer.
     #  The col utility can be used to emulate the proper behavior of VT.
     
     exit 0

  • Ctl-Q

    Resume (XON).

    This resumes stdin in a terminal.

  • Ctl-S

    Suspend (XOFF).

    This freezes stdin in a terminal. (Use Ctl-Q to restore input.)

  • Ctl-U

    Erase a line of input, from the cursor backward to beginning of line. In some settings, Ctl-U erases the entire line of input, regardless of cursor position.

  • Ctl-V

    When inputting text, Ctl-V permits inserting control characters. For example, the following two are equivalent:
    echo -e '\x0a'
     echo <Ctl-V><Ctl-J>

    Ctl-V is primarily useful from within a text editor.

  • Ctl-W

    When typing text on the console or in an xterm window, Ctl-W erases from the character under the cursor backwards to the first instance of whitespace. In some settings, Ctl-W erases backwards to first non-alphanumeric character.

  • Ctl-Z

    Pause a foreground job.

Whitespace

functions as a separator, separating commands or variables. Whitespace consists of either spaces, tabs, blank lines, or any combination thereof. In some contexts, such as variable assignment, whitespace is not permitted, and results in a syntax error.

Blank lines have no effect on the action of a script, and are therefore useful for visually separating functional sections.

$IFS, the special variable separating fields of input to certain commands, defaults to whitespace.

Notes

[1]

The shell does the brace expansion. The command itself acts upon the result of the expansion.

[2]

Exception: a code block in braces as part of a pipe may be run as a subshell.
ls | { read firstline; read secondline; }
 # Error. The code block in braces runs as a subshell,
 # so the output of "ls" cannot be passed to variables within the block.
 echo "First line is $firstline; second line is $secondline"  # Will not work.
 
 # Thanks, S.C.

Variable Substitution

The name of a variable is a placeholder for its value, the data it holds. Referencing its value is called variable substitution.

$

Let us carefully distinguish between the name of a variable and its value. If variable1 is the name of a variable, then $variable1 is a reference to its value, the data item it contains. The only time a variable appears "naked" -- without the $ prefix -- is when declared or assigned, when unset, when exported, or in the special case of a variable representing a signal (see Example 29-5). Assignment may be with an = (as in var1=27), in a read statement, and at the head of a loop (for var2 in 1 2 3).

Enclosing a referenced value in double quotes (" ") does not interfere with variable substitution. This is called partial quoting, sometimes referred to as "weak quoting." Using single quotes (' ') causes the variable name to be used literally, and no substitution will take place. This is full quoting, sometimes referred to as "strong quoting." See Chapter 5 for a detailed discussion.

Note that $variable is actually a simplified alternate form of ${variable}. In contexts where the $variable syntax causes an error, the longer form may work (see Section 9.3, below).

Example 4-1. Variable assignment and substitution

#!/bin/bash
 
 # Variables: assignment and substitution
 
 a=375
 hello=$a
 
 #-------------------------------------------------------------------------
 # No space permitted on either side of = sign when initializing variables.
 # What happens if there is a space?
 
 #  If "VARIABLE =value",
 #              ^
 #+ script tries to run "VARIABLE" command with one argument, "=value".
 
 #  If "VARIABLE= value",
 #               ^
 #+ script tries to run "value" command with
 #+ the environmental variable "VARIABLE" set to "".
 #-------------------------------------------------------------------------
 
 
 echo hello    # Not a variable reference, just the string "hello".
 
 echo $hello
 echo ${hello} # Identical to above.
 
 echo "$hello"
 echo "${hello}"
 
 echo
 
 hello="A B  C   D"
 echo $hello   # A B C D
 echo "$hello" # A B  C   D
 # As you see, echo $hello   and   echo "$hello"   give different results.
 #                                      ^      ^
 # Quoting a variable preserves whitespace.
 
 echo
 
 echo '$hello'  # $hello
 #    ^      ^
 #  Variable referencing disabled by single quotes,
 #+ which causes the "$" to be interpreted literally.
 
 # Notice the effect of different types of quoting.
 
 
 hello=    # Setting it to a null value.
 echo "\$hello (null value) = $hello"
 #  Note that setting a variable to a null value is not the same as
 #+ unsetting it, although the end result is the same (see below).
 
 # --------------------------------------------------------------
 
 #  It is permissible to set multiple variables on the same line,
 #+ if separated by white space.
 #  Caution, this may reduce legibility, and may not be portable.
 
 var1=21  var2=22  var3=$V3
 echo
 echo "var1=$var1   var2=$var2   var3=$var3"
 
 # May cause problems with older versions of "sh".
 
 # --------------------------------------------------------------
 
 echo; echo
 
 numbers="one two three"
 #           ^   ^
 other_numbers="1 2 3"
 #               ^ ^
 #  If there is whitespace embedded within a variable,
 #+ then quotes are necessary.
 echo "numbers = $numbers"
 echo "other_numbers = $other_numbers"   # other_numbers = 1 2 3
 echo
 
 echo "uninitialized_variable = $uninitialized_variable"
 # Uninitialized variable has null value (no value at all).
 uninitialized_variable=   #  Declaring, but not initializing it --
                           #+ same as setting it to a null value, as above.
 echo "uninitialized_variable = $uninitialized_variable"
                           # It still has a null value.
 
 uninitialized_variable=23       # Set it.
 unset uninitialized_variable    # Unset it.
 echo "uninitialized_variable = $uninitialized_variable"
                                 # It still has a null value.
 echo
 
 exit 0

Caution

An uninitialized variable has a "null" value - no assigned value at all (not zero!). Using a variable before assigning a value to it will usually cause problems.

It is nevertheless possible to perform arithmetic operations on an uninitialized variable.
echo "$uninitialized"                                # (blank line)
 let "uninitialized += 5"                             # Add 5 to it.
 echo "$uninitialized"                                # 5
 
 #  Conclusion:
 #  An uninitialized variable has no value, however
 #+ it acts as if it were 0 in an arithmetic operation.
 #  This is undocumented (and probably non-portable) behavior.
Оставьте свой комментарий !

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

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