有没有办法检索重定向到 /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(或者可能是其他写入系统调用,例如send
、sendmsg
或pwrite
),无论 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/null
做cmd | 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
。
如果您的命令记录大量,这可能会造成严重的性能损失。