web-dev-qa-db-ja.com

エラーメッセージのプレフィックスとリダイレクトのためのprintfwork-alikeラッパー

POSIX errorシェルコードにsh関数があります。それは本質的に次のように見えます

error () {
    printf 'utility: ERROR: ' >&2
    printf "$@" >&2
}

2番目のprintfは、たとえばを使用して関数を呼び出すことができるようにします。

error 'Something relating to "%s" went wrong!\n' "$thing"
exit 1

この関数は、メッセージの前に文字列utility: ERROR:を付け、printfの場合と同じように書式設定文字列を使用できる間、すべてが標準エラーに送信されるように調整します。

明らかに、 ShellCheck 2番目のprintfについて文句を言い、

SC2059:printfフォーマット文字列で変数を使用しないでください。 printf "..%s .." "$ foo"を使用します

私がやっていることを実行して、この警告を無視するのは「安全」ですか(コードに# shellcheck disable=SC2059コメントを付けて無効化することもできます) ?注:私は常にerror関数を正確に使用するのと同じように、つまり静的な一重引用符でprintfを使用します。最初の引数。

「安全ですか?」ここで、「私の関数は、適切な方法でprintfの周りの(リダイレクトを除いて)同様のラッパーとして機能しますか?」という意味です。

1
Kusalananda

ここでは、フォーマットが引数として渡されることを意図しているので安全です。ただし、関数を使用するときは、それがフォーマットであることを覚えておく必要があります。したがって、リマインダーとして関数にerrorfという名前を付ける必要があるかもしれません。

次のように使用した場合:

_error "invalid arg: $arg"
_

_$arg_のような_%99999999s_の値のような問題が発生します

理想的には、最初の引数に変数が含まれているprintfの使用法にフラグを立てるために、printfandの使用法を無視するようにshellcheckに指示する必要があります。

いくつかの注意事項:

  • printfを2回呼び出しているので、少なくとも2回のwrite()システムコールになります。違いが生じる可能性はほとんどありません。また、printfのような一部のbash実装は、状況によってはいくつかのシステムコールを実行することに注意してください。 problem私が考えているのは、エラーメッセージの2つの部分が、並行して実行されている別のコマンドの出力と絡み合っていることです。
  • _error "%s\n" error1 error2_では、プレフィックスは最初のエラーに対してのみ出力されます。それがあなたがそれをしたい方法であるならば、それは大丈夫です。次のこともできます。

    _errorf() { printf "utility: ERROR: $@" >&2; }
    _

    プレフィックスがフォーマットの前に付加されるため、フォーマットが再利用されるたびに出力されます(これにより、複数のwrite()issueにも対応します)。

    ただし、utilityを変数にしたい場合は、これは機能しません。

    PROG_NAME = $ {0 ## * /}
    errorf(){printf>&2 "$ PROG_NAME:ERROR:$ @"; } 
    
    _$0_に_%_または_\_が含まれていると失敗するため、問題ありません。

    そして

    PROG_NAME = $ {0 ## * /}
    errorf(){
     format = $ 1; shift 
     printf>&2 "%s:ERROR:$ format" "$ PROG_NAME" "$ @" 
    }
    フォーマットが再利用される場合は機能しません(つまり、ユーザーは_%<n>$d_ディレクティブを1つオフセットする必要があります(移植性がないため、これらを使用することをお勧めしません)。 )。

    _\_と_%_を_$PROG_NAME_で手動でエスケープする以外に、簡単な回避策は考えられません。それでも、多くのprintf実装のように簡単ではありません。 、_0x5c_以外の文字にある_\_バイトもエスケープする必要があります(BIG5やGB18030などのいくつかの文字セットの場合)。

2