3

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.

4
  • 3
    Are you sure you're running Bash? echo $BASH_VERSION?
    – ilkkachu
    Aug 22, 2018 at 20:12
  • ...ooops. I'm using zsh. I guess I had assumed that zsh and bash work the same. Aug 22, 2018 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, 2018 at 20:14
  • 1
    Yeah, it's a zsh problem. Aug 22, 2018 at 20:14

2 Answers 2

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)"
1
  • 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)". Aug 23, 2018 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.