Following the previous article on understanding Here Document and Here String in Linux, this article presents some examples for reference.
1. cat and heredoc
<span>cat</span> using <span>heredoc</span> in its simplest form looks like this:
cat << EOF
123
456
hello, world
EOF
Used with a pipe:
# cat_heredoc.sh
cat << EOF | grep 123
123
456
hello, world
EOF
<span>Output:</span>
bash-5.3$ bash cat_here_doc.sh
123
In the shell, <span>interactive input</span> is also possible:
bash-5.3$ cat << EOF | grep 123
> 123
> 456
> hello, world
> EOF
123
Can we input from both <span>heredoc</span> and a file at the same time? Let’s try it.
# Create a new file word.txt
echo "this is word.txt" > word.txt
Try the following script:
# cat_heredoc.sh
cat << EOF word.txt
123
456
hello, world
EOF
echo "#------------#"
cat word.txt << EOF
123
456
hello, world
EOF
The running result shows that heredoc failed, as follows:
bash-5.3$ bash cat_here_doc.sh
this is word.txt
#------------#
this is word.txt
There was no error message, but the content of heredoc was not output.
The author eventually tested it out, please continue reading.
# cat_heredoc.sh
cat - << EOF word.txt
123
456
hello, world
EOF
echo "#------------#"
cat word.txt - << EOF
123
456
hello, world
EOF
This time it succeeded, both heredoc and file content were output.
bash-5.3$ bash cat_here_doc.sh
123
456
hello, world
this is word.txt
#------------#
this is word.txt
123
456
hello, world
The key point here is that <span>-</span> is used as standard input. If that’s the case, can we use the number <span>0</span>? The author’s test result is: no! It seems that here <span>0</span> is just a regular number, with no other special meaning.
<span>-</span> as standard input is a convention in <span>Unix/Linux</span> systems, but not all commands support it. Commands like <span>cat</span>, <span>grep</span>, <span>sed</span>, <span>awk</span> support it, while the <span>-</span> in the <span>cd</span> command has a special meaning, so the usage scenario should depend on the actual situation of the command.
Another way of writing is also feasible.
# cat_heredoc.sh
cat /dev/stdin << EOF word.txt
123
456
hello, world
EOF
Changing <span>-</span> to <span>/dev/stdin</span>
bash-5.3$ bash cat_here_doc.sh
123
456
hello, world
this is word.txt
As shown, the effect is the same.
/dev/stdin[1] is the <span>duplicated</span> of standard input. The author speculates that as long as the command itself supports reading from <span>standard input</span>, it can use <span>/dev/stdin</span> to read <span>heredoc</span>, but it turns out this idea is <span>not quite right</span>.
# cat_heredoc.sh
# Parameter position is wrong.
grep /dev/stdin << EOF 123
123
456
EOF
echo "#------------#"
# ok
grep 123 /dev/stdin << EOF
123
456
EOF
<span>Running result:</span>
bash-5.3$ bash cat_here_doc.sh
grep: 123: No such file or directory
#------------#
123
<span>grep /dev/stdin << EOF 123</span> treats <span>123</span> as a filename.
At this point, it is clear that whether it is <span>cat</span> or <span>grep</span>, they can use <span>/dev/stdin</span> because their parameters indeed require specifying a file. When specifying <span>/dev/stdin</span>, it is equivalent to specifying a file, but this file is special as it is <span>standard input</span>, and then using <span>heredoc</span> for redirection input achieves the desired effect. The reason why <span>cat</span> can use <span>/dev/stdin</span> is the same.
Here, the author suddenly realizes the design philosophy of <span>everything is a file</span>.
2. python and heredoc
Here, <span>python</span> can be replaced with other scripting languages.
<span>Template code</span>
output=$(python << EOF
import sys
print(sys.path)
EOF
)
echo $output
<span>Calculating the area of a circle</span>
# bash_py.sh
radius=2.5
area=$(python << EOF
import math
radius=${radius}
print(math.pi * radius * radius)
EOF
)
echo $area
# Area of the circle
bash-5.3$ bash bash_py.sh
19.634954084936208
3. ftp and heredoc
<span>ftp</span> and other interactive commands collaborating with heredoc are also worth learning about.
#!/bin/bash
BACKUP_FILE="backup_$(date +%Y%m%d).tar.gz"
FTP_SERVER="ftp.server.com"
FTP_USER="username"
FTP_PASS="password"
# Create backup
tar -czf $BACKUP_FILE /important/data
# Upload to FTP
ftp -n << EOF
open $FTP_SERVER
user $FTP_USER$FTP_PASS
binary
put ${BACKUP_FILE}
quit
EOF
echo "Backup ${BACKUP_FILE} has been uploaded"
4. Functions and heredoc
# func_heredoc.sh
function GetPersonalData()
{
read name
read age
read address
}
GetPersonalData << EOF
张三
20
中国
EOF
echo "$name"
echo "$address"
<span>Running result:</span>
bash-5.3$ bash func_heredoc.sh
张三
中国
5. Anonymous heredoc
<span>The syntax is as follows:</span>
: << EOF
These contents will not be displayed
Suitable for comments or documentation
EOF
Below is an example of a commented code block.
#!/usr/bin/env bash
# test.sh
: << 'EOF'
radius=2.5
result=$(awk -v r=$radius'BEGIN {printf "%.3f", 3.14159 * r * r}')
echo "area: $result"
EOF
: << EOF
The previous code for calculating the area of a circle has been commented out and will not execute
EOF
echo 'hello, world!'
<span>Running result:</span>
bash-5.3$ bash test.sh
hello, world!
If it is an unquoted <span>heredoc</span>, variable expansion and other operations will be executed, just not output.
# test.sh
name="张三"
arg=20
: << EOF
${name}的地址是:${address?}
EOF
echo $?
<span>Running result:</span>
bash-5.3$ bash test.sh
test.sh: 行 6: address: 参数未设置
References
[1]
/dev/stdin: https://www.gnu.org/software/bash/manual/html_node/Redirections.html