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
à 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
$'voil\xc3\xa0' as argument, they run it with
$'voil\xc3' as argument, which means it won't print
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
$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
$ LC_ALL=C bash ./myscript | sed -n l
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
` are found in the encoding of some of those characters, it's better not to use
$'...' (inside which
\ 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
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.
echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'
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'
\ 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
echo is also passed as argument to the
echo utility (to tell it how it was invoked), it's the
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[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
³ 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.