我经常用来tee /proc/self/fd/2
向 stdout 和 stderr 显示一些内容,捕获 stdout,同时仍将所有输出保留到 stderr。
例如,我有一个do.sh
:
STD_OUT_STR=$(CMD ARGS ... 2>&1 | tee /proc/self/fd/2)
# handle STD_OUT_STR ...
...
这效果非常好。
例如,
./do.sh
基本上是这样运行的:
bash -c 'echo hi | tee /proc/self/fd/2'
输出
hi
hi
然而,有一天我在 下运行它sudo -u A_USER ./do.sh
,它失败了。
sudo -u A_USER ./do.sh'
其本质上是运行:
sudo -u A_USER bash -c 'echo hi | tee /proc/self/fd/2'
输出
tee: /proc/self/fd/2: Permission denied
hi
到目前为止,我必须遵循以下解决方法:
sudo -u A_USER bash -c 'echo hi | tee >(cat >&2)'
我知道这是出于安全原因:不与其他用户共享当前用户的 pty。
我想知道是否有任何选项sudo -u A_USER
可以让进程访问其 pty?
编辑:我需要将其放入tee /proc/self/fd/2
do.sh 脚本中,即我不能将其放在tee /proc/self/fd/2
do.sh 之外,所以sudo -u A_USER bash -c '...' | tee ...
对我没有帮助。
答案1
sudo -u A_USER bash -c 'cmd | tee /dev/fd/2'
或者:
sudo -u A_USER sh -c 'cmd | tee /dev/fd/2'
因为里面没有任何特定于 bash 的内容,所以可以在系统上运行以外Linux 或 Cygwin。
虽然在大多数系统上,打开/dev/fd/x
就像在Linux 或 Cygwin 上一样,dup(x)
相同,只是指向在相应文件描述符上打开的文件的“神奇”符号链接,因此打开它们不会获得与根本不。>&x
sh
/dev/fd/x
/proc/self/fd/x
dup()
fd 2 很可能在您无权打开写入的文件上打开,就像您的情况一样,或者根本不能像套接字一样打开(通常是由systemd
stderr 是套接字启动的进程的情况)到journald
)。
即使它是您具有写入权限的常规文件,执行tee /proc/self/fd/2
或 也是tee /dev/fd/2
错误的,因为它从头开始打开相应的文件并截断它。 Eventee -a /proc/self/fd/2
是错误的(尽管实际上更好一些),因为虽然有如此多的截断并且它导致在文件末尾完成写入,但只有在 stderr 也以其他方式打开时才有效附加模式。
例如,之后:
(
echo test | tee -a /proc/self/fd/2
echo hi >&2
) 2> file
$ cat file
hi
t
的输出hi
是test
通过 写入的tee
。
在 Linux/Cygwin 上,只有当相应的 fd 在某些不可查找的文件(例如管道或 tty 设备)上打开时,使用/dev/fd/x
/ /proc/self/fd/x
(或/dev/stdout
, /dev/stderr
)才可以接受,但即使如此,您也可能会遇到像这里这样的权限问题。
您tee >(cat >&2)
的 shell 会将您的 shell 转换为在管道上打开的某个tee /proc/self/fd/somefd
位置somefd
,并且由于管道是由写入该管道的用户创建的,因此不存在权限问题,但在 bash shell 中这样做仍然是错误的,特别是像 bash 那样不等待那个cat
过程。
后:
(bash -c 'echo test | tee >(cat>&2)'; echo hi >&2) 2> file
你会发现有时file
包含:
hi
tee
因为bash
之前已经返回并cat
成功写入test
并echo
输出了它的hi
第一个。
在这里,您可以使用zsh
而不是bash
可以对多个文件描述符进行发球,而不是像tee
with 那样仅对文件进行发球:
sudo -u A_USER zsh -c 'echo hi >&1 >&2'
当 fd(此处为 1/stdout)多次重定向以进行输出时,它会被重定向到一个管道,该管道通向执行发球的内部进程。
sudo -u A_USER zsh -c 'echo hi | tee >(cat >&2)'
无需 bash 的上述警告即可工作,但这些tee
警告cat
是不必要的,因为 zsh 具有内置功能。
如果zsh
是调用 shell,您可以让调用用户而不是 USER_A 完成内部发球:
sudo -u A_USER sh -c 'echo hi' >&1 >&2
在 Linux/Cygwin 上哪个会比
sudo -u A_USER sh -c 'echo hi' | tee -a /dev/fd/2
本身比
sudo -u A_USER sh -c 'echo hi' | tee /dev/fd/2
(尽管他们也解决了许可问题)出于上述原因。
使用sh
(不能使用进程替换) + tee
,将 stderr 转换为管道以解决权限问题的解决方案是手动执行此操作:
sudo sh -c '
{
{
cmd |
tee /dev/fd/2
} 2>&1 >&3 3>&- |
cat >&2 3>&-
} 3>&1'
在内部命令组内部,stderr 成为 的管道cat
,由 共享cmd
,尽管您也可以将cmd
的 stderr 单独保留:
sudo sh -c '
{
{
cmd 3>&- |
tee /dev/fd/3
} 3>&1 >&4 4>&- |
cat >&2 4>&-
} 4>&1'
这将与您的情况更接近,cmd | tee >(cat >&2)
尽管在这里cat
已经得到了适当的等待。
答案2
就按照你自己的样子跑吧tee
。
考虑这个命令:
$ sudo -u A_USER bash -c 'ls -l *.txt 2>&1'
它将文本行发送到标准输出。
无需tee
以其他用户身份运行。接受这些台词并像你自己一样处理它们。sudo
如果您稍后在管道中确实需要这些权限,请再次调用。
$ sudo -u A_USER bash -c 'ls -l *.txt 2>&1' | tee /proc/self/fd/2 | awk '{sum += $5} END {print sum}'
如果遇到困难,您的脚本始终可以写入文件系统,然后写入另一个进程中的文本文件cat
。tail -f
文本生成器始终可以使用 标准缓冲区 如果需要,可以调整行缓冲并立即显示结果。