Diferença entre [[ ]] AND [ ] ou (( )) AND ( ) no Bash

Diferença entre [[ ]] AND [ ] ou (( )) AND ( ) no Bash

Qual é a diferença entre usar [[ condition ]]e [ condition ]ou (( condition ))e ( condition )? Em que cenário precisamos usar qualquer um deles?

  • (( 10 > 9 ))funciona mas (( 10 -gt 9 ))não
  • [[ 10 -gt 9 ]]funciona mas [[ 10 > 9 ]]não

Responder1

((...))é da cascaaritméticaconstruir. Os operadores que você pode usar estão documentados no manual:6.5 Aritmética de Casca

(...)é umagrupamentoconstrução que executa os comandos contidos em um subshell:3.2.4.3 Comandos de agrupamento

[...]é a construção condicional "herdada". A documentação está em6.4 Expressões Condicionais Bash

[[...]]faz tudo o que [...]faz. A diferença é que a divisão de palavras e a expansão glob não são realizadas para variáveis ​​internas, [[...]]portanto, citar as variáveis ​​não é tão crucial. Além disso, [[pode fazercorrespondência de padrõescom o ==operador ecorrespondência de expressão regularcom o =~operador.

A razão [[ 10 > 9 ]]pela qual você obtém um resultado inesperado é que o >operador interno [[...]]é paracomparação de stringse a string "10" é "menor que" a string "9".

Responder2

Sobre ((…))e(…)

Como afirmado emesta outra resposta, ((…))é a construção aritmética do shell (umbashismo) e (…)executa comando(s) em um subshell. Eles são muito diferentes entre si e de [ … ]ou [[ … ]].

Por outro lado [ … ]e [[ … ]]são muito semelhantes em casos simples; eles podem estar confusos. O restante desta resposta concentra-se em [ … ]e [[ … ]].


Nota formal

Meus objetivos aqui são:

  • para elaborardiferenças e semelhanças entre [ … ]e[[ … ]];
  • para fornecer uma resposta canônica no assunto [ … ]vs [[ … ]], para que perguntas que podem ser respondidas por " [is not [[" possam ser vinculadas aqui;
  • para fornecer uma resposta suficientemente exaustiva, para que os usuários não fiquem mais tentados a postar respostas que se concentrem em uma única diferença cada.

O que é [?

  1. [realiza um teste.

    O objetivo [é testar algo (por exemplo, se algum arquivo existir) e retornar o status de saída zero se o teste for bem-sucedido, diferente de zero caso contrário.

  2. [é um comando.

    [é um nome de comando como catou echo. [(como echo) é um recurso interno do Bash, mas isso significa apenas que pode ser executado sem gerar um processo adicional, mas ainda se comporta como um comando. Provavelmente existe um [executável independente em seu sistema (por exemplo /bin/[); tente type -a [no Bash para descobrir.

  3. [requer um espaço depois.

    Como um comando, [recebe argumentos. Como acontece com qualquer outro comando, os argumentos devem ser separados uns dos outros e do nome do comando. [fooé um nome de comando diferente, não é equivalente a [ foo(da mesma forma e mais obviamente echoHello worldnão é echo Hello world). Você certamente precisa de um espaço (ou tabulação) depois de [.

  4. [requer ].

    [requer ]como último argumento, caso contrário, ele se recusa a funcionar. Isso serve apenas para [ … ]se destacar como um bloco, como se fosse alguma sintaxe especial; mas não é uma sintaxe especial. Para passar ]como último argumento você precisa de um espaço (ou tabulação) antes dele. Para comparação: o último argumento [ … bar]é bar]which is not ], portanto [ … bar]não é um comando válido.

  5. [é (quase) parecido test.

    [ … ]é equivalente a test …. Em outras palavras, você pode converter qualquer [comando em testcomando removendo ]e alterando o nome do comando. E você pode converter qualquer testcomando em [comando alterando o nome do comando e adicionando ].

  6. [não é especial.

    É bom lembrar [que não é especial, é apenas um nome um tanto sofisticado para um comando. Se você reconhecer isso, não ficará surpreso com o fato de quetodosa análise e o processamento do shell fazemantesexecutar um comando ( testou echo, lsetc.) também acontece se o comando for [. Em particular:

    • [não sabe se você citou algum de seus argumentos, ele obtém argumentos depois que o shell remove as aspas.

    • Sem aspas e sem escape &&ou ||entre [e ]não é interpretado como um argumento para [. Em outras palavras, [ … || … ]é interpretado como [ …(inválido porque não há ]) e … ](um comando separado, seja o que for) conectado logicamente com ||, exatamente como command1 || command2.

  7. [é especificado pelo POSIX.

    Existe oEspecificação POSIX para [/test. Você pode ligar para [portátil. Notas:

    • Algumas primárias são extensões, outras são marcadas como obsoletas. Por exemplo, nosso código inválido ( [ … || … ]) pode ser corrigido como [ … -o … ], mas como -oé obsoleto, uma correção melhor é [ … ] || [ … ].
    • As implementações podem suportar mais primárias. O [embutido no Bash faz (veja help test); o [binário (como /bin/[) em seu sistema operacional pode (consulte man test).

O que é [[? Como é [[diferente de [?

Notas:
- A pergunta está marcada, agora estamos falando sobre[[ em Bash.
- Os parágrafos numerados desta seção correspondem aos parágrafos numerados da seção anterior.

  1. (semelhança)[[realiza um teste.

    O objetivo [[é testar algo (por exemplo, se algum arquivo existir) e retornar o status de saída zero se o teste for bem-sucedido, diferente de zero caso contrário. [[pode testar qualquer coisa [que puder e muito mais.

  2. (diferença)[[é uma palavra-chave.

    ​é[[ uma palavra-chave do shell (invoque type [[no Bash para confirmar isso). Assim como o [builtin, a [[palavra-chave pertence ao Bash; mas issonãocomportar-se como um comando.

  3. (semelhança)[[requer um espaço depois.

    (Ou uma guia).

  4. (semelhança)[[requer ]].

    [[requer ]]posteriormente no código, porém não é apenas para se [[ … ]]destacar como um bloco. É uma sintaxe especial (uma diferença, já veremos). Você precisa de um espaço (ou tabulação) antes ]].

  5. (diferença)Não existe um comando de aparência comum equivalente a [[.

    Não há outro comando/palavra-chave/qualquer coisa que possa substituir [[da mesma forma testque pode substituir [.

  6. (diferença)[[ éespecial.

    [[muda a maneira como o shell analisa e interpreta o código, até a correspondência ]]. A análise e o processamento normais do shell antes da execução [ou test(ou echo, lsetc.) não se aplicam dentro de [[ … ]]. Em particular:

    • [[ éciente se você citou algum de seus "argumentos". Não é necessário colocar aspas duplas em variáveis ​​(embora normalmenteisso é), mas em alguns casos colocar aspas duplas em uma string (ou variável) faz diferença (vejaesta respostaonde menciona "o lado direito de =ou ==ou !=ou =~").

    • &&ou ||entre [[e ]]pertence à [[ … ]]construção. [[ … || … ]]pode ser um trecho de código válido, virtualmente equivalente a [[ … ]] || [[ … ]].

  7. (diferença)[[não é especificado pelo POSIX.

    [[não é [[funciona no Bash, não funciona em pure sh. Outros shells podem suportar [[(por exemplo, Zsh), mas [[podem ser diferentes dos [[do Bash.

    [[não foi projetado para atender à especificação de [/ test. Em muitos casos, substituir [por [[e ]por ]]fornecerá um [[ … ]]trecho equivalente ao [ … ]trecho original; Mas não sempre,às vezeso interior precisa ser ajustado. Portanto, não mude [para [[cegamente. Mudar cegamente [[para [é ainda mais arriscado porque há testes [[que você pode fazer e [não pode.


Outras notas:

  1. Nem [nem [[faz parte da if … then …sintaxe. Lembre-se ifde apenas testar o status de saída de algum código. if true; then …é válido, if sleep 5; then …é válido, de forma semelhante if [ … ]; then …ou if [[ … ]]; then …é válido (se a parte [ … ]ou [[ … ]]for um snippet válido).

    Como ((…))or também retorna algum status de saída (que depende do que está dentro), também (…)pode ser usado depois .if

  2. Pessoalmente prefiro usar [para qualquer teste [que possa fazer facilmente, recorrendo [[quando for realmente necessário. Assim não me acostumo com o não-portátil [[e sei o que posso fazer em um shell não tão rico quanto o Bash.

  3. O [executável no seu sistema operacional não é necessariamente equivalente ao [integrado no seu Bash. Se [for invocado pelo Bash, o builtin fará o trabalho. Se [for invocado por outra coisa, o executável fará o trabalho. Por exemplo, find … -exec [ … ] …usa o executável. Não existe [[um executável padrão (pelo menos nos sistemas que conheço), então find … -exec [[ … ]] …nunca funcionará. Se houvesse um [[executável, ele teria que se comportar como um comando, não seria capaz de imitar a palavra-chave.

Responder3

Há uma grande diferença entre [] e [[]]:

a=0;
if [ $a = 1 -a $(grep ERR 1.log|wc -l) -ne 0 ];then
      echo "error";
fi 

ao usar [], o operador and -a não oferece suporte à avaliação de curto-circuito. No código acima, mesmo a primeira condição retorna falso, a segunda condição ainda será avaliada. Se o arquivo 1.log não existir, ele irá reclamar "Esse arquivo ou diretório não existe".

Mas pelo [[ ]]:

a=0
if [[ $a = 1 && $(grep ERR 1.log|wc -l) -ne 0 ];then
    echo "error"
fi

mesmo o arquivo "1.log" não existir, tudo bem porque a segunda condição não será avaliada já que a primeira condição é falsa.

Responder4

Eu teria colocado isso em um comentário, mas ainda não tenho permissão para fazê-lo.

Uma diferença que notei entre [] e [[]], é que no primeiro é possível usar comparações múltiplas.

A mesma comparação gera um erro de sintaxe em [[]]

a=1
b=2

Isso funciona:

$  [ "$a" -gt 0 -a "$b" -gt "$a" ] && { echo '[b>a>0]'; }
[b>a>0]

Isso não funciona:

$ [[ $a -gt 0 -a $b -gt $a ]] && { echo '[[b>a>0]]'; }
-bash: syntax error in conditional expression
-bash: syntax error near `-a'

Eu realmente não investiguei para ver por que isso acontece, mas ao usar múltiplas comparações, eu uso [].

informação relacionada