How can I write a heredoc to a file in Bash script?

BashHeredoc

Bash Problem Overview


How can I write a here document to a file in Bash script?

Bash Solutions


Solution 1 - Bash

Read the Advanced Bash-Scripting Guide Chapter 19. Here Documents.

Here's an example which will write the contents to a file at /tmp/yourfilehere

cat << EOF > /tmp/yourfilehere
These contents will be written to the file.
		This line is indented.
EOF

Note that the final 'EOF' (The LimitString) should not have any whitespace in front of the word, because it means that the LimitString will not be recognized.

In a shell script, you may want to use indentation to make the code readable, however this can have the undesirable effect of indenting the text within your here document. In this case, use <<- (followed by a dash) to disable leading tabs (Note that to test this you will need to replace the leading whitespace with a tab character, since I cannot print actual tab characters here.)

#!/usr/bin/env bash
	
if true ; then
	cat <<- EOF > /tmp/yourfilehere
	The leading tab is ignored.
	EOF
fi

If you don't want to interpret variables in the text, then use single quotes:

cat << 'EOF' > /tmp/yourfilehere
The variable $FOO will not be interpreted.
EOF

To pipe the heredoc through a command pipeline:

cat <<'EOF' |  sed 's/a/b/'
foo
bar
baz
EOF

Output:

foo
bbr
bbz

... or to write the the heredoc to a file using sudo:

cat <<'EOF' |  sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF

Solution 2 - Bash

Instead of using cat and I/O redirection it might be useful to use tee instead:

tee newfile <<EOF
line 1
line 2
line 3
EOF

It's more concise, plus unlike the redirect operator it can be combined with sudo if you need to write to files with root permissions.

Solution 3 - Bash

Note:

The question (how to write a here document (aka heredoc) to a file in a bash script?) has (at least) 3 main independent dimensions or subquestions:

  1. Do you want to overwrite an existing file, append to an existing file, or write to a new file?
  2. Does your user or another user (e.g., root) own the file?
  3. Do you want to write the contents of your heredoc literally, or to have bash interpret variable references inside your heredoc?

