2011-08-03 19 views
8

Właśnie bawiłem się bashem, aby ominąć letnie popołudniowe upały, gdy nagle mam tajemniczy wynik, dla którego nie mogę określić jego pochodzenia.Tajemniczy LINENO w pułapce pułapki ERR

Pozwól mi wyjaśnić trochę.

Używam pułapki ERR do tworzenia niektórych funkcji debugowania dla moich skryptów basha.

Jest to skrypt, który działa prawidłowo:

traperror() { 
    local err=$? # error status 
    local line=$1 # LINENO 
    [ "$2" != "" ] && local funcstack=$2 # funcname 
    [ "$3" != "" ] && local linecallfunc=$3 # line where func was called 
    echo "<---" 
    echo "ERROR: line $line - command exited with status: $err" 
    if [ "$funcstack" != "" ]; then 
     echo -n " ... Error at function ${funcstack[0]}() " 
     if [ "$linecallfunc" != "" ]; then 
      echo -n "called at line $3" 
     fi 
     echo 
    fi 
    echo "--->" 
    } 
#trap 'traperror $LINENO ${FUNCNAME}' ERR 

somefunction() { 
trap 'traperror $LINENO ${FUNCNAME} $BASH_LINENO' ERR 
asdfas 
} 

somefunction 

echo foo 

Wyjście jest (stderr idzie /dev/null dla jasności; błąd bash jest oczywiście foo.sh: line 23: asdfas: command not found która jak wiecie kod błędu 127)

~$ bash foo.sh 2> /dev/null 
<--- 
ERROR: line 21 - command exited with status: 127 
    ... Error at function somefunction() called at line 24 
---> 
foo 

Wszystkie numery linii to z prawej, linia 21 to początek funkcji "somefunction", a linia 24 jest tam, gdzie jest wywoływana.

Jednak jeśli I odkomentuj pierwszą pułapkę (jeden w głównym) otrzymuję ten wynik:

~$ bash foo.sh 2> /dev/null 
<--- 
ERROR: line 21 - command exited with status: 127 
    ... Error at function somefunction() called at line 24 
---> 
<--- 
ERROR: line 15 - command exited with status: 127 
---> 
foo 

W przypadku I Odkomentuj pierwszą pułapkę i skomentować drugi dostaję, że błąd znajduje się w linii 23, która jest właściwa, ponieważ jest to linia absolutna, w której umieszczone jest niewłaściwe polecenie.

~$ bash foo.sh 
<--- 
ERROR: line 23 - command exited with status: 127 
---> 
foo 

Moje pytanie brzmi: dlaczego linia 15? skąd pochodzi ten numer linii? Linia 15 jest ostatnią linią w funkcji pułapki. Czy ktokolwiek może wyjaśnić prostym językiem, dlaczego pułapka zwraca ostatni wiersz funkcji, którą wywołuje, jako linię, która spowodowała błąd w linii 21?

Z góry dziękuję!

EDIT

Tylko w przypadku, gdy ktoś jest zainteresowany funkcji debugowania. Jest to wersja produkcyjna:

# Copyright (c): Hilario J. Montoliu <[email protected]> 
# This program is free software; you can redistribute it and/or modify it 
# under the terms of the GNU General Public License as published by the 
# Free Software Foundation; either version 2 of the License, or (at your 
# option) any later version. See http://www.gnu.org/copyleft/gpl.html for 
# the full text of the license. 

set -o errtrace 
trap 'traperror $? $LINENO $BASH_LINENO "$BASH_COMMAND" $(printf "::%s" ${FUNCNAME[@]})' ERR 

traperror() { 
    local err=$1 # error status 
    local line=$2 # LINENO 
    local linecallfunc=$3 
    local command="$4" 
    local funcstack="$5" 
    echo "<---" 
    echo "ERROR: line $line - command '$command' exited with status: $err" 
    if [ "$funcstack" != "::" ]; then 
     echo -n " ... Error at ${funcstack} " 
     if [ "$linecallfunc" != "" ]; then 
      echo -n "called at line $linecallfunc" 
     fi 
     else 
      echo -n " ... internal debug info from function ${FUNCNAME} (line $linecallfunc)" 
    fi 
    echo 
    echo "--->" 
    } 

