Shell POSIX: `$` perde seu significado especial se for o último caractere de uma palavra?

17

Em cinzas, traço e festança, quando corro

$ echo ab$

retorna

ab$

Esse comportamento é especificado pelo POSIX ou é apenas uma convenção comum em shells compatíveis com POSIX? Não consegui encontrar nada na página Linguagem de Comando do Shell POSIX que mencione esse comportamento.

Harold Fischer
fonte
4
A melhor pergunta é: "Será que $ ganha um significado especial se for o último caractere de uma palavra?" Não existe um único significado especial atribuído a $; é usado para introduzir várias expansões, mas distintas, como expansão de parâmetro ${...}, substituição de comando $(...)e expressões aritméticas $((...)). Alguns shells introduzem contextos adicionais, como ksha variante de substituição de comando x=${ echo foo; echo bar;}(que difere do padrão $(...)por não executar os comandos em um subshell).
chepner 4/01
@chepner Você gostaria de avaliar a diferença de opinião entre Issac e Michael Homer? Suas respostas se contradizem explicitamente
Harold Fischer
1
Eu concordo com a interpretação de Michael Homer; o shell nem começa a se preocupar com expansões até que a análise seja concluída; portanto, na única palavra ab$, não há caracteres a seguir $, se foi "seguido" por um caractere nulo na cadeia de entrada original ou por um espaço em um caso como echo ab$ foo; o espaço em branco não citado original foi reconhecido e descartado após a análise.
chepner 6/01

Respostas:

10

$não tem um significado especial, por si só (TRY echo $), apenas quando combinado com outro carácter depois e formando uma expansão, por exemplo $var(ou ${var}), $(util), $((1+2)).

O $recebe seu significado "especial" como definir uma expansão no padrão POSIX na seção Reconhecimento de token :

