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.