somefunction() { 
    asdfasdf param1 
    } 

somefunction 

echo foo 

Który będzie działać jako:

~$ bash foo.sh 2> /dev/null 
<--- 
ERROR: line 26 - command 'asdfasdf param1' exited with status: 127 
    ... Error at ::somefunction::main called at line 29 
---> 
<--- 
ERROR: line 22 - command 'asdfasdf param1' exited with status: 127 
    ... internal debug info from function traperror (line 0) 
---> 
foo 
+0

hmontoliu: Czy istnieje możliwość przypisania licencji do tego kodu? W ten sposób projekty open-source mogą z niego korzystać bez potrzeby uzyskania wyraźnego pozwolenia :-) Jeśli nie masz pewności, http://choosealicense.com/ to dobry zasób. Polecam GPL lub LGPL dla tego kodu, ale to oczywiście osobiste preferencje. – Hamy

+0

Hamy, dodałem licencję GPL. Dziękuję – hmontoliu

+0

Wielkie dzięki! Pracuję, aby uzyskać fragment kodu połączony z debianem, który zawiera to. Jeśli to przejdzie, uaktualnię cię tutaj (i możemy zapewnić, że jesteś wymieniony jako contrib) – Hamy

Odpowiedz

6

kilka istotnych informacji fakty/background:

  • Pułapki na ERR nie są dziedziczone przez funkcje powłoki, chociaż otrzymują resztę środowiska, chyba że ustawiono errtrace.

  • Status wyjścia funkcji to jej ostatnie polecenie.

Zgaduję, co się dzieje:

W przypadku, gdy oba są aktywne pułapki,

  • Komenda nieistniejący uruchamia pułapkę w funkcji ERR. LINENO jest tym z nieistniejącego polecenia.
  • Pułapka zostaje zakończona. Ponieważ nieistniejące polecenie było ostatnim poleceniem, status powrotu funkcji jest niezerowy, więc pułapka ERR w powłoce jest wyzwalana. LINENO jest nadal ustawiony na ostatni wiersz traperror, ponieważ był to ostatni wiersz do wykonania i nadal jest bieżącym wierszem, ponieważ nie została jeszcze wykonana nowa linia.

W przypadku, gdy tylko pułapka powłoka jest aktywny (to w funkcji zakomentowany)

  • polecenie nie istnieje jest ostatnim poleceniu w funkcji więc powoduje, że funkcja zwraca non -zero, powodując wyzwolenie pułapki powłoki ERR. Z tego samego powodu, LINENO jest ostatnim wierszem funkcji, ponieważ była to ostatnia linia do wykonania i nadal jest bieżącą linią.
+0

Dzięki jw013, bawiłem się scenariuszem skupiającym się na twoich wrażeniach i wydaje się, że masz rację. Przy okazji, dzięki za wskazówkę errtrace (teraz czuję się zupełnie ignorantem), unika ona umieszczania zagnieżdżonych pułapek, aby uzyskać informacje o błędach w ramach funkcji. – hmontoliu

+1

zobacz moją EDYTACJĘ o roboczej ERR funkcji pułapki, jeśli czujesz się ciekawy :-) – hmontoliu

2

Aby upewnić się w swojej pierwszej wersji swojej funkcji traperror że teleskopowa ERR sygnał nie zostanie wykonane dwa razy, można zignorować lub zresetować obsługi ERR sygnału do domyślnej akcji do końca programu - w ramach Definicja samej obsługi sygnału ERR. I to zawsze powinno być zrobione dla niestandardowej obsługi sygnału EXIT.

trap "" EXIT ERR # ignore 
trap - EXIT ERR # reset 

# for the first version of your traperror function 
- trap 'traperror $LINENO ${FUNCNAME}' ERR 
- trap 'traperror $LINENO ${FUNCNAME} $BASH_LINENO' ERR 
+ trap 'traperror $LINENO ${FUNCNAME}; trap - ERR' ERR 
+ trap 'traperror $LINENO ${FUNCNAME} $BASH_LINENO; trap - ERR' ERR