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 é [
?
[
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.[
é um comando.[
é um nome de comando comocat
ouecho
.[
(comoecho
) é 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/[
); tentetype -a [
no Bash para descobrir.[
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 obviamenteechoHello world
não éecho Hello world
). Você certamente precisa de um espaço (ou tabulação) depois de[
.[
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.[
é (quase) parecidotest
.[ … ]
é equivalente atest …
. Em outras palavras, você pode converter qualquer[
comando emtest
comando removendo]
e alterando o nome do comando. E você pode converter qualquertest
comando em[
comando alterando o nome do comando e adicionando]
.[
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 (test
ouecho
,ls
etc.) 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 comocommand1 || command2
.
[
é 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 (vejahelp test
); o[
binário (como/bin/[
) em seu sistema operacional pode (consulteman test
).
- Algumas primárias são extensões, outras são marcadas como obsoletas. Por exemplo, nosso código inválido (
O que é [[
? Como é [[
diferente de [
?
Notas:
- A pergunta está marcadafesta, agora estamos falando sobre[[
em Bash.
- Os parágrafos numerados desta seção correspondem aos parágrafos numerados da seção anterior.
(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.(diferença)
[[
é uma palavra-chave.é
[[
uma palavra-chave do shell (invoquetype [[
no Bash para confirmar isso). Assim como o[
builtin, a[[
palavra-chave pertence ao Bash; mas issonãocomportar-se como um comando.(semelhança)
[[
requer um espaço depois.(Ou uma guia).
(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]]
.(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 formatest
que pode substituir[
.(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[
outest
(ouecho
,ls
etc.) 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[[ … ]] || [[ … ]]
.
(diferença)
[[
não é especificado pelo POSIX.[[
não é[[
funciona no Bash, não funciona em puresh
. 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:
Nem
[
nem[[
faz parte daif … then …
sintaxe. Lembre-seif
de apenas testar o status de saída de algum código.if true; then …
é válido,if sleep 5; then …
é válido, de forma semelhanteif [ … ]; then …
ouif [[ … ]]; 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
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.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ãofind … -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 [].