Qual é a diferença entre colchetes duplos e simples no bash?

352

Acabei de me perguntar o que exatamente a diferença entre

[[ $STRING != foo ]]

e

[ $STRING != foo ]

é, além de que o último é posix-compliant, encontrado em sh e o primeiro é uma extensão encontrada em bash.

    
por 0x89 09.08.2009 / 23:11

5 respostas

265

Existem várias diferenças. Na minha opinião, alguns dos mais importantes são:

  1. [ é um Bash embutido e muitos outros shells modernos. O [ incorporado é semelhante a test com o requisito adicional de um ] final. Os membros incorporados [ e test imitam a funcionalidade /bin/[ e /bin/test , juntamente com suas limitações, para que os scripts sejam compatíveis com versões anteriores. Os executáveis originais ainda existem principalmente para compatibilidade com POSIX e compatibilidade com versões anteriores. A execução do comando type [ no Bash indica que [ é interpretado como interno, por padrão. (Nota: which [ procura apenas executáveis no PATH e é equivalente a type -p [ )
  2. [[ não é compatível, não necessariamente funcionará com o que /bin/sh apontar. Então, [[ é a opção mais moderna de Bash / Zsh / Ksh.
  3. Como [[ está embutido no shell e não possui requisitos legados, você não precisa se preocupar com a divisão de palavras com base na variável IFS para alterar as variáveis que avaliam para um string com espaços. Portanto, você não precisa colocar a variável entre aspas duplas.

Na maior parte, o resto é apenas uma sintaxe mais agradável. Para ver mais diferenças, recomendo este link para uma resposta de perguntas frequentes: Qual é a diferença entre o teste, [e [[? . De fato, se você é sério sobre scripts de bash, eu recomendo ler o wiki , incluindo o FAQ, Pitfalls e Guide. A seção de teste da seção de guia explica essas diferenças também, e porque o (s) autor (es) acha que [[ é uma escolha melhor se você não precisa se preocupar em ser tão portátil. As principais razões são:

  1. Você não precisa se preocupar em citar o lado esquerdo do teste para que ele seja lido como uma variável.
  2. Você não precisa escapar com% e menor que < > com barras invertidas para que eles não sejam avaliados como redirecionamento de entrada, o que pode realmente atrapalhar algumas coisas substituindo arquivos. Novamente, isso volta a [[ sendo um builtin. Se [(teste) for um programa externo, o shell teria que fazer uma exceção na forma como ele avalia < e > apenas se /bin/test estiver sendo chamado, o que não faria muito sentido.
por 09.08.2009 / 23:56
112

Em resumo:

[ is a bash Builtin

[[ ]] are bash Keywords

Palavras-chave: As palavras-chave são muito semelhantes às incorporadas, mas a principal diferença é que as regras de análise especiais se aplicam a elas. Por exemplo, [é um bash embutido, enquanto [[é uma palavra-chave bash. Ambos são usados para testar coisas, mas como [[é uma palavra-chave em vez de incorporada, ela se beneficia de algumas regras especiais de análise que facilitam muito:

  $ [ a < b ]
 -bash: b: No such file or directory
  $ [[ a < b ]]

O primeiro exemplo retorna um erro porque o bash tenta redirecionar o arquivo b para o comando [a]. O segundo exemplo realmente faz o que você espera. O personagem < não tem mais o seu significado especial de operador de Redirecionamento de Arquivos.

Fonte: link

    
por 29.12.2010 / 20:42
64

Diferenças de comportamento

Testado no Bash 4.3.11:

  • Extensão POSIX vs Bash:

  • comando regular vs magia

    • [ é apenas um comando normal com um nome estranho.

      ] é apenas um argumento de [ que impede que outros argumentos sejam usados.

      O Ubuntu 16.04 na verdade tem um executável para ele em /usr/bin/[ fornecido pelo coreutils, mas a versão integrada do bash tem precedência.

      Nada é alterado no modo como o Bash analisa o comando.

      Em particular, < é redirecionamento, && e || concatenam vários comandos, ( ) gera subshells, a menos que escape por \ , e a expansão de palavras ocorre normalmente.

    • [[ X ]] é uma única construção que faz com que X seja analisado magicamente. < , && , || e () são tratados especialmente e as regras de divisão de palavras são diferentes.

      Existem outras diferenças, como = e =~ .

    No Bashese: [ é um comando interno e [[ é uma palavra-chave: link

  • <

    • [[ a < b ]] : comparação lexicográfica
    • [ a \< b ] : o mesmo que acima. \ exigido ou então faz o redirecionamento como para qualquer outro comando. Extensão Bash.
    • Não consegui encontrar uma alternativa POSIX para isso, consulte: link
  • && e ||

    • [[ a = a && b = b ]] : verdadeiro, lógico e
    • [ a = a && b = b ] : erro de sintaxe, && analisado como um separador de comando AND cmd1 && cmd2
    • [ a = a -a b = b ] : equivalente, mas preterido por POSIX
    • [ a = a ] && [ b = b ] : recomendação POSIX
  • (

    • [[ (a = a || a = b) && a = b ]] : false
    • [ ( a = a ) ] : erro de sintaxe, () é interpretado como um subshell
    • [ \( a = a -o a = b \) -a a = b ] : equivalente, mas () foi preterido por POSIX
    • ([ a = a ] || [ a = b ]) && [ a = b ] recomendação POSIX
  • divisão de palavras

    • x='a b'; [[ $x = 'a b' ]] : true, aspas não são necessárias
    • x='a b'; [ $x = 'a b' ] : erro de sintaxe, expande para [ a b = 'a b' ]
    • x='a b'; [ "$x" = 'a b' ] : equivalente
  • =

    • [[ ab = a? ]] : true, porque faz correspondência de padrões ( * ? [ são mágicos) . Não glob expandir para arquivos no diretório atual.
    • [ ab = a? ] : a? glob expande. Então, pode ser verdadeiro ou falso, dependendo dos arquivos no diretório atual.
    • [ ab = a\? ] : false, não expansão glob
    • = e == são os mesmos em [ e [[ , mas == é uma extensão Bash.
    • printf 'ab' | grep -Eq 'a.' : equivalente a POSIX ERE
    • [[ ab =~ 'ab?' ]] : false, perde a magia com ''
    • [[ ab? =~ 'ab?' ]] : true
  • =~

Recomendação

Eu prefiro sempre usar [] .

Existem equivalentes POSIX para todas as construções [[ ]] que vi.

Se você usa [[ ]] you:

  • perder portabilidade
  • forçar o leitor a aprender as complexidades de outra extensão bash. [ é apenas um comando normal com um nome estranho, sem uma semântica especial.
por 12.07.2015 / 12:22
4

Com base em uma leitura rápida das seções relevantes da página man, a principal diferença parece ser que os operadores == e != correspondem a um padrão, em vez de uma sequência literal, e também que existe o% co_de operador de comparação% regex.

    
por 09.08.2009 / 23:17
3

Bracket único , ou seja, [] é compatível com shell POSIX para incluir uma expressão condicional.

Double Brackets , ou seja, [[]] é uma versão aprimorada (ou extensão) da versão POSIX padrão, isso é suportado pelo bash e outras shells (zsh, ksh).

No bash, para comparação numérica, usamos eq , ne , lt e gt , com colchetes duplos para comparação, podemos usar == , != , <, e > literalmente.

  • [ é um sinônimo para o comando de teste. Mesmo se estiver embutido no shell, ele cria um novo processo.
  • [[ é uma nova versão melhorada dele, que é uma palavra-chave, não um programa.

por exemplo:

[ var1 lt var2] #works
[ var1 < var2] #error: var2 No such file or directory 
[ var1 \< var2] #works with escape
[[ var1 < var2]] #works
    
por 08.02.2016 / 04:15