私はスクリプトを学ぶためにBashスクリプトを書いています。ある時点では、スクリプトの終了時に不要なディレクトリとファイルがクリーンアップされるようにトラップを追加する必要があります。しかし、何らかの理由でスクリプトがclean_a()
終了すると、トラップはクリーンアップ関数を呼び出しますが、スクリプトが終了したときに$LINENO
int関数ではなくクリーンアップ関数自体の行を指すことを理解していません。archieve_it()
予想される動作:
- スクリプトの実行
- Ctrl+を押すC
- トラップキャッシュCtrl+C呼び出し
clean_a()
機能 clean_a()
Ctrlこの関数は+を押した行番号をエコーしますC。 10行目にしてくださいarchieve_it()
。
実際に起こったこと:
- スクリプトの実行
- Ctrl+を押すC
- トラップキャッシュCtrl+C呼び出し
clean_a()
機能 clean_a()
無関係な行番号をエコーします。たとえば、clean_a()
関数の25行です。
以下は私のスクリプトの例です。
archieve_it () {
trap 'clean_a $LINENO $BASH_COMMAND'\
SIGHUP SIGINT SIGTERM SIGQUIT
for src in ${sources}; do
mkdir -p "${dest}${today}${src}"
if [[ "$?" -ne 0 ]] ; then
error "Something!"
fi
rsync "${options}" \
--stats -i \
--log-file="${dest}${rsync_log}" \
"${excludes}" "${src}" "${dest}${today}${src}"
done
}
clean_a () {
error "something!
line: $LINENO
command: $BASH_COMMAND
removing ${dest}${today}..."
cd "${dest}"
rm -rdf "${today}"
exit "$1"
}
PS:元のスクリプトが表示されます。ここ。定義と変数名はトルコ語です。必要に応じて何でも英語に翻訳できます。
編集する:@mikeservの説明に基づいてスクリプトをできるだけ変更しました。次のようになります。
#!/bin/bash
PS4='DEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO $BASH_COMMAND'\
SIGHUP SIGINT SIGTERM SIGQUIT
..
}
clean_a () {
error " ...
line: $LINENO $LASTNO
..."
}
スクリプトを実行してset -x
+で終了すると、次のように正しい行番号が印刷されます。CtrlC
DDEBUG: 1 : clean_a 1 336 rsync '"${options}"' ...
ただし、clean_a()
関数では値が$LASTNO
1 で印刷されます。
line: 462 1
@Arkadiusz Drabczykが表示したエラーに関連していますか?
編集2: @mikesrv 推奨通りにスクリプトを変更しました。ただし、スクリプトが終了すると、$LASTNOは行値として1を返します(337でなければなりません)。
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO "$BASH_COMMAND"' \
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
clean_a () {
error " ...
line: $LASTNO $LINENO
..."
} 2>&1
スクリプトを実行し、実行中に+を使用してrsyncを終了すると、次Ctrlの出力が表示されます。C
^^MDEBUG: 1 : clean_a '337 1 rsync "${options}" --delete-during ...
...
line: 1 465
ご覧のとおり、$LASTNOの値は1です。
問題が何であるかを調べようとしたときに、testing
パラメータ置換形式を使用する別の関数を作成しました${parameter:-default}
。したがって、スクリプトは次のようになります。
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'testing "$LASTNO $LINENO $BASH_COMMAND"'\
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
testing() {
echo -e "${1:-Unknown error!}"
exit 1
} 2>&1
スクリプトを実行してCtrl+を押すと、C次の結果が表示されます。
^^MDEBUG: 1 : testing '337 1 rsync "${options}" --delete-during ...
337 1 rsync "${options}" --delete-during ...
337は、rsyncの実行中にCtrl+を押した行を指します。C
clear_a
別のテストのために、次の関数を書いてみました。
clear_a () {
echo -e " $LASTNO $LINENO"
}
そして$ LASTNOはまだ1を返します。
それでは、パラメータ置換を使用すると、スクリプトが終了したときに正しい行番号を取得できることを意味しますか?
編集3EDIT2で@mikeservの説明を誤って適用したようです。私の間違いを修正しました。関数の位置パラメーターは"$1
$ LASTNOで置き換える必要があります。clear_a
私が望む方法で動作するスクリプトは次のとおりです。
#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
trap 'clean_a $LASTNO $LINENO "$BASH_COMMAND"' \
SIGHUP SIGINT SIGTERM SIGQUIT
...
} 2>/dev/null
clean_a () {
error " ...
line: $1
..."
} 2>&1
スクリプトが終了すると、最初のパラメータ、2番目のパラメータ、および3番目のパラメータがtrap
評価され、その値が関数に渡されます。最後に、スクリプトが終了する行番号として$ LASTNOを印刷します。$LASTNO
$LINENO
$BASH_COMMAND
clear_a
$1
ベストアンサー1
mikeservの解決策は良いですが、トラップの実行中にその行がfn
渡されるという彼の説明はtrap
正しくありません。$LINENO
前に行を挿入すると、トラップが宣言されている場所に関係なく、トラップが実際に常に転送されるtrap ...
ことがわかります。fn
1
PS4='DEBUG: $LINENO : ' \
bash -x <<\EOF
echo Foo
trap 'fn "$LINENO"' EXIT
fn() { printf %s\\n "$LINENO" "$1"; }
echo "$LINENO"
exit
EOF
出力
DEBUG: 1 : echo Foo
Foo
DEBUG: 2 : trap 'fn "$LINENO"' EXIT
DEBUG: 4 : echo 4
4
DEBUG: 5 : exit
DEBUG: 1 : fn 1
DEBUG: 3 : printf '%s\n' 3 1
3
1
トラップの最初のパラメータfn "$LINENO"
が配置されるため一つ引用する、$LINENO
得る拡大する、そして、もしEXITをトリガするので、拡張する必要がありますfn 5
。それでは、なぜできないのですか?実際、bash-4.0まではトラップがトリガされると$ LINENOが1にリセットされますfn 1
。[源泉]ただし、ERRトラップの元の動作は依然として保持されています。おそらく同様のものがあまりにも頻繁にtrap 'echo "Error at line $LINENO"' ERR
使用されるからです。
#!/bin/bash
trap 'echo "exit at line $LINENO"' EXIT
trap 'echo "error at line $LINENO"' ERR
false
exit 0
出力
error at line 5
exit at line 1
GNU bashバージョン4.3.42(1)リリース(x86_64-pc-linux-gnu)を使用してテストされました。