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

427

Eu só queria saber qual é exatamente a diferença entre

[[ $STRING != foo ]]

e

[ $STRING != foo ]

Além disso, o último é compatível com posix, encontrado em sh e o primeiro é uma extensão encontrada no bash.

0x89
fonte
1
Caso você também esteja se perguntando sobre o uso de colchetes, por exemplo, no contexto de uma ifdeclaração, consulte mywiki.wooledge.org/BashPitfalls#if_.5Bgrep_foo_myfile.5D
Kev
também, dos documentos do Ubuntu: wiki.ubuntu.com/…
radistao 27/02

Respostas:

310

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

  1. [é um builtin no Bash e em muitas outras conchas modernas. O builtin [é semelhante ao testrequisito adicional de um fechamento ]. Os componentes internos [e testimitam a funcionalidade /bin/[e /bin/testsuas limitações para que os scripts sejam compatíveis com versões anteriores. Os executáveis ​​originais ainda existem principalmente para conformidade com POSIX e compatibilidade com versões anteriores. A execução do comando type [no Bash indica que [é interpretado como um interno por padrão. (Nota: which [procura apenas executáveis ​​no PATH e é equivalente a type -p [)
  2. [[não é tão compatível, não funcionará necessariamente com o que quer que /bin/shaponte. Assim [[é a opção Bash / Zsh / Ksh mais moderna.
  3. Como [[está embutido no shell e não possui requisitos herdados, não é necessário se preocupar com a divisão de palavras com base na variável IFS para atrapalhar as variáveis ​​avaliadas para uma sequência com espaços. Portanto, você realmente não precisa colocar a variável entre aspas duplas.

Na maior parte, o resto é apenas uma sintaxe melhor. Para ver mais diferenças, recomendo este link para uma resposta da FAQ: Qual é a diferença entre test, [e [[? . De fato, se você é sério sobre o bash scripting, recomendo a leitura de todo o wiki , incluindo as Perguntas frequentes, Armadilhas e Guia. A seção de teste da seção do guia também explica essas diferenças e por que o (s) autor (es) pensam que [[é uma escolha melhor se você não precisa se preocupar em ser tão portátil. Os principais motivos 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 menor e maior que < >com barras invertidas para que não sejam avaliadas como redirecionamento de entrada, o que pode realmente atrapalhar algumas coisas substituindo arquivos. Isso novamente volta a [[ser um builtin. Se [(test) for um programa externo, o shell precisaria abrir uma exceção na maneira como avalia <e >apenas se /bin/testestiver sendo chamado, o que não faria sentido.
Kyle Brandt
fonte
5
Obrigado, o link para o FAQ do bash era o que eu estava procurando (não sabia sobre essa página, obrigado).
0x89
2
Editei sua postagem com essas informações, mas [e teste são executados como internos. Os componentes internos foram projetados para substituir / bin / [e / bin / test, mas também eram necessários para reproduzir as limitações dos binários. O comando 'type [' verifica se o builtin é usado. 'que [' só procura para executáveis no caminho e é equivalente a 'tipo -P ['
klynch
133

Em resumo:

[é uma festa embutida

[[]] são bash Palavras-chave

Palavras-chave: as palavras- chave são muito parecidas com os built-in, mas a principal diferença é que regras especiais de análise se aplicam a elas. Por exemplo, [é um bash integrado, enquanto [[é uma palavra-chave do bash. Ambos são usados ​​para testar coisas, mas como [[é uma palavra-chave e não um builtin, ela se beneficia de algumas regras de análise especiais 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, na verdade, faz o que você espera. O caractere <não tem mais seu significado especial de operador Redirecionamento de arquivo.

Fonte: http://mywiki.wooledge.org/BashGuide/CommandsAndArguments

abhiomkar
fonte
3
[é um comando shell POSIX; ele não precisa ser incorporado. ]é apenas um argumento que esse comando procura, para que a sintaxe seja equilibrada. O comando é um sinônimo, testexceto que testnão procura um fechamento ].
Kaz
81

Diferenças de comportamento

Algumas diferenças 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, possui 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: https://askubuntu.com/questions/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?' ]]: verdadeiro
  • =~

    • [[ 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 para 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 de (...)executar 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
Como usar printf 'ab' | grep -Eq 'ab?'dentro if [ … ]?
MdDamian #
1
@meeDamian if ( printf 'ab' | grep -Eq 'a' ); then echo 'a'; fi. []é um comando exatamente como grep. O ()pode não ser necessário em que comando não tenho certeza: eu adicionei-o por causa do |, depende de como Bash analisa as coisas. Se não houver |, tenho certeza que você pode escrever apenas if cmd arg arg; then.
Ciro Santilli publicou
1
@meeDamian sim, não há necessidade de ()que parece: stackoverflow.com/questions/8965509/...
Ciro Santilli新疆改造中心法轮功六四事件
1
Boa lista! Veja também: wiki.ubuntu.com/…
radistao 27/02
5

Suporte simples, isto []é, é compatível com o shell POSIX para incluir uma expressão condicional.

Parênteses duplos, isto [[]]é, é uma versão aprimorada (ou extensão) da versão POSIX padrão; isso é suportado pelo bash e outros shells (zsh, ksh).

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

  • [é sinônimo de comando de teste. Mesmo que esteja embutido no shell, ele cria um novo processo.
  • [[ é uma nova versão aprimorada, 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
Premraj
fonte
4

Com base em uma leitura rápida das seções relevantes da página de manual, a principal diferença parece ser que os operadores ==e !=correspondem a um padrão, em vez de uma string literal, e também ao =~operador de comparação de expressões regulares.

mulher
fonte