Chapter 17. 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 17-1. dummyfile: Creates a 2-line dummy file

   1 #!/bin/bash
   2 
   3 # Non-interactive use of 'vi' to edit a file.
   4 # (Will not work with 'vim', for some reason.)
   5 # Emulates 'sed'.
   6 
   7 E_BADARGS=65
   8 
   9 if [ -z "$1" ]
  10 then
  11   echo "Usage: `basename $0` filename"
  12   exit $E_BADARGS
  13 fi
  14 
  15 TARGETFILE=$1
  16 
  17 # Insert 2 lines in file, then save.
  18 #--------Begin here document-----------#
  19 vi $TARGETFILE <<x23LimitStringx23
  20 i
  21 This is line 1 of the example file.
  22 This is line 2 of the example file.
  23 ^[
  24 ZZ
  25 x23LimitStringx23
  26 #----------End here document-----------#
  27 
  28 # Note that ^[ above is a literal escape
  29 # typed by Control-V Escape.
  30 
  31 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

   1 #!/bin/bash
   2 
   3 wall <<zzz23EndOfMessagezzz23
   4 E-mail your noontime orders for pizza to the system administrator.
   5     (Add an extra dollar for anchovy or mushroom topping.)
   6 # Additional 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 # However, saving a message template in a script saves work.
  13 
  14 exit 0


Example 17-3. 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 17-4. 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, but *not* spaces.
   6 
   7 cat <<-ENDOFMESSAGE
   8 	This is line 1 of the message.
   9 	This is line 2 of the message.
  10 	This is line 3 of the message.
  11 	This is line 4 of the message.
  12 	This is the last line of the message.
  13 ENDOFMESSAGE
  14 # The output of the script will be flush left.
  15 # Leading tab in each line will not show.
  16 
  17 # Above 5 lines of "message" prefaced by a tab, not spaces.
  18 # Spaces not affected by   <<-  .
  19 
  20 
  21 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

   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,
   7 #                           ./scriptname "Mortimer Jones"
   8 
   9 CMDLINEPARAM=1     # Expect at least command line parameter.
  10 
  11 if [ $# -ge $CMDLINEPARAM ]
  12 then
  13   NAME=$1          # If more than one command line param,
  14                    # then just take the first.
  15 else
  16   NAME="John Doe"  # Default, if no command line parameter.
  17 fi  
  18 
  19 RESPONDENT="the author of this fine script"  
  20   
  21 
  22 cat <<Endofmessage
  23 
  24 Hello, there, $NAME.
  25 Greetings to you, $NAME, from $RESPONDENT.
  26 
  27 # This comment shows up in the output (why?).
  28 
  29 Endofmessage
  30 
  31 # Note that the blank lines show up in the output.
  32 # So does the "comment".
  33 
  34 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

   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 17-7. upload: Uploads a file pair to "Sunsite" incoming directory

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