如何将所有输出发送到 POSIX shell 中的“logger”?

如何将所有输出发送到 POSIX shell 中的“logger”?

我想在.xprofile使用时分别记录标准输出和标准错误logger。在 Bash 中,我认为这看起来像这样:

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

我该如何在POSIX中做到这一点 /bin/sh兼容方式?

答案1

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 shell 没有提供此类事情的直接推论,但对其进行模拟却非常简单。您需要做的就是创建一个文件,通过命令替换将其名称打印到标准,然后在同一后台运行您的命令,该命令将输出到该文件。现在您可以重定向到该扩展的值 -确切地就像您对流程替换所做的那样。因此,POSIX shell 当然提供了您需要的所有工具 - 所需要的只是您以适合您的方式使用它们。

上述两个版本都确保它们在使用它们之前销毁它们创建/使用的管道的文件系统链接。这意味着事后不需要清理,更重要的是,它们的流仅适用于最初打开它们的进程 - 因此它们的文件系统链接不能用作窥探/劫持您的日志记录活动的手段。将它们的文件系统链接留在文件系统中是一个潜在的安全漏洞。


另一种方法是包裹它。它可以在脚本内完成。

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

这基本上将允许您的脚本调用自身(如果尚未调用)并为您提供一个临时工作目录来启动。

答案2

没有 POSIX 等效项。您只能使用 执行重定向exec,而不能使用 fork。管道需要叉子,外壳等待子进程完成。

一种解决方案是将所有代码放在一个函数中。

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

(这还会将记录器的 stdout 实例中的任何错误记录到 stderr 实例。您可以通过更多的文件描述符改组来避免这种情况。)

如果您希望父 shell 在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"

相关内容