From cdb853f51cc1cc98c6f2cdadb5c60dfa6b1ed098 Mon Sep 17 00:00:00 2001 From: Vidar Holen Date: Thu, 23 Sep 2021 10:25:11 -0700 Subject: [PATCH] Updated SC2005 (markdown) --- SC2005.md | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/SC2005.md b/SC2005.md index 95fd2b2..79fe13e 100644 --- a/SC2005.md +++ b/SC2005.md @@ -4,26 +4,46 @@ Useless `echo`? Instead of `echo $(cmd)`, just use `cmd` ### Problematic code: ```sh -echo "$(cat 1.txt)" -echo `< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c6` +echo "$(whoami)" ``` ### Correct code: ```sh -cat 1.txt # In bash, but faster and still sticks exactly one newline: printf '%s\n' "$(<1.txt)" -# The original `echo` sticks a newline; we want it too. -< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c6; echo +whoami ``` ### Rationale -The command substitution `$(foo)` yields the result of command `foo` with trailing newlines erased, and when it is passed to `echo` it generally just gives the same result as `foo`. +ShellCheck found the unnecessary construct `echo "$(somecommand here)"`. -The command `echo "$(false)"` will return true, whereas `false` of course returns false – beware of the ignored exit code before blindly altering scripts. If using `set -e`, the correct substitution of `echo "$(cmd)"` would be `cmd || true`. +This is generally due to a misunderstanding about what `echo` does. It has no role in "showing on screen" or similar, but simply writes a string to standard output. This is also how all other programs output data. + +`echo "$(somecommand)"` will capture the output `somecommand` writes to standard output and write it to standard output, where it was already going. At best this is a no-op, but it may have several other negative effects: + +* It disables parallel processing in pipelines, such as `echo "$(find . -name '*.iso')" | xargs sha1sum` which does not allow iterating files and checksumming at the same time. Similarly, users don't see incremental updates as programs run. +* It introduces shell and echo related pitfalls like being unable to output the string `-n`, stripping NUL bytes and trailing linefeeds, and expanding escape sequences in some shells but not others. +* It suppresses the exit code of the command, so that `echo "$(grep '^user:' /etc/passwd)"` no longer returns with failure when the user is not found. +* It does not allow programs to tailor their output for terminals, such as `ls` vs `echo "$(ls)"` where the former outputs columns and colors according to user preferences, while the latter doesn't. +* It uses unnecessary memory to buffer up the data before writing it where it was already going. + +To avoid all this, simply replace `echo "$(somecommand)"` with `somecommand` as in the example. It's shorter, faster, and more correct. ### Exceptions -One may want to use command substitutions plus `echo` to make sure there is exactly one trailing newline. The special command substitution `$( file.tmp +cat file.tmp + +# Exactly like `echo "$(cmd)"`, but allows output like `-n` and works the same across shells +printf '%s\n' "$(cmd)" +``` \ No newline at end of file