bashでstdoutまたはファイルをエコーするエレガントなソリューション

bashでstdoutまたはファイルをエコーするエレガントなソリューション

いくつかの結果を生成するbashアプリケーションがあり、stdoutユーザーが選択したファイルに結果をエコーし​​たいと思います。他の対話型メッセージも画面に表示されるため、>結果をファイルに表示しようとするときにユーザーに明示的にリダイレクトを使用するように要求することはオプション(*)ではありません。そのメッセージもファイルに表示されるためです。

これで解決策がありましたが、見苦しいです。

if [ -z $outfile ]
then
    echo "$outbuf"    # Write output buffer to the screen (stdout)
else
    echo "$outbuf" > $outfile  # Write output buffer to file
fi

私は、toおよび他のものと$outfile同じ変数を持つことを試みましたが、実際にstdoutに書き込むのではなく、その名前のファイルにのみ書き込みます。よりエレガントなソリューションはありますか?stdout&1

(*) こんな目的でだまされて使うこともできますが、stderrそれも醜いと思いますか?

ベストアンサー1

まず、あなたはecho任意のデータ出力を防止

Linuxベースのシステム以外のシステムでは、次のものを使用できます。

logfile=/dev/stdout

Linuxでは、これはいくつかのタイプのstdoutで動作しますが、stdoutがソケットの場合は失敗します。または、stdoutが通常のファイルの場合、stdoutが現在作成されているファイルの場所にあるのではなく、ファイルが切り捨てられます。

それ以外の場合、Bourneのようなシェルには方法はありませんホームフレーズリダイレクト(次が利用可能eval

eval 'printf "%s\n" "$buf" '${logfile:+'> "$logfile"'}

変える変える、専用使用可能ファイル記述子:

exec 3>&1
[ -z "$logfile" ] || exec 3> "$logfile"

 printf '%s\n' "$buf" >&3

これの1つの(マイナーな)欠点は、kshfd 3を除いて、スクリプトで実行されるすべてのコマンドでリークされることです。を使用すると、代わりにアクションを実行zshできますが、それに対応するアクションはありません。sysopen -wu 3 -o cloexec -- "$logfile" || exitexec 3> "$logfile"bash

別の一般的なイディオムは、次の関数を使用することです。

log() {
  if [ -n "$logfile" ]; then
    printf '%s\n' "$@" >> "$logfile"
  else
    printf '%s\n' "$@"
  fi
}

log "$buf"

おすすめ記事