Skip to main content

4. Here documents

A here document is an additional form of I/O redirection in which we embed a body of text into our script and feed it into the standard input of a command. It works like this:

command << token
. . . . .
text
. . . . .
token

where command is a command that accepts standard input and token is a string used to indicate the end of the embedded text. It should be at the beginning of the line and should have no trailing spaces.

  1. Let's modify the script to use a here document:

    vim sys_info.sh
    /echo

    Press capital O and type:

    cat << _EOF_

    Press ESC and then G and o to go to the end of the buffer and open a new line. Then type:

    _EOF_

    Press ESC and give this substitute command:

    :%s/echo "//
    Gk$x
    :wq
    The script now should look like this:
    #!/bin/bash

    # Program to output a system information page.

    declare -r TITLE="System Information Report for $HOSTNAME"
    CURRENT_TIME=$(date +"%x %r %Z")
    TIMESTAMP="Generated on $CURRENT_TIME, by $USER"
    cat << _EOF_
    <html>
    <head>
    <title>$TITLE</title>
    </head>
    <body>
    <h1>$TITLE</h1>
    <p>$TIMESTAMP</p>
    </body>
    </html>
    _EOF_
    ./sys_info.sh

    Instead of using echo, the script now uses cat and a here document.

  2. The advantage of a here document is that inside the text we can freely use single and double quotes, since they are not interpreted by the shell as delimiters of a string. For example:

    foo="some text"
    cat << EOF
    $foo
    "$foo"
    '$foo'
    \$foo
    EOF

    The shell treats the quotation marks as ordinary characters.

  3. We also notice that the variables inside the text are expanded. To prevent variable expansion we can enclose the token in quotes:

    cat << "EOF"
    $foo
    "$foo"
    '$foo'
    \$foo
    EOF
    cat << 'EOF'
    $foo
    "$foo"
    '$foo'
    \$foo
    EOF
  4. Here documents can be used with any command that accepts standard input. For example we can use it with ftp to retrieve a file:

    ftp1.sh
    #!/bin/bash

    # Script to retrive a file via FTP

    FTP_SERVER=ftp.nl.debian.org
    FTP_PATH=/debian/dists/bookworm/main/installer-amd64/current/images/cdrom/
    REMOTE_FILE=debian-cd_info.tar.gz

    ftp -n << _EOF_
    open $FTP_SERVER
    user anonymous me@linuxbox
    cd $FTP_PATH
    get $REMOTE_FILE
    bye
    _EOF_
    ls -l $REMOTE_FILE
    vim ftp1.sh
    :q!
    ./ftp1.sh

    If we change the redirection operator from << to <<-, the shell will ignore the leading tab characters in the here document. This allows a here document to be indented, which can improve readability.

    ftp2.sh
    #!/bin/bash

    # Script to retrive a file via FTP

    FTP_SERVER=ftp.nl.debian.org
    FTP_PATH=/debian/dists/bookworm/main/installer-amd64/current/images/cdrom/
    REMOTE_FILE=debian-cd_info.tar.gz

    ftp -n <<- _EOF_
    open $FTP_SERVER
    user anonymous me@linuxbox
    cd $FTP_PATH
    get $REMOTE_FILE
    bye
    _EOF_

    ls -l $REMOTE_FILE
    vim ftp2.sh
    :q!
    ./ftp2.sh
Loading asciinema cast...