Expansão automática de variáveis ​​dentro do comando bash [[]]

13

Ao cancelar a referência de uma variável bash, você deve usar $sign. No entanto, parece que o seguinte está funcionando bem:

x=5
[[ x -gt 2 ]]

Alguém pode explicar isso?

Editar: (mais informações)

O que quero dizer é como e por que o comando [[]] está desreferenciando minha variável x sem o sinal $. E sim, se x = 1, a declaração é avaliada como falsa (status de retorno 1)

Hóspede
fonte
2
O que você quer dizer com "funcionando bem"? E sua avaliação muda se você for x=1seguido por [[ x -gt 2]]?
nohillside
Quero dizer: como e por que o comando [[]] está desreferenciando minha variável x sem o sinal $. E sim, se x = 1, a declaração é falsa (retorne o status 1)
Convidado

Respostas:

9

A razão é que as -eqforças fazem uma avaliação aritmética dos argumentos.

Um operador aritmético: -eq, -gt, -lt, -ge, -lee -nedentro de um [[ ]](em ksh, zsh e bash) meios para expandir automaticamente nomes de variáveis como na linguagem C, não precisa de um líder $.

  • Para confirmação, precisamos examinar o código-fonte do bash. O manual não oferece confirmação direta .

    Dentro test.cdo processamento de operadores aritméticos se enquadram nesta função:

    arithcomp (s, t, op, flags)

    Onde se tsão ambos operandos. Os operandos são entregues a esta função:

    l = evalexp (s, &expok);
    r = evalexp (t, &expok);

    A função evalexpé definida dentro expr.c, que possui este cabeçalho:

    /* expr.c -- arithmetic expression evaluation. */

    Portanto, sim, os dois lados de um operador aritmético caem (diretamente) na avaliação da expressão aritmética. Diretamente, sem buts, sem ifs.


Na prática, com:

 $ x=3

Ambos falham:

 $ [[ x = 4 ]] && echo yes || echo no
 no

 $ [[ x = 3 ]] && echo yes || echo no
 no

O que está correto, xnão está sendo expandido e xnão é igual a um número.

Contudo:

 $ [[ x -eq 3 ]] && echo yes || echo no
 yes

 $ [[ x -eq 4 ]] && echo yes || echo no
 no

A variável denominada xé expandida (mesmo sem um $).

Isso não acontece […]no a zsh ou no bash (no ksh).


É o mesmo que acontece dentro de um $((…)):

 $ echo $(( x + 7 ))
 10

E, por favor, entenda que isso é (muito) recursivo (exceto em hífen):

 $ a=b b=c c=d d=e e=f f=3
 $ echo "$(( a + 7 ))" 
 10

A 😮

E bastante arriscado:

 $ x='a[$(date -u)]'
 $ [[ x -eq 3 ]] && echo yes || echo no
 bash: Tue Dec  3 23:18:19 UTC 2018: syntax error in expression (error token is "Dec  3 23:18:19 UTC 2018")

O erro de sintaxe pode ser facilmente evitado:

 $ a=3; x='a[$(date -u >/dev/tty; echo 0)]'

 $ [[ x -eq 3 ]] && echo yes || echo no
 Tue Dec  4 09:02:06 UTC 2018
 yes

