Suponha que temos uma instrução if da seguinte forma:
if [ $(false) ]; then
echo "?"
fi
Então "?" não é impresso (a condição é falsa). No entanto, no caso a seguir, "?!" é impresso, por quê?
if [ $(false) -o $(false) ]; then
echo "?!"
fi
$(false)
não for avaliado como falso, ele produzirá uma string vazia. Porque não é citado,avalia para
que é falso, porque
[
com uma expressão vazia é falso.avalia para
Isso não usa o
-o
operador, ele avalia-o
como uma expressão com uma única string;[
com tal expressão é verdadeiro, então athen
parte daif
instrução é executada.Consulte a especificação POSIX para
test
, em particular:test
só considera operadores se forem dados pelo menos dois argumentos.Se você quiser usar o status de saÃda de um comando como condição, não o coloque em uma substituição de comando ou em
[ ]
:e
Observe também que os operadores
test
's-a
e-o
são obsoletos e não confiáveis; você deve usar o shell&&
e||
os operadores em vez disso, por exemploNão vou repetir os pontos levantados nas boas respostas de @StephenKitt e @ilkkachu aqui, apenas concentre-se no que significa escrever
if [ $(cmd) ]; then...
.Primeiramente observe que não
[
é um operador POSIXsh
, é um utilitário separado também conhecido como , aqui utilizadotest
naif
parte de uma instruçãoif
//// na sintaxe da linguagem .then
elif
else
fi
sh
Sua função é avaliar seus argumentos como uma expressão condicional. Por exemplo, se ele receber
[
,a
,=
,b
e]
como argumentos outest
,a
,=
eb
, ele retornará um status de saÃda falso/falha, pois o interpretou como "'a' é a mesma string que 'b'?" expressão condicional.[
é freqüentemente usado como o único comando naif
parte de uma instruçãoif
//then
ouelif
// ou na instrução // ou // , mas não precisa ser nem essas instruções precisam invocarelse
nem um único comando.fi
while
do
done
until
do
done
[
Fazer
[ $(cmd) ]
significa chamar o[
comando com[
, o resultado da$(cmd)
expansão e]
como argumentos separados. Em seguida,[
interpreta esses argumentos como uma expressão condicional e retorna verdadeiro ou falso dependendo do resultado (também falso se não conseguir entender a expressão).Em POSIX sh,
$(cmd)
expande para a saÃda padrão decmd
despojado de todos os caracteres de nova linha à direita e, como não é citado e no contexto da lista, sujeito à divisão IFS e, em seguida, globbing (com o comportamento não especificado se houver caracteres NUL nessa saÃda) .Então
if [ $(cmd) ]
outest $(cmd)
realmente não faz sentido.É algo como fazer a pergunta: "a saÃda de
cmd
, uma vez dividida+globbed, constitui uma expressão condicional válida que resulta em true?"Por exemplo, se
cmd
as saÃdasa,*
e$IFS
contiverem,
e o diretório de trabalho atual contiver dois arquivos, um chamado=
, outro chamadob
,[ $(cmd) ]
chamará[
com[
,a
,=
,b
,]
como argumentos. Felizmente, essa é uma expressão condicional válida, mas que retorna false comoa
não éb
.Agora,
[ "$(cmd)" ]
faz um pouco mais de sentido. Colocar aspas em uma substituição de comando não evita a remoção de caracteres de nova linha à direita, ainda não é especificado quandocmd
gera NULs, mas evita a remoção de split+glob e vazia. Portanto,[
sempre serão passados ​​3 argumentos:[
, a saÃda decmd
despojado de caracteres de nova linha à direita e]
. Com um argumento ao lado[
e]
,[
retorna true se e somente se esse argumento não for uma string vazia, é o mesmo que[ -n "$(cmd)" ]
.Portanto, é como dizer: "
cmd
exibe pelo menos um caractere que não é um caractere de nova linha (ou NUL)" . Para comandos que produzem texto (o texto é garantido como sendo feito de linhas e não contém NULs), isso é "cmd
exibe pelo menos uma linha não vazia?"Exceto pela saÃda curta, ele faz isso de maneira ineficiente, pois acabamos lendo toda a saÃda e armazenando na memória, e então passamos is para
[
.Teria sido mais eficiente, pois
grep
sairia com true assim que encontrasse uma linha que contém pelo menos um caractere.Se
cmd
fosseyes
,if [ "$(cmd)" ]
acabaria travando o shell com um erro de memória insuficiente , enquantoif cmd | grep -q .
a saÃda seria sim imediatamente (eyes
seria encerrada por meio de um sinal SIGPIPE).Para testar se é
cmd
bem-sucedido, independentemente do que pode ou não produzir, basta fazer:if $(cmd); then
é outro exemplo de código que faz pouco sentido. Mas há uma pequena reviravolta no exemplo decmd
serfalse
.Novamente,
$(cmd)
é retirado os caracteres de nova linha à direita, sujeitos a split+glob. Mas, desta vez, as palavras resultantes não são passadas para um utilitário[
/test
, mas a primeira dessas palavras, se houver, é tratada como o comando a ser executado e todas as palavras passadas como argumentos para ele.Portanto, se
cmd
outputsecho:hello:world
e$IFS
contém:
, isso acaba sendo executadoecho
comecho
,hello
eworld
como argumentos e assumindoecho
que consegue escrever com sucesso"hello world\n"
em seu stdout, ele retornará true/success, então athen
parte será executada.Agora, se
cmd
não produzir saÃda ou apenas caracteres de nova linha e caracteres de espaço em branco IFS,$(cmd)
não resultará em nenhuma palavra, portanto, outro comando não será executado e, nesse ponto, é o status de saÃda decmd
si mesmo que importará para aif
declaração.Em:
no
é saÃda porque, comofalse
não produz saÃda, nenhum comando foi executado e é o status de saÃdafalse
que decide que aelse
peça seja executada.Em:
e assumindo que
$IFS
não contémt
,r
,u
neme
caractere;true
acaba sendo executado e é seu status de saÃda que determina a ramificação naif
instrução.