Qual é a diferença entre [[$ a == z *]] e [$ a == z *]?

35

Existe alguma diferença entre esses dois.

[[ $a == z* ]]

e

[ $a == z* ] 

Posso ter um exemplo em que eles teriam saídas diferentes?

Além disso, como o trabalho de [[ ]]difere [ ]?

munish
fonte

Respostas:

41

A diferença entre [[ … ]]e [ … ]é abordada principalmente no uso de suporte simples ou duplo - bash . Crucialmente, [[ … ]]é uma sintaxe especial, enquanto que [é um nome engraçado para um comando. [[ … ]]tem regras especiais de sintaxe para o que está dentro, [ … ]não.

Com as rugas adicionadas de um curinga, veja como [[ $a == z* ]]é avaliado:

  1. Analise o comando: esta é a [[ … ]]construção condicional em torno da expressão condicional $a == z*.
  2. Analise a expressão condicional: este é o ==operador binário, com os operandos $ae z*.
  3. Expanda o primeiro operando no valor da variável a.
  4. Avalie o ==operador: teste se o valor da variável acorresponde ao padrão z*.
  5. Avalie a expressão condicional: seu resultado é o resultado do operador condicional.
  6. O comando agora é avaliado, seu status é 0 se a expressão condicional for verdadeira e 1 se for falsa.

Veja como [ $a == z* ]é avaliado:

  1. Analisar o comando: este é o [comando com os argumentos formados por avaliar as palavras $a, ==, z*, ].
  2. Expanda $apara o valor da variável a.
  3. Execute a divisão de palavras e a geração de nome de arquivo nos parâmetros do comando.
    • Por exemplo, se o valor aé a seqüência de 6 caracteres foo b*(obtido por exemplo a='foo b*') e a lista de arquivos no diretório atual é ( bar, baz, qux, zim, zum), então o resultado da expansão é a lista de palavras seguintes: [, foo, bar, baz, ==, zim, zum, ].
  4. Execute o comando [com os parâmetros obtidos na etapa anterior.
    • Com os valores de exemplo acima, o [comando reclama de um erro de sintaxe e retorna o status 2.

Nota: Na [[ $a == z* ]]etapa 3, o valor de anão sofre divisão de palavras e geração de nome de arquivo, porque está em um contexto em que uma única palavra é esperada (o argumento à esquerda do operador condicional ==). Na maioria dos casos, se uma única palavra faz sentido nessa posição, a expansão variável se comporta como entre aspas duplas. No entanto, há uma exceção a essa regra: in [[ abc == $a ]], se o valor de acontiver curingas, aserá comparado com o padrão de curinga. Por exemplo, se o valor de afor, a*então [[ abc == $a ]]é verdadeiro (porque o curinga *proveniente da expansão não citada de $acorrespondências *) enquanto que [[ abc == "$a" ]]é falso (porque o caractere comum*provenientes da expansão citada de $anão corresponde bc). Dentro [[ … ]], aspas duplas não fazer a diferença, exceto no lado direito dos operadores cadeia correspondente ( =, ==, !=e =~).

Gilles 'SO- parar de ser mau'
fonte
39

[é um alias para o testcomando. A versão 6 do Unix possuía um ifcomando, mas a versão 7 (1979) veio com o novo shell Bourne que possuía algumas construções de programação, incluindo a construção if-then-else-elif-fi, e a versão 7 do Unix adicionou um testcomando que executava a maior parte do "testes" que foram executados pelo ifcomando em versões mais antigas.

[foi feito um alias para teste ambos foram criados no shell no Unix System III (1981) . Embora se deva observar que algumas variantes do Unix não possuíam um [comando até muito mais tarde ( até o início dos anos 2000 em alguns BSDs shbaseados no shell Almquist (um testbuilt-in sempre foi incluído no ashcódigo-fonte, mas naqueles BSDs foi inicialmente desativado)).

Observe que testaka [é um comando para fazer "testes"; não há atribuição que esse comando faça; portanto, não há razão para desambiguar entre um operador de atribuição e igualdade, portanto, o operador de igualdade é =. ==é suportado apenas por algumas implementações recentes de [(e é apenas um alias para =).

Como [nada mais é que um comando, ele é analisado da mesma maneira que qualquer outro comando do shell.

Especificamente, no seu exemplo, $aporque não é citado, seria dividido em várias palavras de acordo com as regras usuais de divisão de palavras, e cada palavra passaria pela geração do nome do arquivo, também conhecida como globbing, para resultar em possivelmente mais palavras, cada uma dessas palavras resultando em um argumento separado para o [comando.

Da mesma forma, z*seria expandido para a lista de nomes de arquivos no diretório atual começando com z.

Assim, por exemplo, se $aé b* = x, e há z1, z2, b1e b2arquivos no diretório atual, o [comando teria 9 argumentos: [, b1, b2, =, x, ==, z1, z2e ].

[analisa seus argumentos como uma expressão condicional. Esses 9 argumentos não somam uma expressão condicional válida, portanto, provavelmente retornaria um erro.

A [[ ... ]]construção foi introduzida pelo shell Korn provavelmente por volta de 1988, pois ksh86aem 1987 não o ksh88possuía desde o início.

Além do ksh (todas as implementações), [[...]]também é suportado pelo bash (desde a versão 2.02) e pelo zsh, mas as três implementações são diferentes e há diferenças entre cada versão de um mesmo shell, embora as alterações sejam geralmente compatíveis com versões anteriores (uma exceção notável é o bash =~operador conhecido por quebrar alguns scripts após uma determinada versão quando seu comportamento foi alterado). [[...]]não é especificado pelo POSIX, ou Unix ou Linux (LSB). Ele foi considerado para inclusão algumas vezes, mas não foi incluído, pois a funcionalidade comum suportada pelos shells principais já está coberta pelo [comando e pela case-in-esacconstrução.

Toda a [[ ... ]]construção compõe um comando. Ou seja, ele tem um status de saída (que é seu ativo mais importante, pois é o resultado da avaliação da expressão condicional), você pode canalizá-lo para outro comando (embora não seja útil) e geralmente usá-lo onde quiser use qualquer outro comando (somente dentro do shell, pois é uma construção de shell), mas não é analisado como um comando simples normal. O que está dentro é analisado pelo shell como uma expressão condicional e as regras usuais de divisão de palavras e geração de nome de arquivo se aplicam de maneira diferente.

[[ ... ]]conhece ==desde o início e é equivalente a =1 . Um erro do ksh (e está causando confusão e muitos bugs) é que o operador =e ==não é um operador de igualdade, mas um operador de correspondência de padrões (embora o aspecto de correspondência possa ser desativado com aspas, mas com regras pouco claras que diferem de shell para shell).

No código acima [[ $a == z* ]], o shell analisaria isso em vários tokens em regras semelhantes às usuais, reconhecendo-o como uma comparação de correspondência de padrões, tratado z*como um padrão para corresponder ao conteúdo da avariável.

Geralmente, é mais difícil dar um tiro no pé [[ ... ]]do que com o [comando. Mas algumas regras como

  • citar sempre variáveis
  • nunca use o operador -aor -o(use vários [comandos e os operadores &&e || shell )

Torne [confiável com os shells POSIX.

[[...]]em diferentes shells suportam operadores extras, como operadores de -ntcorrespondência regexp ... mas a lista e o comportamento variam de shell para shell e de versão para versão.

Portanto, a menos que você saiba com qual shell e versão mínima seu script será interpretado, provavelmente é mais seguro manter o [comando padrão .


1 Uma exceção: [[...]]foi adicionada ao bash na versão 2.02. Até 2.03onde foi alterado, [[ x = '?' ]]retornaria true enquanto [[ x == '?' ]]retornaria false. A citação não impediu a correspondência de padrões ao usar o =operador nessas versões, mas impediu ao usar ==.

Stéphane Chazelas
fonte
Ótima informação. Você poderia explicar por que "nunca use o operador -a ou -o"?
grebneke
@grebneke, tente [ '!' = foo -o a = a ]em bashpor exemplo.
Stéphane Chazelas
0

ambos são usados ​​para avaliar expressões e [[não funcionará com o shell de registro mais antigo POSIX e [[também suportam correspondência de padrões e regex. exemplo tente estes

[ $n -eq 0 -a $y -eq 0 ] && echo "Error" || echo "Ok"

[[ $n -eq 0 && $y -eq 0 ]] && echo "Error" || echo "Ok"

harish.venkat
fonte
Observe que o shell Bourne não é POSIX, [[...]]suporta regexps apenas em algumas versões de alguns shells, assim como algumas versões de [suportam a correspondência de regexp. Para efeito de comparação arithemtic, naqueles conchas que o apoio [[, você prefere escrever(( n == 0 && y == 0))
Stéphane Chazelas