Jak wyłączyć zestaw -e dla pojedynczego polecenia?

33

Komenda set -e powoduje, że skrypt bash kończy się niepowodzeniem natychmiast, gdy dowolne polecenie zwróci niezerowy kod wyjścia.

  1. Czy istnieje prosty i elegancki sposób na wyłączenie tego zachowania dla pojedynczego polecenia w skrypcie?

  2. W jakich miejscach ta funkcja jest udokumentowana w Bash Reference Manual ( http://www.gnu.org/software/bash/manual/bashref.html )?

Gustaw
źródło

Odpowiedzi:

29
  1. Coś takiego:

    #!/usr/bin/env bash
    
    set -e
    echo hi
    
    # disable exitting on error temporarily
    set +e
    aoeuidhtn
    echo next line
    
    # bring it back
    set -e
    ao
    
    echo next line

    Biegać:

    $ ./test.sh
    hi
    ./test.sh: line 7: aoeuidhtn: command not found
    next line
    ./test.sh: line 11: ao: command not found
  2. Jest to opisane we setwbudowanej pomocy:

    $ type set
    set is a shell builtin
    $ help set
    (...)
    Using + rather than - causes these flags to be turned off.

To samo udokumentowano tutaj: https://www.gnu.org/software/bash/manual/bashref.html#The-Set-Builtin .

Arkadiusz Drabczyk
źródło
Dzięki! Znalazłem inną możliwość: #! / Bin / bash zestaw -e # Odkomentuj następną linię, aby zobaczyć efekt zestawu -e: #blubb if blubb; potem echo „Polecenie blubb zakończyło się powodzeniem”. else echo „Komenda blubb nie powiodła się. Kod zakończenia: $?” fi echo normalne wyjście ze skryptu
Gustave
21

Alternatywą dla unieruchomienia kaucji za błąd byłoby wymuszenie sukcesu bez względu na wszystko. Możesz zrobić coś takiego:

cmd_to_run || true

Zwróci to 0 (prawda), więc zestaw -e nie powinien być wyzwalany

ernie
źródło
2
faulty_cmd || :jest kolejnym popularnym idiomem tego. (To :polecenie jest synonimem true).
ulidtko
2
@ulidtko - ciekawe. Poprowadził mnie do króliczej nory, aby znaleźć ten stackoverflow.com/questions/3224878/... i uzyskać historię prawdziwej kontra:
ernie
11

Jeśli próbujesz złapać kod powrotu / błędu (funkcja lub rozwidlenie), działa to:

function xyz {
    return 2
}

xyz && RC=$? || RC=$?
h0tw1r3
źródło
7

To, czy „opcja natychmiastowego wyjścia z powłoki” ma zastosowanie lub jest ignorowane, zależy od kontekstu wykonanego polecenia (zobacz sekcję Bash Reference Manual wbudowanego zestawu - dzięki Arkadiusz Drabczyk).

W szczególności opcja jest ignorowana, jeśli polecenie jest częścią testu w instrukcji if. Dlatego możliwe jest wykonanie polecenia i sprawdzenie jego powodzenia lub niepowodzenia w „kontekście natychmiastowego wyjścia” za pomocą instrukcji if, takiej jak ta:

#!/bin/bash

set -e

# Uncomment next line to see set -e effect:
#blubb

if blubb; then
  echo "Command blubb was succesful."
else 
  echo "Command blubb failed. Exit code: $?"
fi
echo "Script exited normally."

Można pominąć instrukcję „then” i użyć mniejszej liczby wierszy:

if blubb; then :;
else echo "Command blubb failed. Exit code: $?"; fi
Gustaw
źródło
0

Właściwie miałem ostatnio podobne pytanie (chociaż nie opublikowałem, zająłem się tym) i, z tego, co widzę, wygląda na to, że po prostu używam set + e przed poleceniem, a później zestaw -e działa najbardziej elegancko. Oto przykład, łapiąc odpowiedź polecenia i nie pozwalając, by błąd go wyrzucił.

#!/bin/sh

args=""
for argcol in $*
do
    args="${args} ${argcol}"
done
fortunevar=""
fortfail=""
{
    set +e
    fortunevar=`fortune $args`
    fortfail=$?
    set -e
} &> /dev/null
if [ $fortfail == 0 ]
then
    echo ${fortunevar}
    say ${fortunevar}
else
    echo misfortune: an illegal option was detected!
    echo misfortune: usage: misfortune [-afilosw] [-m pattern][ [#%] file/directory/all]
fi

Pobiera to wynik „fortuny”, sprawdza jego status wyjścia, a także odbija się echem i mówi. Myślę, że o to prosiłeś, a przynajmniej coś podobnego? W każdym razie mam nadzieję, że to pomoże.

Addison Crump
źródło
Aby to wyjaśnić, uważam, że to właśnie w nawiasach klamrowych przekierowywanych do / dev / null jest to, czego szukasz. Sprawia, że ​​polecenie nie ma wyjścia i sprawdza status błędu bez wychodzenia z programu.
Addison Crump,
0

Lubię uruchamiać podpowłokę, jeśli chcę coś tymczasowo zmienić. Poniższa komenda pokazuje, że pierwsza komenda bad_comm jest ignorowana, a druga przerywa wykonywanie.

bash -c 'set -e ;\
( set +e; bad_command ; echo still here ) ;\
echo even here ; \
bad_command ; \
echo but not here;'
noonex
źródło
0

Kolejne podejście, które wydaje mi się dość proste (i dotyczy innych setopcji oprócz -e):

Skorzystaj z, $-aby przywrócić ustawienia.

Na przykład:

oldopt=$-
set +e
# now '-e' is definitely disabled.
# do some stuff...

# Restore things back to how they were
set -$oldopt

Choć -ekonkretnie, opcje, o których wspominali inni ( || truelub „umieszczają w środku if”), mogą być bardziej idiomatyczne.

jwd
źródło