/proc/self/fd/2 无法在 `sudo -u other_user` 下访问

/proc/self/fd/2 无法在 `sudo -u other_user` 下访问

我经常用来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/2do.sh 脚本中,即我不能将其放在tee /proc/self/fd/2do.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)相同,只是指向在相应文件描述符上打开的文件的“神奇”符号链接,因此打开它们不会获得与根本不。>&xsh/dev/fd/x/proc/self/fd/xdup()

fd 2 很可能在您无权打开写入的文件上打开,就像您的情况一样,或者根本不能像套接字一样打开(通常是由systemdstderr 是套接字启动的进程的情况)到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

的输出hitest通过 写入的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成功写入testecho输出了它的hi第一个。

在这里,您可以使用zsh而不是bash可以对多个文件描述符进行发球,而不是像teewith 那样仅对文件进行发球:

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}' 

如果遇到困难,您的脚本始终可以写入文件系统,然后写入另一个进程中的文本文件cattail -f

文本生成器始终可以使用 标准缓冲区 如果需要,可以调整行缓冲并立即显示结果。

相关内容