3.15. Here Documents

A here document uses a special form of I/O redirection to feed a command script to an interactive program, such as ftp, telnet, or ex. Typically, the script consists of a command list to the program, delineated by a limit string. The special symbol << precedes 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

   1 command #1
   2 command #2
   3 ...

The "here document" alternative looks like this:

   1 #!/bin/bash
   2 interactive-program <<LimitString
   3 command #1
   4 command #2
   5 ...
   6 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 3-125. dummyfile: Creates a 2-line dummy file

   1 #!/bin/bash
   2 
   3 # Non-interactive use of 'vi' to edit a file.
   4 # Emulates 'sed'.
   5 
   6 E_BADARGS=65
   7 
   8 if [ -z "$1" ]
   9 then
  10   echo "Usage: `basename $0` filename"
  11   exit $E_BADARGS
  12 fi
  13 
  14 TARGETFILE=$1
  15 
  16 vi $TARGETFILE <<x23LimitStringx23
  17 i
  18 This is line 1 of the example file.
  19 This is line 2 of the example file.
  20 ^[
  21 ZZ
  22 x23LimitStringx23
  23 
  24 # Note that ^[ above is a literal escape
  25 # typed by Control-V Escape.
  26 
  27 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 3-126. broadcast: Sends message to everyone logged in

   1 #!/bin/bash
   2 
   3 wall <<zzz23EndOfMessagezzz23
   4 Dees ees a message frrom Central Headquarters:
   5 Do not keel moose!
   6 # Other message text goes here.
   7 # Note: Comment lines printed by 'wall'.
   8 zzz23EndOfMessagezzz23
   9 
  10 # Could have been done more efficiently by
  11 # wall <message-file
  12 
  13 exit 0


Example 3-127. Multi-line message using cat

   1 #!/bin/bash
   2 
   3 # 'echo' is fine for printing single line messages,
   4 #  but somewhat problematic for for message blocks.
   5 #  A 'cat' here document overcomes this limitation.
   6 
   7 cat <<End-of-message
   8 -------------------------------------
   9 This is line 1 of the message.
  10 This is line 2 of the message.
  11 This is line 3 of the message.
  12 This is line 4 of the message.
  13 This is the last line of the message.
  14 -------------------------------------
  15 End-of-message
  16 
  17 exit 0
  18 
  19 
  20 #--------------------------------------------
  21 # Code below disabled, due to "exit 0" above.
  22 
  23 # S.C. points out that the following also works.
  24 echo "-------------------------------------
  25 This is line 1 of the message.
  26 This is line 2 of the message.
  27 This is line 3 of the message.
  28 This is line 4 of the message.
  29 This is the last line of the message.
  30 -------------------------------------"
  31 # 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 3-128. Multi-line message, with tabs suppressed

   1 #!/bin/bash
   2 # Same as previous example, but...
   3 
   4 #  The - option to a here document <<-
   5 #  suppresses tabs in the body of the document,
   6 #  but *not* spaces.
   7 
   8 cat <<-ENDOFMESSAGE
   9 	This is line 1 of the message.
  10 	This is line 2 of the message.
  11 	This is line 3 of the message.
  12 	This is line 4 of the message.
  13 	This is the last line of the message.
  14 ENDOFMESSAGE
  15 # The output of the script will be flush left.
  16 # Leading tab in each line will not show.
  17 
  18 # Above 5 lines of "message" prefaced by a tab, not spaces.
  19 # Spaces not affected by   <<-  .
  20 
  21 
  22 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 3-129. Here document with parameter substitution

   1 #!/bin/bash
   2 #  Another 'cat' here document, using parameter substitution.
   3 
   4 # Try it with no command line parameters,   ./scriptname
   5 # Try it with one command line parameter,   ./scriptname Mortimer
   6 # Try it with one two-word quoted command line parameter,  ./scriptname "Mortimer Jones"
   7 
   8 CMDLINEPARAM=1  # Expect at least command line parameter.
   9 
  10 if [ $# -ge $CMDLINEPARAM ]
  11 then
  12   NAME=$1   # If more than one command line param, then just take the first.
  13 else
  14   NAME="John Doe"  # Default, if no command line parameter.
  15 fi  
  16 
  17 RESPONDENT="the author of this fine script"  
  18   
  19 
  20 cat <<Endofmessage
  21 
  22 Hello, there, $NAME.
  23 Greetings to you, $NAME, from $RESPONDENT.
  24 
  25 # This comment shows up in the output (why?).
  26 
  27 Endofmessage
  28 
  29 # Note that the blank lines show up in the output.
  30 # So does the "comment".
  31 
  32 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 3-130. Parameter substitution turned off

   1 #!/bin/bash
   2 #  A 'cat' here document, but with parameter substitution disabled.
   3 
   4 NAME="John Doe"
   5 RESPONDENT="the author of this fine script"  
   6 
   7 cat <<'Endofmessage'
   8 
   9 Hello, there, $NAME.
  10 Greetings to you, $NAME, from $RESPONDENT.
  11 
  12 Endofmessage
  13 
  14 #  No parameter substitution when the "limit string" is quoted or escaped.
  15 #  Either of the following at the head of the here document would have the same effect.
  16 #  cat <<"Endofmessage"
  17 #  cat <<\Endofmessage
  18 
  19 exit 0

This is a useful script containing a here document with parameter substitution.


Example 3-131. upload: Uploads a file pair to "Sunsite" incoming directory

   1 #!/bin/bash
   2 
   3 # upload
   4 # upload file pair (filename.lsm, filename.tar.gz)
   5 # to incoming directory at Sunsite
   6 
   7 
   8 if [ -z "$1" ]
   9 then
  10   echo "Usage: `basename $0` filename"
  11   exit 65
  12 fi  
  13 
  14 
  15 Filename=`basename $1`
  16 # Strips pathname out of file name
  17 
  18 Server="metalab.unc.edu"
  19 Directory="/incoming/Linux"
  20 # These need not be hard-coded into script,
  21 # may instead be changed to command line argument.
  22 
  23 Password="your.e-mail.address"
  24 # Change above to suit.
  25 
  26 ftp -n $Server <<End-Of-Session
  27 # -n option disables auto-logon
  28 
  29 user anonymous "$Password"
  30 binary
  31 bell
  32 # Ring 'bell' after each file transfer
  33 cd $Directory
  34 put "$Filename.lsm"
  35 put "$Filename.tar.gz"
  36 bye
  37 End-Of-Session
  38 
  39 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 3-132. "Anonymous" Here Document

   1 #!/bin/bash
   2 
   3 : <<TESTVARIABLES
   4 ${HOSTNAME?}${USER?}${MAIL?}  # Print error message if one of the variables not set.
   5 TESTVARIABLES
   6 
   7 exit 0

Note

Here documents create temporary files, but these files are deleted after opening and are not accessible to any other process.

 bash$ bash -c 'lsof -a -p $$ -d0' << EOF
 > EOF
 lsof    1213 bozo    0r   REG    3,5    0 30386 /tmp/t1213-0-sh (deleted)
 	      

Caution

Some utilities will not work in 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.