Apenas uma nota extra em cima da boa resposta de @ Kusalananda .
echo run after_bundle
é bom porque nenhum dos caracteres nesses 3 argumentos¹ passou para echo
conter caracteres especiais para o shell.
E (o ponto extra que quero destacar aqui) não há localidade do sistema em que esses bytes possam ser traduzidos para caracteres especiais para o shell.
Todos esses caracteres estão no que o POSIX chama de conjunto de caracteres portátil . Esses caracteres devem estar presentes e codificados da mesma forma em todos os conjuntos de caracteres em um sistema POSIX².
Portanto, essa linha de comando será interpretada da mesma forma, independentemente da localidade.
Agora, se começarmos a usar caracteres fora desse conjunto de caracteres portátil, é uma boa ideia citá-los, mesmo que não sejam especiais para o shell, porque em outro local, os bytes que os constituem podem ser interpretados como caracteres diferentes que podem se tornar especial para a concha. Observe que, independentemente de você estar usando echo
ou qualquer outro comando, o problema não é com echo
mas como o shell analisa seu código.
Por exemplo, em um UTF-8:
echo voilà | iconv -f UTF-8 -t //TRANSLIT
Isso à
é codificado como 0xc3 0xa0. Agora, se você tiver essa linha de código em um script de shell e o script de shell for invocado por um usuário que use um código de idioma cujo conjunto de caracteres não seja UTF-8, esses dois bytes poderão gerar caracteres muito diferentes.
Por exemplo, em um fr_FR.ISO8859-15
código de idioma, um código de idioma típico do francês que usa o conjunto de caracteres de byte padrão que cobre o idioma francês (o mesmo usado para a maioria dos idiomas da Europa Ocidental, incluindo o inglês), que 0xc3 byte é interpretado como o Ã
caractere e 0xa0 como não quebrando o caráter do espaço.
E em alguns sistemas como o NetBSD³, esse espaço sem quebra é considerado como um caractere em branco ( isblank()
ele retorna verdadeiro, é correspondido por [[:blank:]]
) e shells como, bash
portanto, o tratam como um delimitador de token em sua sintaxe.
Isso significa que em vez de correr echo
com $'voil\xc3\xa0'
como argumento, eles executá-lo com $'voil\xc3'
como argumento, o que significa que não será impresso voilà
corretamente.
Ele fica muito pior com conjuntos de caracteres chineses como BIG5, BIG5-HKSCS, GB18030, GBK que têm muitos personagens cujas codificação contém a mesma codificação como |
, `
, \
(para citar o pior) (também que SJIS ridícula, aka Microsoft Kanji, excepto em ¥
vez de \
, mas ainda tratado como \
pela maioria das ferramentas, pois está codificado como 0x5c).
Por exemplo, se em um zh_CN.gb18030
local chinês, você escreve um script como:
echo 詜 reboot
Esse script será produzido 詜 reboot
em um 唰 reboot
código de idioma usando GB18030 ou GBK, em um código de idioma usando BIG5 ou BIG5-HKSCS, mas em um código de idioma C usando ASCII ou em um código de idioma usando ISO8859-15 ou UTF-8, será reboot
executado porque a codificação GB18030 de 詜
é 0xd4 0x7c e 0x7c é a codificação |
em ASCII, portanto, acabamos executando:
echo �| reboot
(que representa no entanto o byte 0xd4 é renderizado no código do idioma). Exemplo usando o menos prejudicial em uname
vez de 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
foi executado).
Portanto, meu conselho seria citar todas as strings que contêm caracteres fora do conjunto de caracteres portáteis.
No entanto, observe que, como a codificação de \
e `
é encontrada na codificação de alguns desses caracteres, é melhor não usar \
or "..."
ou $'...'
(dentro do qual `
e / ou \
ainda são especiais), mas '...'
sim citar caracteres fora do conjunto de caracteres portátil.
Não conheço nenhum sistema que possua uma localidade em que o conjunto de caracteres possua qualquer caractere (que não seja '
ele próprio, é claro) cuja codificação contenha a codificação de '
, portanto esses '...'
devem ser definitivamente os mais seguros.
Observe que vários shells também suportam uma $'\uXXXX'
notação para expressar caracteres com base em seu ponto de código Unicode. Em shells como zsh
e bash
, o caractere é inserido codificado no conjunto de caracteres da localidade (embora possa causar comportamentos inesperados se esse conjunto de caracteres não tiver esse caractere). Isso permite que você evite inserir caracteres não ASCII no seu código de shell.
Então acima:
echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'
Ou:
echo $'voil\u00e0'
echo $'\u8a5c reboot'
(com a ressalva, ele pode interromper o script quando executado em locais que não possuem esses caracteres).
Ou melhor, já que \
também é especial para echo
(ou pelo menos algumas echo
implementações, pelo menos as compatíveis com Unix):
printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'
(observe que \
também é especial no primeiro argumento para printf
, portanto, caracteres não-ASCII também são melhor evitados no caso de conterem a codificação de \
).
Observe que você também pode fazer:
'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'
(isso seria um exagero, mas poderia lhe dar alguma tranqüilidade se você não tiver certeza de quais caracteres estão no conjunto de caracteres portáteis)
Além disso, certifique-se de nunca usar a `...`
forma antiga de substituição de comando (que introduz outro nível de processamento de barra invertida), mas use em $(...)
vez disso.
¹ tecnicamente, echo
também é passado como argumento para o echo
utilitário (para dizer como foi chamado), é o argv[0]
e argc
é 3, embora na maioria dos shells hoje em dia echo
esteja embutido, de modo que exec()
um /bin/echo
arquivo com uma lista de 3 argumentos seja simulado pelo Concha. Também é comum considerar a lista de argumentos como iniciando com o segundo ( argv[1]
para argv[argc - 1]
), pois é sobre isso que os comandos atuam principalmente.
² uma exceção notável por ser o ja_JP.SJIS
local ridículo dos sistemas FreeBSD cujo charset não tem \
nem ~
caráter!
³ observe que, embora muitos sistemas (FreeBSD, Solaris, e não os GNU) considerem U + 00A0 como um local [[:blank:]]
UTF-8, poucos o fazem em outros locais como os que usam ISO8859-15, possivelmente para evitar esse tipo de problema.