shellcheck icon indicating copy to clipboard operation
shellcheck copied to clipboard

Consider warning about echo containing a backslash when the shell is /bin/sh

Open helmutg opened this issue 2 years ago • 3 comments

For bugs

  • Rule ID: SC2028
  • My shellcheck version: online
  • [x] The rule's wiki page does not already cover this entirely
  • [x] I tried on https://www.shellcheck.net/

Here's a snippet or screenshot that shows the problem:


#!/bin/sh
echo '\\ output depends on shell'

# bash: \\ output depends on shell
# coreutils: \\ output depends on shell
# dash: \ output depends on shell

Here's what shellcheck currently says:

Line 2:
echo '\\ output depends on shell'
     ^-- SC2028 (info): echo may not expand escape sequences. Use printf.

Here's what I wanted or expected to see:

The info message and advice is useful already, but I think that it is too weak for some situations. If you try an echo in different shells, you shall see that

  • bash will echo back the unescaped string (unless adding -e).
  • /bin/echo from coreutils likewise.
  • dash will unescape the string. If passing -e, it will echo -e as well. See also https://bugs.debian.org/538100

Thus the behaviour of this code is dependent on the shell. As such I would like to see a severity warning message from shellcheck (if and only if the shell is specified as sh).

Furthermore, the wiki page advises to use echo "\\t" to get a literal backslash t. Notably, on dash this actually outputs a tab. I think this advice should be changed to printf "\\t".

I am unsure whether this should be an extension to SC2028 or a new rule, but I hope you see the problem.

In essence, I'd like to get a warning whenever the shell is sh and an argument to echo involves a literal backslash. It is questionable whether there also should be a warning when echo is used to output a variable (which may contain a backslash of its own). For concrete shells, depending on the particular shell behaviour seems fine to me.

helmutg avatar Jan 13 '23 10:01 helmutg

I'm not sure I quite follow. Without -e, all 3 cases return the unescaped string right? Maybe checking only if -e is passed would be appropiate in that case. I'm more in favor of encouraging printf usage for this.

port19x avatar Jan 16 '23 06:01 port19x

Thank you for following up. As surprising as it may sound, dash echo will always unescape regardless of whether you specify -e or not. If you also pass -e it will echo that and -e does not affect escaping. It is not considered an option.

This behavior of dash is the primary reason for me filing this issue. If it were not unconditionally unescaping, I would not have reported this.

helmutg avatar Jan 16 '23 06:01 helmutg

https://github.com/SwuduSusuwu/SubStack/issues/47 has to do with this; the problem was that the SC2028 info message does not show if you have

#!/bin/sh
echo "\033[34m Coded blue"

, which results in color codes which /bin/sh shows but which break (which print literal escape sequences) if you switch to #!/bin/bash (or if you have a system where /bin/sh points to /bin/bash, although such is perhaps not a POSIX system). Such should not produce a warning (as such is valid for POSIX /bin/sh), but is odd that such does not produce a notice or info message.

The solution was :%s/echo "\([^"]*\)"/printf '%b\\n' "\1"/.

SwuduSusuwu avatar Jan 24 '25 02:01 SwuduSusuwu