O que faz o utilitário interpretador bash interpretar aspas duplas diferentemente padrão e inteligentes, presentes em um shell script, criado no TextEdit?

3

Enquanto aprendia o script bash através de um Guia do Iniciante , tentei escrever algumas linhas de código no .sharquivo, criado no TextEdit no macOS Yosemite e executá-lo usando o comando bash \path\to\script\file\example_script.shbash Terminal.

Linha de código que eu testei pela primeira vez:

echo The path to my home directory is: $HOME

Saída padrão (stdout) no Terminal:

The path to my home directory is: ??

em vez de obter:

The path to my home directory is: /Users/Ri$hi

Então, eu fiquei sabendo do curioso caso de "smart quotes"uma troca de pilha e joguei com alguns combos engraçados, como abaixo:

Linha de código que testei mais tarde:

Cenário 1:

echo The path to my home directory is: $HOME (foo) bar

stdout:

-bash: syntax error near unexpected token `('

Cenário 2:

echo "The path to my home directory is: $HOME (foo) bar"

stdout:

The path to my home directory is: /Users/Ri$hi (foo) bar

Cenário 3:

echo The path to my home directory is: $HOME “(foo)” bar

stdout:

-bash: syntax error near unexpected token `('

Cenário 4:

echo "The path to my home directory is: $HOME" “(foo)” bar

stdout:

-bash: syntax error near unexpected token `('

Cenário 5:

echo The path to my home directory is: $HOME "(foo)" bar

stdout:

The path to my home directory is: ?? (foo) bar

Então, pensei por que não descobrir o motivo interativamente neste fórum.

NOTA: Terminal sempre exibe Citação duplo padrão "quando Shift+ "é pressionado, mas permite exibir aspas inteligentes “ ”através + C, + Voperação.

Rishi Khanna
fonte
Admito que não li a pergunta inteira, porque isso não importa, pois você não pode usar aspas inteligentes em um script bash! Sempre use formatação e pontuação de código adequadas ao criar scripts! Use 'e ou "conforme apropriado, quando aplicável. Eu também sugiro fortemente que você nunca use o TextEdit e, em vez disso, use um editor de código apropriado, por exemplo, Sublime Text.
user3439894
Além disso, sempre execute seu código de script bash através do ShellCheck antes de executá-lo! :) Além disso, ao usar o RegEx, você encontrará o Regex101 - editor e depurador online de regex útil para testar suas expressões regulares.
user3439894
Você pode usar o TextEdit em uma pitada, desde que defina o formato como Texto sem formatação. Pessoalmente, uso o BBEdit e o TextWrangler.
IconDaemon
@IconDaemon, o TextEdit no modo Texto sem formatação ainda inserirá aspas inteligentes ao digitar aspas simples, a menos que você desative esse recurso. Embora eu tenha sugerido o Sublime Text, um aplicativo pago, no entanto, existem editores de código gratuitos disponíveis que são muito melhores que o TextEdit (em qualquer circunstância IMO).
user3439894
@ user3439894 - Concordado.
IconDaemon

Respostas:

9

Há duas coisas acontecendo aqui: Primeiro, o bash reconhece as aspas simples ASCII simples "(código de caractere 0x22) como aspas duplas; ele não reconhece as aspas duplas à esquerda do unicode chique (unicode U + 201C, UTF-8 que codifica 0xe2809c) e a citação dupla à direita correspondente (unicode U + 201D, UTF-8 que codifica 0xe2809d) como algo diferente de seqüências aleatórias de bytes (ou talvez caracteres aleatórios, se estiver usando um código de idioma UTF-8). É isso que você deve perceber: no que diz respeito ao bash, e na verdade não são aspas , são apenas coisas que parecem aspas quando são impressas.

A segunda complicação é que as aspas duplas unicode são caracteres multibyte; portanto, se o bash não estiver em um código de idioma UTF-8, ele poderá tratar alguns bytes de maneira diferente dos outros (!)

Para ver o efeito da primeira coisa, tente substituir cada ocorrência de aspas duplas pela string WIBBLE- outra sequência arbitrária que não tem significado especial para o shell:

$ echo "The path to my home directory is: $HOME bar"
The path to my home directory is: /Users/gordon bar
$ echo The path to my home directory is: $HOME bar
The path to my home directory is: /Users/gordon bar
$ echo WIBBLEThe path to my home directory is: $HOME barWIBBLE
WIBBLEThe path to my home directory is: /Users/gordon barWIBBLE

No primeiro comando (com aspas duplas ASCII), as aspas são analisadas e removidas pelo bash antes que os argumentos sejam passados ​​para o echocomando e, portanto, não são impressos. Na segunda e na terceira (com aspas duplas sofisticadas e WIBBLE no lugar de aspas simples), elas são tratadas apenas como parte das strings a serem passadas echo, e as echoimprimem como parte de sua saída.

$ echo "The path to my home directory is: $HOME (foo) bar"
The path to my home directory is: /Users/gordon (foo) bar
$ echo The path to my home directory is: $HOME (foo) bar
-bash: syntax error near unexpected token `('
$ echo WIBBLEThe path to my home directory is: $HOME (foo) barWIBBLE
-bash: syntax error near unexpected token `('

Nos segundo e terceiro comandos (com aspas duplas e WIBBLE), o bash vê parênteses em uma parte não citada do comando (lembre-se: no que diz respeito ao bash, aspas extravagantes não são realmente aspas ), em um local onde eles não são permitidos pela sintaxe do shell e, portanto, reclama.

$ echo The path to my home directory is: $HOME
The path to my home directory is: ??
$ echo WIBBLEThe path to my home directory is: $HOMEWIBBLE
WIBBLEThe path to my home directory is:

Aqui, algo mais estranho está acontecendo. No segundo comando, ele está procurando uma variável denominada HOMEWIBBLE, não a encontra, substituindo-a por uma em branco. No caso do primeiro, com aspas duplas, parece-me tratar cada byte da codificação UTF-8 como um caractere separado, tratando o primeiro como parte do nome da variável (causando novamente a variável não encontrado) e, em seguida, basta passar o segundo e o terceiro bytes, fornecendo um caractere UTF-8 inválido, que é impresso como ??. Usar um dump hexadecimal para ter uma idéia melhor do que está acontecendo fornece:

$ echo $HOME
“??
$ echo $HOME | xxd -g1
00000000: e2 80 9c 80 9d 0a                                ......

Observe que a primeira passa bem e aparece no dump hexadecimal como e2 80 9c(a cotação dupla extravagante codificada UTF-8 esperada), mas depois disso é apenas 80 9d- a primeira e2da segunda cotação extravagante foi comida de alguma forma! (BTW, o 0ano final é um avanço de linha, marcando o final da saída.) Para ver o que está acontecendo, deixe-me definir uma variável do shell como HOME+ o primeiro byte da codificação e observe o que acontece:

$ eval $'HOME\xe2=foo'
$ echo $HOME
foo??
$ echo $HOME | xxd -g1
00000000: e2 80 9c 66 6f 6f 80 9d 0a                       ...foo...

... Então, o que está acontecendo: trata o primeiro byte da codificação de aspas duplas como parte do nome da variável, substitui-o (se definido) e passa apenas pelo segundo e terceiro bytes órfãos, deixando UTF inválido- 8) Não tenho certeza se isso é um bug do bash, estranheza de sua análise ou o quê.

De qualquer forma, os detalhes são bastante confusos, mas a explicação deve ser clara: não use aspas sofisticadas em seus scripts de shell; eles não vão funcionar direito. E o mesmo se aplica a aspas simples e outros sinais de pontuação unicode.

Gordon Davisson
fonte
Boa resposta +1, eu simplesmente não queria me dar ao trabalho de explicar o porquê, quando a resposta já estava na Internet e é preciso usar aspas simples de qualquer maneira.
user3439894
@ gordon-davisson Além disso, durante a execução , $ echo ”$HOME” | xxd -g1recebo 0000000: e2 80 9d 66 6f 6f 80 9d 0a7 zeros inicialmente, em vez de 8, no seu caso. Qual poderia ser a razão por trás disso?
Rishi Khanna
Tenho um novo respeito pelo poder do WIBBLE - isso é incrivelmente útil e abrangente. +1 e mais.
bmike
@RishiKhanna Isso é estranho; os zeros iniciais não se baseiam na saída de echo, eles são adicionados xxdpara acompanhar onde você está (necessário ao descarregar arquivos grandes). Portanto, eles não deveriam estar relacionados ao bash ou às citações sofisticadas ... BTW, eu originalmente copiei esse comando final errado (corrigido agora) - mas a 00000000parte estava certa.
Gordon Davisson