A here document uses a special form of I/O redirection to feed a command list to an interactive program or command, such as ftp, telnet, or ex. A "limit string" delineates (frames) the command list. The special symbol << designates the limit string. This has the effect of redirecting the output of a file into the program, similar to interactive-program < command-file, where command-file contains
command #1 command #2 ... |
The "here document" alternative looks like this:
#!/bin/bash interactive-program <<LimitString command #1 command #2 ... LimitString |
Choose a limit string sufficiently unusual that it will not occur anywhere in the command list and confuse matters.
Note that here documents may sometimes be used to good effect with non-interactive utilities and commands.
Example 17-1. dummyfile: Creates a 2-line dummy file
#!/bin/bash # Non-interactive use of 'vi' to edit a file. # Emulates 'sed'. E_BADARGS=65 if [ -z "$1" ] then echo "Usage: `basename $0` filename" exit $E_BADARGS fi TARGETFILE=$1 # Insert 2 lines in file, then save. #--------Begin here document-----------# vi $TARGETFILE <<x23LimitStringx23 i This is line 1 of the example file. This is line 2 of the example file. ^[ ZZ x23LimitStringx23 #----------End here document-----------# # Note that ^[ above is a literal escape #+ typed by Control-V <Esc>. # Bram Moolenaar points out that this may not work with 'vim', #+ because of possible problems with terminal interaction. exit 0 |
The above script could just as effectively have been implemented with ex, rather than vi. Here documents containing a list of ex commands are common enough to form their own category, known as ex scripts.
Example 17-2. broadcast: Sends message to everyone logged in
#!/bin/bash wall <<zzz23EndOfMessagezzz23 E-mail your noontime orders for pizza to the system administrator. (Add an extra dollar for anchovy or mushroom topping.) # Additional message text goes here. # Note: Comment lines printed by 'wall'. zzz23EndOfMessagezzz23 # Could have been done more efficiently by # wall <message-file # However, saving a message template in a script saves work. exit 0 |
Example 17-3. Multi-line message using cat
#!/bin/bash # 'echo' is fine for printing single line messages, # but somewhat problematic for for message blocks. # A 'cat' here document overcomes this limitation. cat <<End-of-message ------------------------------------- This is line 1 of the message. This is line 2 of the message. This is line 3 of the message. This is line 4 of the message. This is the last line of the message. ------------------------------------- End-of-message exit 0 #-------------------------------------------- # Code below disabled, due to "exit 0" above. # S.C. points out that the following also works. echo "------------------------------------- This is line 1 of the message. This is line 2 of the message. This is line 3 of the message. This is line 4 of the message. This is the last line of the message. -------------------------------------" # However, text may not include double quotes unless they are escaped. |
The - option to mark a here document limit string (<<-LimitString) suppresses tabs (but not spaces) in the output. This may be useful in making a script more readable.
Example 17-4. Multi-line message, with tabs suppressed
#!/bin/bash # Same as previous example, but... # The - option to a here document <<- # suppresses tabs in the body of the document, but *not* spaces. cat <<-ENDOFMESSAGE This is line 1 of the message. This is line 2 of the message. This is line 3 of the message. This is line 4 of the message. This is the last line of the message. ENDOFMESSAGE # The output of the script will be flush left. # Leading tab in each line will not show. # Above 5 lines of "message" prefaced by a tab, not spaces. # Spaces not affected by <<- . exit 0 |
A here document supports parameter and command substitution. It is therefore possible to pass different parameters to the body of the here document, changing its output accordingly.
Example 17-5. Here document with parameter substitution
#!/bin/bash # Another 'cat' here document, using parameter substitution. # Try it with no command line parameters, ./scriptname # Try it with one command line parameter, ./scriptname Mortimer # Try it with one two-word quoted command line parameter, # ./scriptname "Mortimer Jones" CMDLINEPARAM=1 # Expect at least command line parameter. if [ $# -ge $CMDLINEPARAM ] then NAME=$1 # If more than one command line param, # then just take the first. else NAME="John Doe" # Default, if no command line parameter. fi RESPONDENT="the author of this fine script" cat <<Endofmessage Hello, there, $NAME. Greetings to you, $NAME, from $RESPONDENT. # This comment shows up in the output (why?). Endofmessage # Note that the blank lines show up in the output. # So does the "comment". exit 0 |
Quoting or escaping the "limit string" at the head of a here document disables parameter substitution within its body. This has very limited usefulness.
Example 17-6. Parameter substitution turned off
#!/bin/bash # A 'cat' here document, but with parameter substitution disabled. NAME="John Doe" RESPONDENT="the author of this fine script" cat <<'Endofmessage' Hello, there, $NAME. Greetings to you, $NAME, from $RESPONDENT. Endofmessage # No parameter substitution when the "limit string" is quoted or escaped. # Either of the following at the head of the here document would have the same effect. # cat <<"Endofmessage" # cat <<\Endofmessage exit 0 |
This is a useful script containing a here document with parameter substitution.
Example 17-7. upload: Uploads a file pair to "Sunsite" incoming directory
#!/bin/bash # upload.sh # Upload file pair (Filename.lsm, Filename.tar.gz) # to incoming directory at Sunsite (metalab.unc.edu). E_ARGERROR=65 if [ -z "$1" ] then echo "Usage: `basename $0` filename" exit $E_ARGERROR fi Filename=`basename $1` # Strips pathname out of file name. Server="metalab.unc.edu" Directory="/incoming/Linux" # These need not be hard-coded into script, # but may instead be changed to command line argument. Password="your.e-mail.address" # Change above to suit. ftp -n $Server <<End-Of-Session # -n option disables auto-logon user anonymous "$Password" binary bell # Ring 'bell' after each file transfer cd $Directory put "$Filename.lsm" put "$Filename.tar.gz" bye End-Of-Session exit 0 |
A here document can supply input to a function in the same script.
Example 17-8. Here documents and functions
#!/bin/bash # here-function.sh GetPersonalData () { read firstname read lastname read address read city read state read zipcode } # This certainly looks like an interactive function, but... # Supply input to the above function. GetPersonalData <<RECORD001 Bozo Bozeman 2726 Nondescript Dr. Baltimore MD 21226 RECORD001 echo echo "$firstname $lastname" echo "$address" echo "$city, $state $zipcode" echo exit 0 |
It is possible to use : as a dummy command accepting output from a here document. This, in effect, creates an "anonymous" here document.
Example 17-9. "Anonymous" Here Document
#!/bin/bash : <<TESTVARIABLES ${HOSTNAME?}${USER?}${MAIL?} # Print error message if one of the variables not set. TESTVARIABLES exit 0 |
A variation of the above technique permits "commenting out" blocks of code. |
Example 17-10. Commenting out a block of code
#!/bin/bash # commentblock.sh : << COMMENTBLOCK echo "This line will not echo." This is a comment line missing the "#" prefix. This is another comment line missing the "#" prefix. &*@!!++= The above line will cause no error message, because the Bash interpreter will ignore it. COMMENTBLOCK echo "Exit value of above \"COMMENTBLOCK\" is $?." # 0 # No error shown. # The above technique also comes in useful for commenting out #+ a block of working code for debugging purposes. # This saves having to put a "#" at the beginning of each line, #+ then having to go back and delete each "#" later. : << DEBUGXXX for file in * do cat "$file" done DEBUGXXX exit 0 |
Yet another twist of this nifty trick makes "self-documenting" scripts possible. |
Example 17-11. A self-documenting script
#!/bin/bash # self-document.sh: self-documenting script # Modification of "colm.sh". DOC_REQUEST=70 if [ "$1" = "-h" -o "$1" = "--help" ] # Request help. then echo; echo "Usage: $0 [directory-name]"; echo sed --silent -e '/DOCUMENTATIONXX$/,/^DOCUMENTATION/p' "$0" | sed -e '/DOCUMENTATIONXX/d'; exit $DOC_REQUEST; fi : << DOCUMENTATIONXX List the statistics of a specified directory in tabular format. --------------------------------------------------------------- The command line parameter gives the directory to be listed. If no directory specified or directory specified cannot be read, then list the current working directory. DOCUMENTATIONXX if [ -z "$1" -o ! -r "$1" ] then directory=. else directory="$1" fi echo "Listing of "$directory":"; echo (printf "PERMISSIONS LINKS OWNER GROUP SIZE MONTH DAY HH:MM PROG-NAME\n" \ ; ls -l "$directory" | sed 1d) | column -t exit 0 |
Here documents create temporary files, but these files are deleted after opening and are not accessible to any other process.
|
Some utilities will not work inside a here document. |
For those tasks too complex for a "here document", consider using the expect scripting language, which is specifically tailored for feeding input into interactive programs.