bash -e kończy działanie, gdy let lub expr ma wartość 0

19

Mam skrypt bash, który ustawia -e, więc skrypt zakończy działanie w dowolnym stanie wyjścia! = 0.

Próbuję wykonać podstawową arytmetykę powłoki przypisaną do zmiennych, a czasami wyrażenie jest równe 0, co powoduje, że stanem wyjścia komendy let lub expr jest „1”.

Oto przykład:

#!/bin/bash -ex
echo "Test 1"
Z=`expr 1 - 1` || true
echo "Z will print"
let "A=4 - 4"
echo "A WILL NEVER PRINT $A"
Y=`expr 1 - 1`
echo "Y WILL NEVER PRINT $Y"
X=$(expr 2 - 2)
echo "X WILL NEVER PRINT $X"

Dane wyjściowe to:

$ ./test_error.sh 
+ echo 'Test 1'
Test 1
++ expr 1 - 1
+ Z=0
+ true
+ echo 'Z will print'
Z will print
+ let 'A=4 - 4'

Moje pytanie brzmi: w jaki sposób idiomatyczny skrypt bash pozwala na niepowodzenie skryptu w przypadku rzeczywistych błędów wyjścia, a nie w przypadku podstawowej arytmetyki równej 0. Mogłem sufikować wszystkie te wyrażenia za pomocą:

A=`expr $C - $D`    || true

Ale to wydaje się hacking.

Dougnukem
źródło

Odpowiedzi:

16

Nie używaj exprdo arytmetyki. Od dawna jest przestarzały: powłoki mają teraz wbudowaną arytmetykę, z $((…))konstruktem (POSIX) lub z letwbudowanym (ksh / bash / zsh) lub ((…))konstruktem (ksh / bash / zsh).

leti ((…))zwróć 1 (kod stanu awarii), jeśli ostatnio oceniane wyrażenie ma wartość 0. Aby uniknąć tego, że skrypt kończy działanie poniżej set -e, ustaw, aby ostatnie wyrażenie nie zwróciło 0, na przykład:

let "a = 2 - 2" 1
((a = 2 - 2, 1))

Alternatywnie użyj || trueidiomu:

((a = 2 - 2)) || true

Alternatywnie, wykonaj swoją arytmetykę wewnątrz, $((…))a swoje zadania na zewnątrz. Przypisanie zwraca status ostatniego podstawienia polecenia w wartości lub 0, jeśli nie ma podstawienia polecenia, więc jesteś bezpieczny. Ma to dodatkową zaletę pracy w dowolnej powłoce POSIX (takiej jak dash).

a=$((2 - 2))
Gilles „SO- przestań być zły”
źródło
1

Użyj $(( $C - $D ))zamiast tego do arytmatyki. Jest też bardziej wydajny.

tylerl
źródło
Co sprawia, że ​​jest bardziej wydajny niż powiedzieć (( A = $C - $D ))?
biskup
1

Miałem ten sam problem . tl; dr:

Jeśli ostatni ARG [let] ma wartość 0, let zwraca 1; niech zwraca 0 w przeciwnym razie.

l0b0
źródło
1

Ta składnia działa dla mnie:

a=$((b + c))
Mark Sawers
źródło