Reputation: 2766
I'm wondering when using cat <<
is preferable over simply using <<
. I'm testing in a ZSH shell and
cat <<EOF
Hello world!
EOF
outputs the same of
<<EOF
Hello world!
EOF
.
var=$(cat <<EOF
Hello world!
EOF
)
has the same variable value of
var=$(<<EOF
Hello world!
EOF
)
What are the practical differences between the two forms of here-doc?
Upvotes: 5
Views: 4990
Reputation: 5309
As long as zsh default behavior (for example invoking zsh -f
), there is no defference between the two examples. We could check the trace output with invoking the two examples with zsh -x -f
, cat
will be called actually even in the latter case.
Although these differences are described in Redirections with no command, zshmisc(1)
. zsh will behave in several ways for redirections with no command, so we can do about it to not break anything:
cat
explicitly.Redirections with no command
When a simple command consists of one or more redirection operators and zero or more parameter assignments, but no command name, zsh can behave in several ways.
If the parameter NULLCMD is not set or the option CSH_NULLCMD is set, an error is caused. This is the csh behavior and CSH_NULLCMD is set by default when emulating csh.
If the option SH_NULLCMD is set, the builtin ‘:’ is inserted as a command with the given redirections. This is the default when emulating sh or ksh.
Otherwise, if the parameter NULLCMD is set, its value will be used as a command with the given redirections. If both NULLCMD and READNULLCMD are set, then the value of the latter will be used instead of that of the former when the redirection is an input. The default for NULLCMD is ‘cat’ and for READNULLCMD is ‘more’.Thus
< file
shows the contents of file on standard output, with paging if that is a terminal. NULLCMD and READNULLCMD may refer to shell functions.
So, in the latter case, $var
will not be set if the zsh shell option SH_NULLCMD
is set, etc.
# I've tested with running `zsh -f` to check the various behaviors.
test-nullcmd () {
local var=$(<<EOF
Hello world!
EOF
)
echo "$var"
}
test-nullcmd
#> Hello world!
() {
setopt localoptions shnullcmd
test-nullcmd
}
# nothing will be printed
() {
setopt localoptions cshnullcmd
test-nullcmd
}
# test-nullcmd:1: redirection with no command (error)
() {
local NULLCMD=
test-nullcmd
}
# test-nullcmd:1: redirection with no command (error)
BTW, we could use command cat
rather than cat
solely to prevent alias expansions.
Upvotes: 6
Reputation: 295403
The behavior described is zsh-only. On zsh, this is a significant performance enhancement over unnecessarily invoking an external binary (/bin/cat
).
If you wanted to emit a heredoc's contents to stdout without overhead of starting an external tool such as cat
on a POSIX shell such as bash, you'd need something like the following instead:
# read heredoc into $var, terminated by first NUL, w/ truthy exit status
IFS= read -r -d '' var <<'EOF' ||:
content goes here
EOF
# emit $var to stdout
printf '%s' "$var"
The zsh syntax, while not guaranteed by POSIX, is significantly more terse than the read; printf
approach, while being significantly faster than cat
.
Upvotes: 1