Como exatamente 'se $ cmd; depois $ cmd; fi 'difere de' $ cmd && $ cmd '?

5

Em resposta a outra pergunta muito boa , fiz a seguinte afirmação:

De acordo com a minha leitura das especificações POSIX, o uso de uma ou outra não faz diferença do ponto de vista da análise.

POSIX especifica que &&|| listas são comandos compostos , o que significa que a lista inteira deve ser lida e analisada antes da execução dos comandos simples constituintes. O POSIX também especifica que um comando não deve ser expandido se seguir um ||OR e outro comando com um status 0-exit. Quando você considera que um comando após uma palavra reservada &&|| pode ser facilmente outro% { command || ( command ; list) ; } agrupado torna cada ramificação do &&|| ou lista seu próprio comando composto .

Mas o POSIX também especifica que cada ramificação da construção if...;then...;else...fi seja seu próprio comando composto :

if compound-list
then
    compound-list
[elif compound-list
then
    compound-list] ...
[else
    compound-list]
fi

The if compound-list shall be executed; if its exit status is zero, the then compound-list shall be executed and the command shall complete...

É esta especificação que a string que segue as palavras then... ou else... reservada são comandos compostos por si só e não apenas comandos simples que significam O analisador do shell deve denotá-los com um comando e um delimitador para funcionar corretamente.

Então, basicamente, then ' ' não funciona pelo mesmo motivo:

function()
sh: line 2: syntax error: unexpected end of file 

... não - não faz sentido.

Eu sei disso ...

[ -e doesntexist ] && $((i=1)) ; echo $i

... causará um curto-circuito em todos os efeitos colaterais e resultará em apenas \n ewline como resultado da especificação que observei na minha resposta. O resultado de ...

if [ -e doesntexist ] 
    then $((i=1))
fi
echo $i

... é idêntico.

Mas eu honestamente uso if...fi tão raramente que eu não tenho certeza se eu interpretei mal alguma coisa e então quando recebi um comentário indicando que minha interpretação estava incorreta e que se eu tivesse mais perguntas sobre o assunto eu só perguntaria eliminei a resposta em vez desta pergunta: como eu errei - como eles diferem?

    
por mikeserv 01.06.2014 / 23:18

2 respostas

2

Apenas casos simples podem ser expressos com && e || . A construção if é mais geral. if CONDITION; then FOO; fi é equivalente a CONDITION && FOO (assumindo o uso adequado de chaves para delimitar um bloco, se necessário), mas assim que houver um else (ou elif ), isso não será mais possível em geral.

if CONDITION; then FOO; else BAR; fi

não é equivalente a

CONDITION && FOO || BAR

Se CONDITION for true, ambas as construções executam FOO . Se FOO for falso, a construção if ignorará BAR , enquanto a construção && … || executará BAR .

E não, você não pode contornar isso com CONDITION && { FOO; true; } || BAR . Isso faz com que o comando composto retorne verdadeiro se CONDITION for true e FOO for falso, enquanto if CONDITION; then FOO; else BAR; fi retornará falso nesse caso.

Isso é para a diferença semântica. Além disso, há legibilidade: os usos aninhados de && e || tornam-se muito difíceis de decifrar rapidamente. Eu não recomendo usar ambos no mesmo comando, na verdade - especialmente considerando que os dois operadores têm igual precedência no shell, enquanto eles têm precedências diferentes em C e na maioria das outras linguagens inspiradas em C (incluindo Perl e Ruby). / p>     

por 02.06.2014 / 02:16
1

if testlist; then else ...fi e testlist && thenlist || elselist não são equivalentes em geral. A diferença é que na construção if exatamente um dos thenlist e elselist é executado. No outro caso, isso depende do código de saída de thenlist (e possíveis eliflist blocks).

Assim, você precisa adicionar (por exemplo) true no final de uma lista de resultados selecionada, para que a lista tenha o mesmo código de saída, como a lista de testes que causou sua execução.

if true; then
  echo true
  test -e doesntexist
else
  echo false
fi

pode ser reescrito com && e || certificando-se de que o código de saída do código bloqueia. Em vez de

true && { echo true; test -e doesntexist; } || echo false

você precisa

true && { echo true; test -e doesntexist; true; } || echo false

elif

if test -e exists; then
  echo true
elif test -e doesntexist
  echo elif
else
  echo false
fi

seria

test -e exists && { echo true; true; } ||
  { test -e doesntexist && { echo elif; true; } || echo false; }
    
por 02.06.2014 / 00:57