检索 std 输出重定向到 /dev/null

检索 std 输出重定向到 /dev/null

有没有办法检索重定向到 /dev/null 的 std 输出?我试过了tail -f /proc/{PID}/fd/1,看起来除了重定向到之外只能工作/dev/null

IE

tail -f /proc/${cmd_pid}/fd/1

有效cmd > log.txt但无效cmd > /dev/null

- - - - - - 更新 - - - - - -

事实上,log.txt如果总是记录标准输出,我的问题将会非常大。如果我可以控制何时记录或停止日志(而不停止进程cmd本身),那将是最好的。

那么是否可以将标准输出重定向到某个tmp不占用资源的文件系统,并且当我需要时我可以检索标准输出?

答案1

当您这样做时cmd > /dev/null,shell 在一个新的子进程中打开 fd 1/dev/null并执行cmd

cmd执行一些write(1, "output"...)操作将数据写入 stdout(或者可能是其他写入系统调用,例如sendsendmsgpwrite),无论 fd 在什么上打开。

在 Linux 上,/proc/$pid_of_cmd/fd/1将只是一个神奇的符号链接/dev/null,文件 fd 1 在其上打开,因此tail -f that将与 相同tail -f /dev/null

您可以拦截这些write()系统调用strace并解码结果:

strace -zqqs99999 -xxa0 -o /dev/stdout -e write -p "$pid" |
   perl -ne 'print chr(hex($_)) for /\\x(..)/g'

或者:

strace -zqqs99999 -xxa0 -o /dev/stdout -e write -p "$pid" |
   perl -ne 'if (/^write\(1,/) {print chr(hex($_)) for /\\x(..)/g}'

仅适用于write()s 到 stdout (fd 1)。或者:

strace -yzqqs99999 -xxa0 -o /dev/stdout -e write -p "$pid" |
   perl -ne 'if (/^\Qwrite(1<\x2f\x64\x65\x76\x2f\x6e\x75\x6c\x6c>,\E "(.*)"/) {
     print chr(hex($_)) for $1 =~ /\\x(..)/g}'

仅适用于write()s 到 stdout (fd 1) 并且仅当 stdout 在 上打开时/dev/null

请注意,如果您将 的输出重定向perl到 tty 设备以外的设备,它将开始按块而不是按行缓冲,因此您只能看到几 KB 的批量输出。您可以通过将$|变量设置为来禁用该缓冲1

strace -zqqs99999 -xxa0 -o /dev/stdout -e write -p "$pid" |
   perl -ne 'BEGIN{$| = 1}
             print chr(hex($_)) for /\\x(..)/g' > file

或者,不做 ,而是cmd > /dev/nullcmd | cat > /dev/null

那么cmd的 stdout 将是一个管道,在 Linux 上,/proc/$pid_of_cmd/fd/1将表现得像一个命名管道,所以如果你这样做:

kill -s STOP "$pid_of_cat"
cat "/proc/$pid_of_cmd/fd/1"

您实际上会将管道重定向到新cat命令。

如果终止该新的cat,您将需要恢复另一个 ( ) 以重新打开该管道上的kill -s CONT "$pid_of_cat"水龙头。/dev/null

要实际更改进程的标准输出的位置,您可以附加一个调试器:

gdb --pid "$pid_of_cmd"

然后在gdb中:

call close(1)
p open("/path/to/some/file", 0x241, 0600)
detach

(0x241 表示 O_WRONLY|O_TRUNC|O_CREAT)

检查是否open()返回 fd 1。如果没有,您可能需要一些调用dup2()和额外的close().

答案2

不,您无法更改文件句柄。

那么是否可以将标准输出重定向到某个不占用资源的 tmp 文件系统,并且当我需要时我可以检索标准输出?

不,如果不存储数据就无法存储数据某处

有人可能写了一个循环缓冲区 - 它可以让你看到(比如说)最后 100k 的数据。或者,如果程序通常无头运行,您可以将输出发送到屏幕并通过 运行它screen

答案3

当我不检索它时,标准输出可能会被丢弃,当我开始检索时,标准输出可能会重定向到日志文件。

这可以通过将输出传输到 shell 脚本来完成,如下所示:

#! /bin/sh
# a-script.sh
while IFS= read -d input
do
  printf "%s\n" input >> /some/file
done 

请注意,重定向是在循环内完成的,特别是为了在每次迭代中重新打开输出文件。然后,开始:

ln -s /dev/null /some/file
cmd | a-script.sh

然后当你想要日志时:

ln -sf /some/log/file /some/file

循环的下一次迭代将开始写入 to/some/log/file而不是 to /dev/null

如果您的命令记录大量,这可能会造成严重的性能损失。

相关内容