8

私の会社が現在使用しているいくつかの複雑なスクリプトで実装される bash ロギング ライブラリを作成しました。ログ呼び出しを行うときに、呼び出し元のスクリプトのスクリプト ファイル名 (${BASH_SOURCE}) と行番号 (${LINENO}) を提供することに行き詰まりました。ただし、これら 2 つの変数をパラメーターとして渡すために、ユーザーや実装スクリプトに依存する必要はありませんでした。これが C/C++ の場合は、"__FILE__" と "__LINE__" をパラメーター リストの先頭に追加するマクロを作成します。

私はついにこの部分を機能させることができました。概念実証として、大幅に簡略化した要約を次に示します。

ロギング ライブラリは次のとおりです。

# log.sh

LOG="eval _log \${BASH_SOURCE} \${LINENO}"

_log () {
    _BASH_SOURCE=`basename "${1}"` && shift
    _LINENO=${1} && shift

    echo "(${_BASH_SOURCE}:${_LINENO}) $@"
}

そして、実装テスト スクリプト:

# MyTest.sh

. ./log.sh

${LOG} "This is a log message"
# (test.sh:5) This is a log message

これはかなりうまく機能します (そして、最初は機能することにわくわくしました)。ただし、これには 1 つの明らかな問題があります。引用符と eval の間の相互作用です。私が電話をかける場合:

${LOG} "I'm thrilled that I got this working"
# ./test.sh: eval: line 5: unexected EOF while looking for matching `''
# ./test.sh: eval: line 6: syntax error: unexpected end of file

今、私はこれがなぜ起こっているのかを理解していると信じています。引用符で囲まれたパラメーターは、eval に渡されるときにそのまま保持されますが、その時点で、コンテンツは結果のコマンド文字列にそのまま配置されます。エスケープを行うことでこれを修正できることはわかっています。ただし、実装するスクリプトにこれを強制する必要はありません。この「評価マクロ」機能を実装する前に、ユーザーに「_log」を直接呼び出してもらい、オプションで「${LINENO}」を渡せるようにしました。この実装では、上記の失敗した呼び出し (引用文のみ) は問題なく機能しました。

最も基本的なレベルでは、スクリプトが[ログ関数/マクロ] 「特殊文字を使用してログに記録する文字列」を呼び出し、結果のログ メッセージに呼び出し元のスクリプトのファイル名と行番号を含めることができるようにするだけです。ログメッセージが続きます。可能であれば、私は非常に近いと思いますが、別のアプローチが必要な見落としがある場合は、それも受け入れます. ユーザーがすべてのメッセージをエスケープするように強制することはできません。これにより、ユーザーはこのライブラリを使用しなくなる可能性があります。これは非常に問題であるため、解決策が見つからない場合は、古い機能に戻る可能性があります (関数パラメーターとして ${LINENO} を渡す必要がありましたが、これはそれほど邪魔になりませんでした)。

TLDR :引用符で囲まれたパラメーター内の特殊文字をエスケープせずに尊重するように eval を取得する方法はありますか?

4

3 に答える 3

16

可能であれば避けることをお勧めしevalます。ロギングのユースケースについては、シェルビルトインをご覧callerください。さらに情報が必要な場合は、変数、、およびを使用BASH_SOURCEできBASH_LINENOますFUNCNAME。これらの変数はすべて配列であり、完全な呼び出しスタックが含まれていることに注意してください。次の例を参照してください。

#! /bin/bash       

function log() {
    echo "[$( caller )] $*" >&2
    echo "BASH_SOURCE: ${BASH_SOURCE[*]}"
    echo "BASH_LINENO: ${BASH_LINENO[*]}"
    echo "FUNCNAME: ${FUNCNAME[*]}"
}

function foobar() {
    log "failed:" "$@"
}

foobar "$@"
于 2012-05-22T18:01:25.220 に答える
2

(注:これにより、引用の差し迫った問題が解決されますが、コールスタックへのアクセスに関する@nosidの回答ははるかに優れています)

_log位置パラメータからログ メッセージを取得する代わりに、標準入力から読み取るように、定義を少し変更します。

_log () {
    # Set up _BASH_SOURCE and _LINENO the same way

    cat <(echo -n "$(_BASH_SOURCE:$_LINENO) ") -
}

次に、ヒア ドキュメントまたはヒア文字列を使用して、標準入力経由でログ メッセージを渡します。

${LOG} <<<"This is a log message"
${LOG} <<<"I'm thrilled this works, too!"
${LOG} <<HERE
Even this long
message works as
intended!
HERE
于 2012-05-22T18:32:54.357 に答える