Daniele Orlando
Daniele Orlando

Reputation: 2766

Why not using <<EOF instead of cat <<EOF?

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

Answers (2)

hchbaw
hchbaw

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:

  • To use cat explicitly.
  • To set the localoptions/parameters.

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.

-- Redirections with no command, zshmisc(1)

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

Charles Duffy
Charles Duffy

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

Related Questions