Se o caractere atual for um não citado $ou `, o shell deve identificar o início de qualquer candidato para expansão de parâmetro, substituição de comando ou expansão aritmética a partir de suas seqüências de caracteres não citadas introdutórias: $ou ${, $(ou `, e $((, respectivamente. O invólucro deve ler entradas suficientes para determinar o final da unidade a ser expandida ( como explicado nas seções citadas) Ao processar os caracteres, se instâncias de expansões ou citações forem encontradas aninhadas na substituição, o shell deverá processá-las recursivamente da maneira especificada para a construção encontrada. Os caracteres encontrados desde o início da substituição até o fim, permitindo qualquer recursão necessária para reconhecer construções incorporadas, devem ser incluídos não modificados no token de resultado, incluindo quaisquer operadores ou aspas de substituição incorporados ou anexos. O token não deve ser delimitado até o final da substituição.

Portanto, se $não formar uma expansão, outras regras de análise entrarão em vigor:

Se o caractere anterior fazia parte de uma palavra, o caractere atual deve ser anexado a essa palavra.

Isso cobre sua ab$corda.

No caso de um solitário $(a "nova palavra" seria a $mesma):

O caractere atual é usado como o início de uma nova palavra.

O significado da palavra gerada que contém uma $que não é uma expansão padrão é explicitamente definida como não especificada pelo POSIX.

Observe também que esse $é o último caractere $$, mas também é a variável que mantém o PID do shell atual. Em bash, !$pode invocar uma expansão do histórico (o último argumento do comando anterior). Portanto, em geral, não, $não deixa de ter significado no final de uma palavra não citada, mas no final de uma palavra pelo menos não indica uma expansão padrão.

Kusalananda
fonte
7

Dependendo da situação exata, isso é explicitamente não especificado (portanto, as implementações podem fazer o que quiserem) ou deve acontecer como você observou. No seu cenário exato echo ab$, o POSIX determina a saída "ab $" que você observou e não é especificada . Um resumo rápido de todos os casos diferentes está no final.

Existem dois elementos: primeiro colocar token em palavras e depois interpretá-las.


Tokenização

A tokenização POSIX exige que uma $que não seja o início de uma expansão de parâmetro válida , substituição de comando ou substituição aritmética seja considerada uma parte literal do WORDtoken que está sendo construído. Isso ocorre porque a regra 5 ("Se o caractere atual é um não citado $ou `, o shell deve identificar o início de qualquer candidato para expansão de parâmetro, substituição de comando ou expansão aritmética de suas sequências de caracteres introdutórias não citadas: $ou ${, $(ou `, e $((, respectivamente") ) não se aplica, pois nenhuma dessas expansões é viável lá. A expansão de parâmetros requer que um nome válido apareça lá e um nome vazio não é válido.

Como essa regra não se aplica, continuamos a seguir até encontrar uma que o aplique. Os dois candidatos são o número 8 ("Se o caractere anterior fazia parte de uma palavra, o caractere atual será anexado a essa palavra.") E o número 10 ("O caractere atual é usado como o início de uma nova palavra.") , que se aplicam a echo a$e echo $respectivamente.

Há também um terceiro caso do formulário echo a$+bque se enquadra na mesma rachadura, já que +não é o nome de um parâmetro especial. Voltaremos a este, pois desencadeia diferentes partes das regras.

A especificação exige, portanto, que $seja considerada uma parte da palavra sintaticamente, e pode ser processada posteriormente posteriormente.


Expansão de palavras

Após a entrada ter sido analisada dessa maneira, com o $incluído na palavra, expansões de palavras são aplicadas a cada uma das palavras que foram lidas. Cada palavra é processada individualmente .

É especificado que :

Se um '$' não citado for seguido por um caractere que não seja um dos seguintes:

  • Um caractere numérico
  • O nome de um dos parâmetros especiais (consulte Parâmetros especiais )
  • Um primeiro caractere válido de um nome de variável
  • A <left-curly-bracket>('{')
  • UMA <left-parenthesis>

o resultado não é especificado.

"Não especificado" é um termo específico aqui, significando que

  1. Um shell em conformidade pode escolher qualquer comportamento neste caso
  2. Um aplicativo em conformidade não pode confiar em nenhum comportamento específico

No seu exemplo, echo ab$the $ não é seguido por nenhum caractere ; portanto, esta regra não se aplica e o resultado não especificado não é chamado. Simplesmente não há expansão incitada pelo $, portanto está literalmente presente e impressa.

Onde ele iria aplicar está em nosso terceiro caso de cima: echo a$+b. Aqui $é seguido por +, o que não é um número, parâmetro especial ( @, *, #, ?, -, $, !, ou 0), comece de uma variável nome (sublinhado ou um alfabética a partir do conjunto de caracteres portáteis ), ou um dos suportes. Nesse caso, o comportamento não é especificado: é permitido que+ um shell em conformidade invente um parâmetro especial chamado expansão , e um aplicativo em conformidade não deve assumir que o shell não o faça . O shell também poderia fazer qualquer outra coisa que gostasse, incluindo relatar um erro.

Por exemplo, zsh, inclusive em seu modo POSIX, interpreta $+bcomo " bconjunto de variáveis " e substitui 1 ou 0 em seu lugar. Da mesma forma, possui extensões para ~e =. Este é um comportamento conforme.

Outro lugar em que isso poderia acontecer é echo "a$ b". Novamente, o shell pode fazer o que quiser, e você, como autor do script, deve escapar do $se desejar saída literal. Caso contrário, pode funcionar, mas você não pode confiar nela. Esta é a letra absoluta da especificação, mas não acho que esse tipo de granularidade tenha sido planejado ou considerado.


Em suma

  • echo ab$: saída literal, totalmente especificado
  • echo a$ b: saída literal, totalmente especificado
  • echo a$ b$: saída literal, totalmente especificado
  • echo a$b: expansão do parâmetro b, totalmente especificado
  • echo a$-b: expansão de parâmetro especial -, totalmente especificado
  • echo a$+b: comportamento não especificado
  • echo "a$ b": comportamento não especificado

No $final de uma palavra, você tem permissão para confiar no comportamento e ele deve ser tratado literalmente e transmitido ao echocomando como parte de seu argumento. Esse é um requisito de conformidade no shell.

Michael Homer
fonte
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
terdon
@MichaelHomer echo $Também seria literal e totalmente especificado?
Harold Fischer
@HaroldFischer Yes
Michael Homer
Onde caem echo "$"e echo "a b$"caem?
Harold Fischer