Is it dangerous to run echo without quotes?

Just an extra note on top of @Kusalananda's fine answer.

echo run after_bundle

is fine because none of the characters in those 3 arguments¹ passed to echo contain characters that are special to the shell.

And (the extra point I want to make here) there's no system locale where those bytes could translate to characters that are special to the shell.

All those characters are in what POSIX calls the portable character set. Those characters should be present and encoded the same in all character sets on a POSIX system².

So that command line will be interpreted the same regardless of the locale.

Now, if we start using characters outside of that portable character set, it's a good idea to quote them even if they are not special to the shell, because in another locale, the bytes that constitute them may be interpreted as different characters that could become special to the shell. Note that it's whether you're using echo or any other command, the problem is not with echo but with how the shell parses its code.

For instance in a UTF-8:

echo voilà | iconv -f UTF-8 -t //TRANSLIT

That à is encoded as 0xc3 0xa0. Now, if you have that line of code in a shell script and the shell script is invoked by a user who uses a locale whose charset is not UTF-8, those two bytes could make very different characters.

For instance, in a fr_FR.ISO8859-15 locale, a typical French locale using the standard single-byte charset that covers the French language (the same used for most western European languages including English), that 0xc3 byte is interpreted as the à character and 0xa0 as the non-breaking space character.

And on a few systems like NetBSD³, that non-breaking space is considered as a blank character (isblank() on it returns true, it is matched by [[:blank:]]) and shells like bash therefore treat it as a token delimiter in their syntax.

That means that instead of running echo with $'voil\xc3\xa0' as argument, they run it with $'voil\xc3' as argument, which means it won't print voilà correctly.

It gets a lot worse with Chinese character sets like BIG5, BIG5-HKSCS, GB18030, GBK which have many characters whose encoding contains the same encoding as the |, `, \ (to name the worst) (also that ludicrous SJIS, aka Microsoft Kanji, except that it's ¥ instead of \, but still treated as \ by most tools as it's encoded as 0x5c there).

For instance, if in a zh_CN.gb18030 Chinese locale, you write a script like:

echo 詜 reboot

That script will output 詜 reboot in a locale using GB18030 or GBK, 唰 reboot in a locale using BIG5 or BIG5-HKSCS, but in a C locale using ASCII or a locale using ISO8859-15 or UTF-8, will cause reboot to be run because the GB18030 encoding of is 0xd4 0x7c and 0x7c is the encoding of | in ASCII so we end up running:

 echo �| reboot

(that � representing however the 0xd4 byte is rendered in the locale). Example using the less harmful uname instead of reboot:

$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$

(uname was run).

So my advise would be to quote all strings that contain characters outside of the portable character set.

However note that since the encoding of \ and ` are found in the encoding of some of those characters, it's better not to use \ or "..." or $'...' (inside which ` and/or \ are still special), but the '...' instead to quote characters outside the portable character set.

I'm not aware of any system that has a locale where the charset has any character (other than ' itself of course) whose encoding contains the encoding of ', so those '...' should definitely be the safest.

Note that several shells also support a $'\uXXXX' notation to express characters based on their Unicode code point. In shells like zsh and bash, the character is inserted encoded in the locale's charset (though can cause unexpected behaviours if that charset doesn't have that character). That lets you avoid to insert non-ASCII characters in your shell code.

So above:

echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'

Or:

echo $'voil\u00e0'
echo $'\u8a5c reboot'

(with the caveat the it could break the script when run in locales that don't have those characters).

Or better, since \ is also special to echo (or at least some echo implementations, at least the Unix compliant ones):

printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'

(note that \ is also special in the first argument to printf, so non-ASCII characters are also better avoided there in case they may contain the encoding of \).

Note that you could also do:

'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'

(that would be overkill but could give you some peace of mind if you're not sure which characters are in the portable character set)

Also make sure never to use the ancient `...` form of command substitution (which introduces another level of backslash processing), but use $(...) instead.


¹ technically, echo is also passed as argument to the echo utility (to tell it how it was invoked), it's the argv[0] and argc is 3, though in most shells nowadays echo is builtin, so that exec() of a /bin/echo file with a list of 3 arguments is simulated by the shell. It's also common to consider the list of arguments as starting with the second one (argv[1] to argv[argc - 1]) as that's the ones the commands mainly act upon.

² a notable exception to that being the ludicrous ja_JP.SJIS locale of FreeBSD systems whose charset has no \ nor ~ character!

³ note that while many systems (FreeBSD, Solaris, not GNU ones though) consider U+00A0 as a [[:blank:]] in UTF-8 locales, few do in other locales like those using ISO8859-15, possibly to avoid this kind of issue.


For the specific case

echo run after_bundle

quoting is not needed. No quoting is needed because the argument to echo are static strings that contain no variable expansions or command substitutions etc. They are "just two words" (and as Stéphane points out, they are additionally constructed out of the portable character set).

The "danger" comes when you deal with variable data that the shell may expand or interpret. In such cases, care must be taken that the shell does the correct thing and that the result is what's intended.

The following two questions contain relevant information about that:

  • Why is printf better than echo?
  • Security implications of forgetting to quote a variable in bash/POSIX shells

echo is sometimes used to "protect" potentially harmful commands in answers on this site. For example, I may show how to remove files or move files to a new destination using

echo rm "${name##*/}.txt"

or

echo mv "$name" "/new_dir/$newname"

This would output commands on the terminal instead of actually removing or renaming files. The user could then inspect the commands, decide that they look ok, remove the echo and run again.

Your command echo run after_bundle may be an instruction to the user, or it may be a "commented out" piece of code that is too dangerous to run without knowing the consequences.

Using echo like this, one has to know what the modified command does and one must guarantee that the modified command actually is safe (it would potentially not be if it contained redirections, and using it on a pipeline doesn't work, etc.)