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

l0b*_*0b0 10 shell pipe posix

我想在.xprofile使用logger. 在 Bash 中,我认为这看起来像这样:

exec 1> >(logger --priority user.notice --tag $(basename $0)) \
     2> >(logger --priority user.error --tag $(basename $0))
Run Code Online (Sandbox Code Playgroud)

我将如何以POSIX /bin/sh兼容的方式做到这一点?

Gil*_*il' 10

没有 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")"
Run Code Online (Sandbox Code Playgroud)

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

如果您希望父 shell 在logger进程仍在运行时退出,请将其放在调用&的末尾logger

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

或者,您可以使用命名管道。

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"
Run Code Online (Sandbox Code Playgroud)


mik*_*erv 10

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
Run Code Online (Sandbox Code Playgroud)

你应该能够像这样使用它:

exec >"$(_log notice)" 2>"$(_log error)"
Run Code Online (Sandbox Code Playgroud)

这是一个使用mktemp命令的版本:

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

...它的作用大致相同,只是它允许mktemp为您选择文件名。这是有效的,因为进程替换绝不是神奇的,它的工作方式与命令替换非常相似。进程替换不是像命令替换那样用其中运行的命令的值替换扩展,而是用可以找到输出的文件系统链接的名称替换它。

虽然 POSIX shell 没有为这样的事情提供直接的推论,但模拟它是非常简单的。您需要做的就是创建一个文件,通过命令替换将其名称打印到标准,并在相同的后台运行您的命令,该命令将输出到该文件。现在,你可以重定向到该扩张的价值-正是因为你进程替换做。因此,POSIX shell 提供了您当然需要的所有工具——所需要的只是您以适合您的方式使用它们。

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


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

exec >"$(_log notice)" 2>"$(_log error)"
Run Code Online (Sandbox Code Playgroud)

这基本上允许您的脚本在尚未调用时自行调用,并为您提供临时工作目录以进行启动。