すべての出力をPOSIXシェルの「ロガー」に送信する方法は?


10

私はで別々に標準出力と標準エラーを記録したいと思います.xprofile使用しますlogger。バッシュでは、次のようになると思います。

exec 1> >(logger --priority user.notice --tag $(basename $0)) \
     2> >(logger --priority user.error --tag $(basename $0))

POSIX /bin/sh互換の方法でそれをどのように行うのですか?

回答:


10

同等のPOSIXはありません。リダイレクトexecは、フォークではなくでのみ実行できます。パイプはフォークを必要とし、シェルは子が完了するのを待ちます。

1つの解決策は、すべてのコードを関数に入れることです。

all_my_code () {
  
}
{ all_my_code |
  logger --priority user.notice --tag "$(basename "$0")"; } 2>&1 | 
  logger --priority user.error --tag "$(basename "$0")"

(これにより、ロガーのstdoutインスタンスからstderrインスタンスへのエラーもログに記録されます。より多くのファイル記述子シャッフルでこれを回避できます。)

loggerプロセスがまだ実行されている場合でも親シェルを終了する場合&は、logger呼び出しの最後に置きます。

{ all_my_code |
  logger --priority user.notice --tag "$(basename "$0")" & } 2>&1 | 
  logger --priority user.error --tag "$(basename "$0")" &

または、名前付きパイプを使用できます。

pipe_dir=$(mktemp -d)
mkfifo "$pipe_dir/out" "$pipe_dir/err"
<"$pipe_dir/out" logger --priority user.notice --tag "$(basename "$0")" &
<"$pipe_dir/err" logger --priority user.error --tag "$(basename "$0")" &
exec >"$pipe_dir/out" 2>"$pipe_dir/err" 

rm -r "$pipe_dir"

これは、スクリプト全体の2つのエントリ(outおよびerr)としてsyslogに表示されます。質問の例では、コマンドごとに1つ(または2つ)のエントリがあります。
DarkHeart 2015

@DarkHeart私はあなたのコメントを理解できません。問題のコードと私が提案する両方のソリューションは、同じ数のインスタンスを実行し、loggerそれぞれに同じ入力を渡します。
Gilles「SO-邪悪なことをやめなさい」2015

すみません、私の間違い
DarkHeart 2015

1
これはもっと簡単見えるので、私はあなたの名前付きパイプソリューションを使用します。
l0b0

9

POSIXコマンド/プロセス置換


_log()( x=0
    while  [ -e "${TMPDIR:=/tmp}/$$.$((x+=1))" ]
    do     continue; done        &&
    mkfifo -- "$TMPDIR/$$.$x"    &&
    printf %s\\n "$TMPDIR/$$.$x" || exit
    exec >&- >/dev/null
    {  rm -- "$TMPDIR/$$.$x"
       logger --priority user."$1" --tag "${0##*/}"
    }  <"$TMPDIR/$$.$x" &
)   <&- </dev/null

あなたはそれを次のように使うことができるはずです:

exec >"$(_log notice)" 2>"$(_log error)"

mktempコマンドを使用するバージョンは次のとおりです。

_log()( p=
    mkfifo "${p:=$(mktemp -u)}"    &&
    printf %s "$p"                 &&
    exec  <&- >&- <>/dev/null >&0  &&
    {   rm "$p"
        logger --priority user."$1" --tag "${0##*/}"
    }   <"$p" &
)

...ほとんど同じmktempですが、ファイル名を選択できる点が異なります。これが機能するのは、プロセスの置換が魔法ではなく、コマンドの置換と非常によく似ているためです。コマンド置換のように展開内で実行されるコマンドの値で展開を置き換えるのではなく、プロセス置換は、出力が見つかるファイルシステムリンクの名前に置き換えます。

POSIXシェルはそのようなものへの直接の帰結を提供しませんが、それをエミュレートすることは非常に簡単に行われます。必要なのは、ファイルを作成し、その名前をコマンド置換から標準に出力し、同じ実行のバックグラウンドでそのファイルに出力するコマンドを実行することだけです。これで、その拡張の値にリダイレクトできます- プロセス置換の場合とまったく同じです。そして、POSIXシェルはもちろん、必要なすべてのツールを提供します-必要なのは、自分に合った方法でそれらを使用することだけです。

上記のバージョンはどちらも、作成または使用するパイプへのファイルシステムリンクを破棄してから、それらを使用する前に確実に破棄します。これは、事後にクリーンアップが必要ないことを意味します。さらに重要なことに、それらのストリームは最初にそれらを開くプロセスでのみ使用できます。したがって、それらのファイルシステムリンクは、ロギングアクティビティをスヌープ/ハイジャックする手段として使用できません。ファイルシステムにfsリンクを残すことは、潜在的なセキュリティホールです。


別の方法は、それをラップすることです。スクリプト内から実行できます。

x=${x##*[!0-9]*}
_log(){ 
    logger --priority user."$1" --tag "${0##*/}"
}   2>/dev/null >&2

cd ../"$PPID.$x" 2>/dev/null &&
trap 'rm -rf -- "${TMPDIR:-/tmp}/$PPID.$x"' 0 || 
{   until cd -- "${TMPDIR:=/tmp}/$$.$x"
    do    mkdir -- "$TMPDIR/$$.$((x+=1))"
    done  && 
    x=$x "$0" "$@" | _log notice
    exit
}   2>&1 | _log error

これにより、スクリプトがまだ呼び出されていない場合に基本的にスクリプトがそれ自体を呼び出し、一時的に作業ディレクトリを起動することができます。


1
完了、ありがとうございます!
l0b0 2015

@ l0b0-私はこれのような別の種類を更新しようとしています...それはより一般的な目的であり、私はようやく、I / O記述子/ファイルに関連するいくつかのオプションを処理する機能をそれに与えました。
mikeserv 2015

@ l0b0- mktempその他の非標準コマンドを使用したい場合は、次のようになります_log()( p=; mkfifo "${p:=$(mktemp -u)}"; printf %s "$p"; exec <&- >&- <>/dev/null >&0; { rm "$p"; logger --priority user."$1" --tag "${0##*/}"; } <"$p" &)。私は、完全に移植可能なコマンド言語を使って、自分の例外を除いて、あらゆる方法でそれを書きました。また、basenameあまり有用なユーティリティではありません。"${0##*/}"より堅牢で高速です。
mikeserv 2015

私はそれを現代のプラットフォームで動作するために必要なだけなので、それを変更しました。主な問題は、それだっ.xprofile 持って実行されますsh
l0b0

1
@ワイルドカード-ちょっと待って、質問の最初の部分を整理しようとしますが、2番目の答えはイエスです:エッジケースはzshマルチOS対応です。最初の部分については:{ this; can\'t; happen; before; } < this。それは役に立ちますか?
mikeserv 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.