2011-08-03 8 views
8

夏の午後の熱を迂回するためにbashで遊んでいました。突然私はそれが原因であると判断できない不思議な結果を得ました。bash trapの神秘的なLINENO ERV

少し説明しましょう。

私はトラップERRを使用して、bashスクリプトのデバッグ機能をいくつか作成しています。

この

は正常に動作するスクリプトです:

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 

出力する(標準エラー出力は、明確にするため/dev/nullに進み、bashのエラーは、あなたが知っているようで、もちろんfoo.sh: line 23: asdfas: command not foundのあるエラーコード127)

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

すべての行番号はで、21行目は "somefunction"関数を開始し、24行目は呼び出される場所です。

しかし私は最初のトラップ(メインの1)をアンコメント場合、私はこの出力を得る:場合

~$ 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 

私は最初のトラップのコメントを解除し、私はエラーがあることを得る二番目のコメントそれは間違ったコマンドが置かれている絶対行なので、行23にもあります。

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

私の質問は、なぜ15行目ですか?その行番号はどこから来ますか? 15行目はトラップ関数の最後の行です。誰もが普通の英語で説明することができますなぜtrapは、行21のエラーを生成した行として呼び出す関数の最後の行を返します

ありがとうございます!誰かがデバッグ機能に興味を持っているだけの場合には

EDIT

。この製品版である:

として動作します
# 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 

~$ 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:このコードにライセンスを明示的に割り当てることはできますか?そうすれば、オープンソースのプロジェクトではあなたの明示的な許可を得ることなくそれを使うことができます:-)あなたが確信が持てないなら、http://choosealicense.com/は良いリソースです。私はこのコードのためにGPLまたはLGPLをお勧めしますが、明らかに個人的な好みです – Hamy

+0

私はGPLライセンスを追加しました。ありがとうございます – hmontoliu

+0

ありがとう!私は、これを含むdebianにマージされたコードを取得しようとしています。それが終わったら私はここであなたを更新します(そしてあなたが貢献者として言及されていることを保証することができます) – Hamy

答えて

6

いくつかの関連する事実/背景情報を:ERR

  • トラップがたとえシェル関数によって継承されていませんerrtraceが設定されていない限り、環境の残りの部分を取得します。

  • 関数の終了ステータスは、最後のコマンドの終了ステータスです。何が起こっているのかについての

私の推測:

両方のトラップがアクティブになっている場合には、

  • 存在しないコマンドは、関数でERRトラップをトリガします。 LINENOは、存在しないコマンドのものです。
  • トラップの実行が終了しました。存在しないコマンドが最後のコマンドであるため、関数の戻りステータスは0ではないため、シェルのERRトラップがトリガされます。 LINENOは、実行する最後の行であり、新しい行がまだ実行されていないので、現在の行ですので、まだtraperrorの最後の行に設定されています。

のみシェルトラップがアクティブである場合(関数内の1つがコメントアウトされている)

  • に存在しないコマンドはとても非返す関数を引き起こし、関数の最後のコマンドであります-zeroになり、シェルのERRがトリガーされます。同様の理由から、LINENOは関数の最後の行であり、実行する最後の行であり、依然として現在の行です。
+0

ありがとうございますjw013、私はあなたの印象に焦点を当てたスクリプトで遊んできました。 Btw、errtraceヒント(私はまったく気づいていません)のおかげで、関数内にエラー情報を得るために入れ子にされたトラップを避けることができます。 – hmontoliu

+1

好奇心を感じる場合は、動作中のトラップERR機能についての私の編集を参照してください:-) – hmontoliu

2

、あなたのプログラムの残りの部分のためにそのデフォルトアクションにERRシグナルハンドラを無視するか、リセットすることができ、ERRシグナルハンドラが2回実行されないことを、あなたのtraperror機能のあなたの最初のバージョンで確認するために - 内ERRシグナルハンドラ自体の定義です。そして、これはカスタム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 
関連する問題