Diferença entre colchetes simples e duplos no Bash

161

Estou lendo exemplos do bash, ifmas alguns exemplos são escritos com colchetes simples:

if [ -f $param ]
then
  #...
fi

outros com colchetes duplos:

if [[ $? -ne 0 ]]
then
    start looking for errors in yourlog
fi

Qual é a diferença?

rkmax
fonte
3
Você pode obter sua resposta consultando a resposta desta pergunta: unix.stackexchange.com/questions/3831/…
Amir Naghizadeh

Respostas:

188

Simples []são os testes de condições compatíveis com o posix shell.

Double [[]]é uma extensão do padrão []e é suportado pelo bash e outros shells (por exemplo, zsh, ksh). Eles suportam operações extras (bem como as operações posix padrão). Por exemplo: em ||vez de -oe regex combinando com =~. Uma lista completa de diferenças pode ser encontrada na seção manual do bash sobre construções condicionais .

Use []sempre que quiser que seu script seja portável através de shells. Use [[]]se você quiser expressões condicionais não suportadas []e não precisar ser portátil.

cmh
fonte
6
Eu acrescentaria que, se o seu script não começar com um shebang que solicita explicitamente um shell que suporte [[ ]](por exemplo, bash com #!/bin/bashou #!/usr/bin/env bash), você deve usar a opção portable. Os scripts que assumem que o / bin / sh suporta extensões como essa serão interrompidos em sistemas operacionais como os lançamentos recentes do Debian e Ubuntu, onde esse não é o caso.
Gordon Davisson
87

Diferenças de comportamento

Testado no Bash 4.3.11:

  • Extensão POSIX vs Bash:

  • comando regular vs magia

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

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

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

      Nada é alterado na maneira como Bash analisa o comando.

      Em particular, <é o redirecionamento &&e ||concatena vários comandos, ( )gera sub-conchas, a menos que seja escapado \, e a expansão de palavras acontece normalmente.

    • [[ X ]]é uma construção única que faz com que Xseja analisado magicamente. <, &&, ||E() são tratados de maneira especial, e regras de decomposição palavra são diferentes.

      Existem também outras diferenças como =e =~.

      No Bashese: [é um comando interno e [[é uma palavra-chave: /ubuntu/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && e ||

    • [[ a = a && b = b ]]: verdadeiro, lógico e
    • [ a = a && b = b ]: erro de sintaxe, &&analisado como um separador de comandos ANDcmd1 && cmd2
    • [ a = a -a b = b ]: equivalente, mas reprovado pelo POSIX³
    • [ a = a ] && [ b = b ]: POSIX e equivalente confiável
  • (

    • [[ (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 descontinuado pelo POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX equivalente 5
  • divisão de palavras e geração de nome de arquivo após expansões (divisão + glob)

    • x='a b'; [[ $x = 'a b' ]]: true, aspas não necessárias
    • x='a b'; [ $x = 'a b' ]: erro de sintaxe, expande para [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: erro de sintaxe se houver mais de um arquivo no diretório atual.
    • x='a b'; [ "$x" = 'a b' ]: Equivalente POSIX
  • =

    • [[ ab = a? ]]: true, porque faz a correspondência de padrões ( * ? [são mágicos). Não é expandido para arquivos no diretório atual.
    • [ ab = a? ]: a?glob se expande. Portanto, pode ser verdadeiro ou falso, dependendo dos arquivos no diretório atual.
    • [ ab = a\? ]: expansão falsa, não global
    • =e ==são iguais em ambos [e [[, mas ==é uma extensão do Bash.
    • case ab in (a?) echo match; esac: Equivalente POSIX
    • [[ ab =~ 'ab?' ]]: falso 4 , perde mágica com''
    • [[ ab? =~ 'ab?' ]]: verdade
  • =~

    • [[ ab =~ ab? ]]: true, correspondência de expressão regular estendida POSIX , ?não expande glob
    • [ a =~ a ]: erro de sintaxe. Nenhum equivalente do bash.
    • printf 'ab\n' | grep -Eq 'ab?': Equivalente ao POSIX (apenas dados de linha única)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': Equivalente POSIX.

Recomendação : use sempre [].

Existem equivalentes POSIX para cada [[ ]]construção que eu já vi.

Se você usa [[ ]]você:

  • perder portabilidade
  • forçar o leitor a aprender os meandros de outra extensão do bash. [é apenas um comando regular com um nome estranho, sem semântica especial envolvida.

¹ Inspirado na [[...]]construção equivalente no shell Korn

² mas falha para alguns valores de aou b(como +ou index) e faz comparação numérica se ae se bparecem com números inteiros decimais. expr "x$a" '<' "x$b"trabalha em torno de ambos.

³ e também falha em alguns valores de aou bcomo !ou (.

4 no bash 3.2 e superior e a compatibilidade fornecida com o bash 3.1 não está ativada (como em BASH_COMPAT=3.1)

5 embora o agrupamento (aqui com o {...;}grupo de comandos em vez do (...)qual executaria um subshell desnecessário) não seja necessário, pois os operadores ||e &&shell (em oposição aos operadores ||e && [[...]]ou -o/ -a [operadores) têm igual precedência. Então [ a = a ] || [ a = b ] && [ a = b ]seria equivalente.

Ciro Santilli adicionou uma nova foto
fonte
3
" Existem equivalentes POSIX para cada construção [[]] que eu já vi. " O mesmo pode ser dito de qualquer linguagem Turing Complete na face do planeta.
A. Rick
3
@ A.Rick que seria uma resposta válida para todas as perguntas sobre "Como fazer X no idioma Y" :-) É claro que há uma "conveniente" implícita nessa declaração.
Ciro Santilli escreveu
6
Resumo fantástico. Obrigado pelo esforço. No entanto, discordo da recomendação. É portabilidade versus sintaxe mais consistente e poderosa. Se você pode exigir o bash> 4 em seus ambientes, a sintaxe [[]] é recomendada.
Bernard
@Downvoters, por favor, explique para que eu possa aprender e melhorar as informações :-)
Ciro Santilli 法轮功郝海东 法轮功 病
8
Ótima resposta, mas acho que a Recomendação : use sempre[] deve ser lida como Minha preferência : use []se você não quiser perder a portabilidade . Conforme declarado aqui : Se a portabilidade / conformidade com o POSIX ou o BourneShell for uma preocupação, a sintaxe antiga deve ser usada. Se, por outro lado, o script exigir BASH, Zsh ou KornShell, a nova sintaxe geralmente será mais flexível, mas não necessariamente compatível com versões anteriores. Eu prefiro ir com [[ ab =~ ab? ]]se eu puder e não têm preocupação com compatibilidade com versões anteriores do queprintf 'ab' | grep -Eq 'ab?'
MauricioRobayo
14

Entre parênteses simples para teste de condição (por exemplo, [...]), alguns operadores, como single, =são suportados por todas as conchas, enquanto o uso do operador ==não é suportado por algumas conchas mais antigas.

Dentro de colchetes duplos para teste de condição (ou seja, [...]), não há diferença entre usar =ou ==em cascas velhas ou novas.

Editar: Devo também observar que: No bash, sempre use colchetes [...] duplos, se possível, porque é mais seguro que colchetes simples. Ilustrarei o porquê com o seguinte exemplo:

if [ $var == "hello" ]; then

se $ var estiver nulo / vazio, é isso que o script vê:

if [ == "hello" ]; then

o que quebrará seu script. A solução é usar colchetes duplos ou sempre lembrar de colocar aspas em torno de suas variáveis ​​( "$var"). Parênteses duplos são melhores práticas de codificação defensiva.

sampson-chen
fonte
1
Colocar aspas em todas as leituras de variáveis, a menos que você tenha uma boa razão para não fazê-lo, é uma prática de codificação defensiva muito melhor, pois se aplica a todas as leituras de variáveis, não apenas às condições. Um bug do instalador do iTunes uma vez excluía os arquivos das pessoas se o nome do disco rígido contivesse espaços (ou algo parecido). Também resolve o problema que você mencionou.
Chai T. Rex
1

você pode usar colchetes duplos para correspondência de expressões regulares leves, por exemplo:

if [[ $1 =~ "foo.*bar" ]] ; then

(desde que a versão do bash que você está usando suporte esta sintaxe)

asf107
fonte
6
Exceto que você citou o padrão, agora ele é tratado como uma sequência literal.
Ormaaj 24/11/12
muito verdadeiro. às vezes isso irrita-me :)
asf107
1

O manual do Bash diz:

Quando usados ​​com [[, os operadores '<' e '>') classificam lexicograficamente usando o código do idioma atual. O comando test usa pedidos ASCII.

(O comando test é idêntico a [])

user5500105
fonte