2013-05-03 14 views
6

Mam skrypt bash, który prowadzi trzy kontrole nad moim kodu źródłowego, a następnie exit 0 jeśli wszystkie polecenia udało, albo exit 1 czy któryś z nich nie powiodło się:kody wyjścia Anding w bash

#!/bin/bash 

test1 ./src/ --test-1=option 
exit_1=$? 

test2 ./src/ test-2-options 
exit_2=$? 

test3 ./src/ -t 3 -o options 
exit_3=$? 

# Exit with error if any of the above failed 
[[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] 
exit $? 

Ten kod działa, ale wydaje się zbyt długie i pełne gadatliwości. Czy jest jakiś sposób, żeby to było przyjemniejsze? W szczególności nie jestem zadowolony z:

  • konieczności uruchomienia polecenia, a następnie przypisać kod wyjścia do zmiennej
  • konieczności korzystania [[ ... ]], następnie zbierać jego kod wyjścia w następnej linii do wyjście z
  • Mając jawnie porównania zmiennych 0, jak w [[ $var -eq 0 ]], zamiast traktowania ich jako wartości logiczne

idealnie, koniec wynik Wou ld być coś bardziej czytelne jak:

exit_1=(test1 ./src/ --test-1=option) 
exit_2=(test2 ./src/ test-2-options) 
exit_3=(test3 ./src/ -t 3 -o options) 

# Exit with error if any of the above failed 
exit ($exit_1 && $exit_2 && $exit_3) 

pewne rzeczy mam wziąć pod uwagę:


Uzyskiwanie kod błędu w zmiennej w jednym wierszu:

exit_1=$(test1 ./src/ --test-1=option)$? 
exit_2=$(test2 ./src/ test-2-options)$? 
exit_3=$(test3 ./src/ -t 3 -o options)$? 

to działa i sprawia, że ​​jest to trochę krótsze, ale nigdy wcześniej nie widziałem, żeby ktoś z tego korzystał. Czy jest to rozsądne/rozsądne zachowanie? Czy są jakieś problemy z tym?


Wystarczy uruchomione testy i & & je razem:

test1 ./src/ --test-1=option && \ 
test2 ./src/ test-2-options && \ 
test3 ./src/ -t 3 -o options 
status=$? 

to robi nie pracy, jak zwarć bash. Jeśli nie powiedzie się test1, test2 i test3 nie działają i chcę, aby wszystkie z nich działały.


Detecing błędy i wychodzenia z użyciem || exit

[[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] || exit 1 

Oszczędza to jeden wiersz niewygodnych kodów wyjścia i zmiennych, ale ważne trochę exit 1 jest teraz w prawo na końcu linii, gdzie może tego nie zauważyć. Idealnie coś jak to będzie działać:

exit [[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] 

Oczywiście, to nie robi pracy, jak [[ zwraca jego wyjście zamiast odbijając go.

exit $([[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] ; echo $?) 

robi pracy, ale wciąż wydaje się okropny cludge


nie zostały wyraźnie do czynienia z wyjściem kodów-as-logicznych

[[ $exit_1 && $exit_2 && $exit_3 ]] 

nie robić co miałbyś nadzieję, że to zrobi. Najprostszym sposobem połączenia trzech kodów zwrotnych przechowywanych w zmiennych jest pełna wersja & z pełną wersją &. Z pewnością jest lepszy sposób?


wiem bash nie jest ładny język programowania - jeśli można nawet nazwać - ale czy jest jakiś sposób mogę zrobić to mniej niezręcznie?

+0

Metoda 'var = $ (foo) $?' Jest interesująca, ale powoduje, że twoje wywołania testowe są niepotrzebne. – kojiro

+0

Co najmniej, twoje ostatnie 'exit $?' Nie jest konieczne, jeśli '[[... && ... && ... && ...]]' jest ostatnią instrukcją skryptu. – chepner

Odpowiedz

7

Można użyć polecenia arytmetyczną bash „s do OR kody wyjścia razem i neguje wynik, aby uzyskać kod zakończenia 1, jeśli któryś z kodów jest niezerowe. Po pierwsze, przykład:

$ ! ((0 | 0 | 0)); echo $? 
0 
$ ! ((1 | 0 | 0)); echo $? 
1 

Teraz skrypt:

#!/bin/bash 

test1 ./src/ --test-1=option; exit_1=$? 
test2 ./src/ test-2-options; exit_2=$? 
test3 ./src/ -t 3 -o options; exit_3=$? 

# Exit with error if any of the above failed. No need for a final 
# call to exit, if this is the last command in the script 
! (($exit_1 || $exit_2 || $exit_3)) 

Albo w ogóle, można gromadzić kody wyjścia jak uruchomić dowolną liczbę testów:

#!/bin/bash 

# Unfortunately, ||= is not an assignment operator in bash. 
# You could use |=, I suppose; you may not be able to assign 
# any meaning to any particular non-zero value, though. 
test1 ./src/ --test-1=option; ((exit_status = exit_status || $?)) 
test2 ./src/ test-2-options; ((exit_status = exit_status || $?)) 
test3 ./src/ -t 3 -o options; ((exit_status = exit_status || $?)) 
# ... 
testn ./src "${final_option_list[@]}"; ((exit_status = exit_status || $?)) 

exit $exit_status # 0 if they all succeeded, 1 if any failed 
+0

'! (($ exit_1 || $ exit_2 || $ exit_3)) 'jest bardzo przydatne. Próbowałem uniknąć umieszczania przydziałów "exit_1 = $?" Na końcu linii, ponieważ niektóre ważne stwierdzenia są ukryte pod koniec długich linii. Poszedłem z rozwiązaniem $ exit_1 = $ (...) $? ', Ale poza tym użyłem twojego. Dzięki! –

+0

Właściwie to złom. Nie mogę użyć '$ exit_1 = $ (...) $?', Ponieważ gromadzi zarówno dane wyjściowe polecenia *, jak i * kod powrotu. Po prostu wykorzystam całą twoją odpowiedź, tak myślę. –

2

Niektóre ulepszenia

[ $exit_1$exit_2$exit3 = 000 ] 
# no exit needed here, script exits with code from last command 
0

można przypisać kod zakończenia polecenia za pomocą tej linii:

RES1=$(CMD > /dev/null)$? 

przykład:

RES1=$(test1 ./src/ --test-1=option > /dev/null)$? 

Tak Twój kod będzie:

exit_1=$(test1 ./src/ --test-1=option > /dev/null)$? 
exit_2=$(test2 ./src/ test-2-options > /dev/null)$? 
exit_3=$(test3 ./src/ -t 3 -o options > /dev/null)$? 

# Exit with error if any of the above failed 
exit ($exit_1 && $exit_2 && $exit_3) 
+0

Schludny trik, którego nie znałem, ale tworzy to niepotrzebną podpowłokę, więc nie mogę go naprawdę pobudzić z czystym sumieniem. – kojiro

+0

Skrypty testowe uruchamiają testowy serwer sieciowy i 'wget - mirror' sprawdzają pod kątem niedziałających linków, różnicują wiele folderów, siebie nawzajem i przeglądają niektóre pliki, aby pozostały dokumenty' TODO 'w dokumentach, rozpalanie podpowiadanie jest czymś, z czym mogę żyć! Jednak nadal dobrze jest wiedzieć, czy szybkość i wydajność są zawsze problemem. –

+0

W rzeczywistości to nie działa! Jeśli polecenia w podpowłoce generują dowolne wyjście (co robią moje polecenia), to dane wyjściowe są przypisywane do zmiennej. Oznacza to, że 'out = $ (echo 'foo'; exit 1) $?' Powoduje, że '$ out' jest' 'foo1''. –

0

Dlaczego potrzebujesz trzech różnych zmiennych?

fn() { 
    local -i st 
    test1 ./src/ --test-1=option 
    st=$? 

    test2 ./src/ test-2-options 
    ((st = ($? || st))) # Use arithmetic expression to or the values at each step 

    test3 ./src/ -t 3 -o options 
    ((0 == ($? || st))) 
} 
1

Szukałem na nie odpowiedzi i zdecydowałem się na sposób podobny do @ chepnera, ale nie stosuję arytmetyki wyrażeń basha:

#!/bin/bash 

failed=0 
test1 ./src/ --test-1=option || failed=1 
test2 ./src/ test-2-options || failed=1 
test3 ./src/ -t 3 -o options || failed=1 

# Exit with error if any of the above failed 
if [ "$failed" -ne 0 ] ; then 
    exit 
fi 

Po prostu ustaw flagę na początku i ustaw flagę na 1, jeśli którakolwiek z instrukcji się nie powiedzie. Kluczem jest tutaj ||.