How can I write a here document to a file in Bash script?
110 Answers
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 13Instead 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.
Note:
- the following condenses and organizes other answers in this thread, esp the excellent work of Stefan Lasiewski and Serge Stroobandt
- Lasiewski and I recommend Ch 19 (Here Documents) in the Advanced Bash-Scripting Guide
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:
- Do you want to overwrite an existing file, append to an existing file, or write to a new file?
- Does your user or another user (e.g.,
root) own the file? - 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:
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. EOFTo 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. FOETo 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_FILETo 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. eofTo 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_endsTo 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
To build on @Livven's answer, here are some useful combinations.
variable substitution, leading tab retained, overwrite file, echo to stdout
tee /path/to/file <<EOF ${variable} EOFno variable substitution, leading tab retained, overwrite file, echo to stdout
tee /path/to/file <<'EOF' ${variable} EOFvariable substitution, leading tab removed, overwrite file, echo to stdout
tee /path/to/file <<-EOF ${variable} EOFvariable substitution, leading tab retained, append to file, echo to stdout
tee -a /path/to/file <<EOF ${variable} EOFvariable substitution, leading tab retained, overwrite file, no echo to stdout
tee /path/to/file <<EOF >/dev/null ${variable} EOFthe above can be combined with
sudoas wellsudo -u USER tee /path/to/file <<EOF ${variable} EOF
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 4For 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 5As 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 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"
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.
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.