Porque o último comando executado (o echo ) foi bem-sucedido. Se você deseja agrupar os comandos, é mais claro usar uma instrução if :
if ! step 1 ; then
echo >&2 "Failed to execute step 1"
exit 2
fi
Você também pode agrupar a mensagem de erro e sair com { ... } , mas isso é um pouco mais difícil de ler. No entanto, isso preserva o valor exato do status de saída.
step 1 || { echo >&2 "step 1 failed with code: $?"; exit 2; }
Note que eu mudei o && para um ponto-e-vírgula, pois suponho que você queira sair mesmo se a mensagem de erro falhar (e gerar os erros no stderr para uma boa prática).
Para a variante if preservar o status de saída, você precisará adicionar seu código à else part:
if step 1; then
: OK, do nothing
else
echo >&2 "step 1 failed with code: $?"
exit 2
fi
(isso também o torna compatível com o shell Bourne que não possui a palavra-chave ! ).
Como ou por que o grupo de comandos faz, o padrão diz :
An AND-OR list is a sequence of one or more pipelines separated by the operators
"&&"and"||".The operators
"&&"and"||"shall have equal precedence and shall be evaluated with left associativity.
O que significa que algo como somecmd || echo && exit age como se somecmd e echo fossem agrupados primeiro, ou seja, { somecmd || echo; } && exit e não somecmd || { echo && exit; } .