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


10

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

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

我将如何以POSIX /bin/sh兼容的方式进行操作?

Answers:


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")"

(这还将记录从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")" &

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

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"

对于整个脚本,这将在syslog中显示为两个条目(out和err)。问题中的示例每个命令将有一个(或两个)条目。
DarkHeart

@DarkHeart我不明白您的评论。问题中的代码和我建议的两个解决方案都运行相同数量的实例,logger并将相同的输入传递给每个实例。
吉尔斯(Gillles)“所以-别再邪恶了”

抱歉,我的错
DarkHeart

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 shell并不能直接为此类事情提供推论,但模拟它非常简单。您要做的就是创建一个文件,用命令替换输出其名称为标准名称,并在相同的背景下运行命令,该命令将输出到该文件。现在,你可以重定向到该扩张的价值- 正是因为你进程替换做。因此,POSIX Shell当然提供了您需要的所有工具-所需要做的就是以适合您的方式使用它们。

以上两个版本均确保在使用它们之前销毁了它们创建/使用的管道的文件系统链接。这意味着事后不需要清理,更重要的是,它们的流仅可用于最初打开它们的进程-因此,它们的文件系统链接不能用作监听/劫持日志记录活动的手段。将其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

@ l0b0-我将要更新另一种类似的方法...这是更通用的方法,我终于让它能够处理一些与I / O描述符/文件有关的选项。
mikeserv

@ 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

我更改了它,因为我只需要它在现代平台上工作即可。主要的问题是,.xprofile 与执行sh
l0b0

1
@Wildcard-只需一分钟,我将尝试整理问题的第一部分,但第二部分的答案是肯定的:最重要的情况是zsh启用了w / multios。至于第一部分:{ this; can\'t; happen; before; } < this。有帮助吗?
mikeserv
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.