Como diz o ditado: higienize sua entrada

 $ [[ ${x//[^0-9]} -eq 3 ]] && echo yes || echo no
 no

fim de 😮


O externo (mais antigo) /usr/bin/test(não o interno test) e o ainda mais antigo e também o externo exprnão expandem expressões apenas números inteiros (e, aparentemente, apenas números inteiros decimais):

 $ /usr/bin/test "x" -eq 3
 /usr/bin/test: invalid integer x

 $ expr x + 3
 expr: non-integer argument
Isaac
fonte
Interessante. Não é difícil dizer como isso é possível - sendo [[uma palavra-chave, operadores e operandos são detectados quando o comando é lido e não após a expansão. Assim, [[pode tratar -eqde uma maneira mais inteligente do que, digamos [,. Mas o que me pergunto é: onde podemos encontrar documentação sobre o bash lógico usado para interpretar comandos compostos? Não parece muito óbvio para mim e, aparentemente, não consigo encontrar explicações satisfatórias em manou info bash.
fra-san
O Bash não documenta isso em nenhum lugar que eu possa encontrar. Há um tipo de descrição no man ksh93 : As seguintes comparações aritméticas obsoletas também são permitidas: exp1-eq exp2 . Há este texto na testsecção de homem zshbuiltins operadores aritméticos esperar inteiro argumentos em vez de expressões aritméticas . O que confirma que alguns argumentos são tratados como expressões aritméticas pelo teste incorporado sob condições não especificadas nesta citação. Eu vou confirmar com o código fonte .... ...
Isaac
7

Os operandos das comparações numéricas -eq, -gt, -lt, -ge, -lee -nesão tomados como expressões aritméticas. Com algumas limitações, eles ainda precisam ser palavras de casca simples.

O comportamento dos nomes de variáveis ​​na expressão aritmética é descrito em Aritmética do Shell :

Variáveis ​​de shell são permitidas como operandos; a expansão do parâmetro é executada antes da expressão ser avaliada. Dentro de uma expressão, variáveis ​​de shell também podem ser referenciadas por nome sem usar a sintaxe de expansão de parâmetro. Uma variável de shell que é nula ou não definida é avaliada como 0 quando referenciada pelo nome sem usar a sintaxe de expansão do parâmetro.

e também:

O valor de uma variável é avaliado como uma expressão aritmética quando é referenciada

Mas não consigo encontrar a parte da documentação em que se diz que as comparações numéricas usam expressões aritméticas. Não é descrito em Construções condicionais em [[, nem em Expressões condicionais de bash .

Mas, por experimento, parece funcionar como dito acima.

Então, coisas assim funcionam:

a=6
[[ a -eq 6 ]] && echo y 
[[ 1+2+3 -eq 6 ]] && echo y
[[ "1 + 2 + 3" -eq 6 ]] && echo y

isso também (o valor da variável é avaliado):

b='1 + 2 + 3'
[[ b -eq 6 ]] && echo y

Mas isso não acontece; não é uma única palavra de shell quando a [[ .. ]]análise é analisada; portanto, há um erro de sintaxe na condicional:

[[ 1 + 2 + 3 -eq 6 ]] && echo y

Em outros contextos aritméticos, não há necessidade de a expressão ficar sem espaço em branco. Isso é impresso 999, pois os colchetes delimitam inequivocamente a expressão aritmética no índice:

a[6]=999; echo ${a[1 + 2 + 3]}

Por outro lado, a =comparação é uma correspondência de padrões e não envolve aritmética, nem a expansão automática de variáveis ​​feita em um contexto aritmético (Construções Condicionais):

Quando os operadores ==e !=são usados, a cadeia à direita do operador é considerada um padrão e é correspondida de acordo com as regras descritas abaixo em Correspondência de padrões, como se a opção de shell extglob estivesse ativada. O =operador é idêntico a ==.

Portanto, isso é falso, pois as strings são obviamente diferentes:

[[ "1 + 2 + 3" = 6 ]] 

como é isso, mesmo que os valores numéricos sejam os mesmos:

[[ 6 = 06 ]] 

e aqui também as comparações ( xe 6) são diferentes:

x=6
[[ x = 6 ]]

Isso expandiria a variável, no entanto, então isso é verdade:

x=6
[[ $x = 6 ]]
ilkkachu
fonte
Na verdade, não é possível encontrar a parte da documentação em que se diz que as comparações numéricas usam expressões aritméticas. A confirmação está no código .
Isaac
O mais próximo é que a descrição arg1 OP arg2diz que os argumentos podem ser números inteiros positivos ou negativos, o que suponho que implica que eles sejam tratados como expressões aritméticas. Confusamente, isso também implica que eles não podem ser zero. :)
Barmar
@ Barmar, ehh, certo. Mas isso também se aplica às comparações numéricas [e não existem expressões aritméticas. Em vez disso, o Bash reclama de não-números inteiros.
Ilkkachu
@ilkkachu [é um comando externo, não tem acesso a variáveis ​​de shell. Muitas vezes, é otimizado com um comando interno, mas ainda se comporta da mesma maneira.
Barmar
@ Barmar, o que eu quis dizer foi que a frase "Arg1 e arg2 pode ser um número inteiro positivo ou negativo". aparece em Expressões condicionais do bash e essa lista se aplica a [apenas como [[. Mesmo com [, os operandos -eqe amigos são / precisam ser inteiros, de modo que a descrição também se aplica. Tomando "deve ser um número inteiro" para significar "são interpretados como expressões aritméticas" não se aplica nos dois casos. (Provavelmente, pelo menos em parte devido a [agir como um comando comum, como você diz.)
ilkkachu
1

Sim, sua observação está correta, a expansão de variáveis ​​é realizada em expressões entre colchetes duplos [[ ]], para que você não precise colocar $na frente de um nome de variável.

Isso é explicitamente declarado no bashmanual:

[expressão]

(...) A divisão de palavras e a expansão do nome do caminho não são executadas nas palavras entre [[e]]; expansão de til, expansão de parâmetro e variável, expansão aritmética, substituição de comando, substituição de processo e remoção de cotação.

Observe que este não é o caso da versão de colchete único [ ], pois [não é uma palavra-chave do shell (sintaxe), mas sim um comando (no bash está embutido, outros shells podem usar externo, alinhado para testar).

jimmij
fonte
1
Obrigado por responder. Parece que isso está funcionando apenas para números. x = cidade [[$ x == cidade]] Isso não funciona sem o sinal de $.
Visitante
3
Parece que há mais aqui: (x=1; [[ $x = 1 ]]; echo $?)retornos 0, (x=1; [[ x = 1 ]]; echo $?)retornos 1, ou seja, a expansão dos parâmetros não é executada xquando comparamos as strings. Esse comportamento parece uma avaliação aritmética desencadeada por expansão aritmética, ou seja, o que acontece em (x=1; echo $((x+1))). (Sobre a avaliação aritmética, man bashafirma que "dentro de uma expressão, variáveis do shell também podem ser referenciados pelo nome sem usar a expansão de parâmetros de sintaxe).
fra-san
@ fra-san De fato, como o -gtoperador espera um número, então toda a expressão é reavaliada como se estivesse dentro (()), por outro lado, ==espera seqüências de caracteres para que a função de correspondência de padrões seja acionada. Eu não procurei no código fonte, mas parece razoável.
jimmij
[é um shell embutido no bash.
Nizam Mohamed
1
@NizamMohamed É um builtin, mas ainda não é uma palavra-chave.
Kusalananda