2

How can I disable word-splitting during command substitution? Here's a simplified example of the problem:

 4:00PM /Users/paymahn/Downloads
 ❯❯❯ cat test.txt
hello\nworld
 4:00PM /Users/paymahn/Downloads
 ❯❯❯ echo $(cat test.txt )
hello
world
 4:00PM /Users/paymahn/Downloads
 ❯❯❯ echo "$(cat test.txt )"
hello
world
 4:01PM /Users/paymahn/Downloads
 ❯❯❯ echo "$(cat "test.txt" )"
hello
world

What I want is for echo $(cat test.txt) (or some variant of that which includes command subsitution) to output hello\nworld.

I found https://www.gnu.org/software/bash/manual/html_node/Command-Substitution.html which says at the bottom If the substitution appears within double quotes, word splitting and filename expansion are not performed on the results. but I can't seem to make sense of that. I would have thought that one of the examples I already tried conformed to that rule but I guess not.

  • 3
    Are you sure you're running Bash? echo $BASH_VERSION? – ilkkachu Aug 22 '18 at 20:12
  • ...ooops. I'm using zsh. I guess I had assumed that zsh and bash work the same. – Paymahn Moghadasian Aug 22 '18 at 20:14
  • If the result of the first cat is literally "hello\nworld", then the rest of the examples don't make sense. Please show a hexdump of your file hexdump -C test.txt. – RalfFriedl Aug 22 '18 at 20:14
  • 1
    Yeah, it's a zsh problem. – Paymahn Moghadasian Aug 22 '18 at 20:14
10

Having a literal \n get changed to a newline isn't about word splitting, but echo processing the backslash. Some versions of echo do that, some don't... Bash's echo doesn't process backslash-escapes by default (without the -e flag or xpg_echo option), but e.g. dash's and Zsh's versions of echo do.

$ cat test.txt 
hello\nworld
$ bash -c 'echo "$(cat test.txt)"'
hello\nworld
$ zsh -c 'echo "$(cat test.txt)"'
hello
world

Use printf instead:

$ bash -c 'printf "%s\n" "$(cat test.txt)"'
hello\nworld
$ zsh -c 'printf "%s\n" "$(cat test.txt)"'
hello\nworld

See also: Why is printf better than echo?

Regardless of that, you should put the quotes around the command substitution to prevent word splitting and globbing in sh-like shells. (zsh only does word splitting (not globbing) upon command substitution (not upon parameter or arithmetic expansions) except in sh-mode.)

0

The echo implemented by zsh interprets escape sequences by default. The simplest solution is:

$ echo -E "$(cat test.txt)"
hello\nworld

or

$ print -r "$(cat test.txt)"

The correct solution is to use printf:

$ printf '%s\n' "$(<test.txt)"
  • Note that in zsh one may prefer echo -E - "$(<test.txt)" or print -r -- "$(<test.txt)" in case the content of test.txt starts with -f, which ends up not being much shorter than printf '%s\n' "$(<test.txt)". – Stéphane Chazelas Aug 23 '18 at 6:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.