(There are other dimensions/subquestions which I don't consider important. Consider editing this answer to add them!) Here are some of the more important combinations of the dimensions of the question listed above, with various different delimiting identifiers--there's nothing sacred about EOF, just make sure that the string you use as your delimiting identifier does not occur inside your heredoc:

  1. To overwrite an existing file (or write to a new file) that you own, substituting variable references inside the heredoc:

     cat << EOF > /path/to/your/file
     This line will write to the file.
     ${THIS} will also write to the file, with the variable contents substituted.
     EOF
    
  2. To append an existing file (or write to a new file) that you own, substituting variable references inside the heredoc:

     cat << FOE >> /path/to/your/file
     This line will write to the file.
     ${THIS} will also write to the file, with the variable contents substituted.
     FOE
    
  3. To overwrite an existing file (or write to a new file) that you own, with the literal contents of the heredoc:

     cat << 'END_OF_FILE' > /path/to/your/file
     This line will write to the file.
     ${THIS} will also write to the file, without the variable contents substituted.
     END_OF_FILE
    
  4. To append an existing file (or write to a new file) that you own, with the literal contents of the heredoc:

     cat << 'eof' >> /path/to/your/file
     This line will write to the file.
     ${THIS} will also write to the file, without the variable contents substituted.
     eof
    
  5. To overwrite an existing file (or write to a new file) owned by root, substituting variable references inside the heredoc:

     cat << until_it_ends | sudo tee /path/to/your/file
     This line will write to the file.
     ${THIS} will also write to the file, with the variable contents substituted.
     until_it_ends
    
  6. To append an existing file (or write to a new file) owned by user=foo, with the literal contents of the heredoc:

     cat << 'Screw_you_Foo' | sudo -u foo tee -a /path/to/your/file
     This line will write to the file.
     ${THIS} will also write to the file, without the variable contents substituted.
     Screw_you_Foo
    

Solution 4 - Bash

To build on @Livven's answer, here are some useful combinations.

  1. variable substitution, leading tab retained, overwrite file, echo to stdout

     tee /path/to/file <<EOF
     ${variable}
     EOF
    
  2. no variable substitution, leading tab retained, overwrite file, echo to stdout

     tee /path/to/file <<'EOF'
     ${variable}
     EOF
    
  3. variable substitution, leading tab removed, overwrite file, echo to stdout

     tee /path/to/file <<-EOF
         ${variable}
     EOF
    
  4. variable substitution, leading tab retained, append to file, echo to stdout

     tee -a /path/to/file <<EOF
     ${variable}
     EOF
    
  5. variable substitution, leading tab retained, overwrite file, no echo to stdout

     tee /path/to/file <<EOF >/dev/null
     ${variable}
     EOF
    
  6. the above can be combined with sudo as well

     sudo -u USER tee /path/to/file <<EOF
     ${variable}
     EOF
    

Solution 5 - Bash

When root permissions are required

When root permissions are required for the destination file, use |sudo tee instead of >:

cat << 'EOF' |sudo tee /tmp/yourprotectedfilehere
The variable $FOO will *not* be interpreted.
EOF

cat << "EOF" |sudo tee /tmp/yourprotectedfilehere
The variable $FOO *will* be interpreted.
EOF

Solution 6 - Bash

For future people who may have this issue the following format worked:

(cat <<- _EOF_
        LogFile /var/log/clamd.log
        LogTime yes
        DatabaseDirectory /var/lib/clamav
        LocalSocket /tmp/clamd.socket
        TCPAddr 127.0.0.1
        SelfCheck 1020
        ScanPDF yes
        _EOF_
) > /etc/clamd.conf

Solution 7 - Bash

As instance you could use it:

First(making ssh connection):

while read pass port user ip files directs; do
    sshpass -p$pass scp -o 'StrictHostKeyChecking no' -P $port $files $user@$ip:$directs
done <<____HERE
    PASS    PORT    USER    IP    FILES    DIRECTS
      .      .       .       .      .         .
      .      .       .       .      .         .
      .      .       .       .      .         .
    PASS    PORT    USER    IP    FILES    DIRECTS
____HERE

Second(executing commands):

while read pass port user ip; do
    sshpass -p$pass ssh -p $port $user@$ip <<ENDSSH1
    COMMAND 1
    .
    .
    .
    COMMAND n
ENDSSH1
done <<____HERE
    PASS    PORT    USER    IP
      .      .       .       .
      .      .       .       .
      .      .       .       .
    PASS    PORT    USER    IP    
____HERE

Third(executing commands):

Script=$'
#Your commands
'

while read pass port user ip; do
    sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip "$Script"

done <<___HERE
PASS    PORT    USER    IP
  .      .       .       .
  .      .       .       .
  .      .       .       .
PASS    PORT    USER    IP  
___HERE

Forth(using variables):

while read pass port user ip fileoutput; do
    sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip fileinput=$fileinput 'bash -s'<<ENDSSH1
    #Your command > $fileinput
    #Your command > $fileinput
ENDSSH1
done <<____HERE
    PASS    PORT    USER    IP      FILE-OUTPUT
      .      .       .       .          .
      .      .       .       .          .
      .      .       .       .          .
    PASS    PORT    USER    IP      FILE-OUTPUT
____HERE

Solution 8 - Bash

For those looking for a pure bash solution (or a need for speed), here's a simple solution without cat:

# here-doc tab indented
{ read -r -d '' || printf >file '%s' "$REPLY"; } <<-EOF
        foo bar
EOF

or for an easy "mycat" function (and avoid leaving REPLY in environment):

mycat() {
  local REPLY
  read -r -d '' || printf '%s' "$REPLY"
}
mycat >file <<-EOF
        foo bar
EOF

Quick speed comparison of "mycat" vs OS cat (1000 loops >/dev/null on my OSX laptop):

mycat:
real    0m1.507s
user    0m0.108s
sys     0m0.488s

OS cat:
real    0m4.082s
user    0m0.716s
sys     0m1.808s

NOTE: mycat doesn't handle file arguments, it just handles the problem "write a heredoc to a file"

Solution 9 - Bash

I like this method for concision, readability and presentation in an indented script:

<<-End_of_file >file
→       foo bar
End_of_file

Where →        is a tab character.

Solution 10 - Bash

If you want to keep the heredoc indented for readability:

$ perl -pe 's/^\s*//' << EOF
     line 1
     line 2
EOF

The built-in method for supporting indented heredoc in Bash only supports leading tabs, not spaces.

Perl can be replaced with awk to save a few characters, but the Perl one is probably easier to remember if you know basic regular expressions.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJoshua EnfieldView Question on Stackoverflow
Solution 1 - BashStefan LasiewskiView Answer on Stackoverflow
Solution 2 - BashLivvenView Answer on Stackoverflow
Solution 3 - BashTomRocheView Answer on Stackoverflow
Solution 4 - Bashgo2nullView Answer on Stackoverflow
Solution 5 - BashSerge StroobandtView Answer on Stackoverflow
Solution 6 - BashJoshua EnfieldView Answer on Stackoverflow
Solution 7 - BashMLSCView Answer on Stackoverflow
Solution 8 - BashScott ShambargerView Answer on Stackoverflow
Solution 9 - BashdanView Answer on Stackoverflow
Solution 10 - BashRoger DahlView Answer on Stackoverflow