如何重定向一个已经运行的进程的输出

184

通常我会像这样开始一个命令:

longcommand &;

我知道你可以通过类似以下的方式重定向:

longcommand > /dev/null;

例如,要摆脱输出或者...

longcommand 2>&1 > output.log

捕获输出。

但有时我会忘记,想知道是否有一种方法可以在事后捕获或重定向。

longcommand
ctrl-z
bg 2>&1 > /dev/null
或者类似的东西,这样我就可以继续使用终端而不会弹出终端上的消息。

2
这个问题可能不适合在SO上提问。我猜SU是最好的选择,尽管SF也有更好的回答机会。顺便说一下,我的最佳理解是:如果disown无效,那么你就没有办法了。 - dmckee --- ex-moderator kitten
1
你可能是想要使用 > 而不是 |。管道符两边都需要可执行文件。如果你想要重定向到一个文件或设备(例如 /dev/null),你需要使用 > 和 < 符号。 - Tyler McHenry
请返回翻译后的文本:https://dev59.com/e3VC5IYBdhLWcg3wliGe http://stackoverflow.com/questions/682783/linux-redirect-stdout-after-the-process-started - mark4o
1
reptyr - lsl
我知道这是一个变通方法,但你也可以执行 shopt huponexit 退出并重新登录。 - jimh
5个回答

164

请参考Redirecting Output from a Running Process以了解更多信息。

首先,在一个会话窗口中运行命令cat > foo1,并测试将stdin中的数据复制到文件中。然后在另一个会话中重定向输出。

首先找到该进程的PID:

$ ps aux | grep cat
rjc 6760 0.0 0.0 1580 376 pts/5 S+ 15:31 0:00 cat

现在检查它打开的文件句柄:

$ ls -l /proc/6760/fd
total 3
lrwx—— 1 rjc rjc 64 Feb 27 15:32 0 -> /dev/pts/5
l-wx—— 1 rjc rjc 64 Feb 27 15:32 1 -> /tmp/foo1
lrwx—— 1 rjc rjc 64 Feb 27 15:32 2 -> /dev/pts/5

现在运行GDB:

$ gdb -p 6760 /bin/cat
GNU gdb 6.4.90-debian

[license stuff snipped]

Attaching to program: /bin/cat, process 6760

[snip other stuff that's not interesting now]

(gdb) p close(1)
$1 = 0
(gdb) p creat("/tmp/foo3", 0600)
$2 = 1
(gdb) q
The program is running. Quit anyway (and detach it)? (y or n) y
Detaching from program: /bin/cat, process 6760
在 GDB 中,p 命令将打印表达式的值,该表达式可以是要调用的函数,也可以是系统调用...所以我执行了一个 close() 系统调用并传递文件句柄 1,然后我执行了一个 creat() 系统调用来打开一个新文件。 creat() 的结果为 1,这意味着它替换了先前的文件句柄。如果我想要使用相同的文件作为 stdout 和 stderr,或者如果我想要替换文件句柄为其他数字,则需要调用 dup2() 系统调用以实现该结果。
在这个例子中,我选择使用 creat() 而不是 open(),因为参数较少。C 标志的宏无法从 GDB 中使用(它不使用 C 头文件),因此我必须阅读头文件才能发现这一点-这并不难做,但需要更多的时间。请注意,0600 是所有者具有读/写访问权限,组和其他人没有访问权限的八进制权限。也可以使用 0 作为该参数,并在后面运行 chmod 对文件进行更改。
之后我验证了结果:
ls -l /proc/6760/fd/
total 3
lrwx—— 1 rjc rjc 64 2008-02-27 15:32 0 -> /dev/pts/5
l-wx—— 1 rjc rjc 64 2008-02-27 15:32 1 -> /tmp/foo3 <====
lrwx—— 1 rjc rjc 64 2008-02-27 15:32 2 -> /dev/pts/5

cat中输入更多数据会将文件/tmp/foo3附加到其中。

如果您想关闭原始会话,您需要关闭它的所有文件句柄,打开一个可以成为控制终端的新设备,然后调用setsid()


31
最好在此处复制其要点,而不仅是提供链接。 - dmckee --- ex-moderator kitten
6
要点:使用gdb连接到正在运行的进程,然后执行'p close(1)'和'p creat("/tmp/foo3", 0600)'来重定向标准输出流,最后在程序继续运行的情况下断开gdb连接。这是一个聪明的技巧! - cjm
7
如果我可以为每次使用你帖子中的链接给你投票,你至少能进入前100名。顺便问一下,你不觉得想要离开“userXXX联盟”吗? - khael
2
这个解决方案可行,但我必须使用 root 身份运行 gdb,否则进程无法附加。 - Seth Difley
3
creat调用是如何知道它需要覆盖文件句柄1(即stdout)的? (翻译说明:将原文中的缩写“stdout”和数字“1”翻译成了中文,其他未提及的术语按照通用惯例不翻译。) - iggy
显示剩余6条评论

30

你也可以使用reredirect (https://github.com/jerome-pouiller/reredirect/)来完成此操作。

以下命令将进程PID的输出(标准输出和错误输出)重定向到FILE

reredirect -m FILE PID

reredirectREADME 还介绍了其他有趣的功能:如何恢复进程的原始状态,如何重定向到另一个命令或仅重定向 stdout 或 stderr。

该工具还提供了 relink,这是一个脚本,允许将输出重定向到当前终端:

relink PID
relink PID | grep usefull_content

reredirect似乎具有与另一个答案中描述的Dupx相同的功能,但它不依赖于Gdb)。


关于 reredirect 的好处还在于,一旦你完成操作,它还可以让你恢复之前的状态。 - lemonsqueeze
请注意,在运行上述命令之前,文件不应该存在,否则reredirect会挂起。否则它是一个非常有用的程序。 - nimrodm
有没有想法可以重定向输出,而不会影响进程当前在终端上的输出? - Mateen Ulhaq

16

Dupx

Dupx是一个简单的*nix实用程序,可以重定向已经运行的进程的标准输出/输入/错误。

动机

我经常发现自己处于这样一种情况:通过SSH在远程系统上启动的进程比我预期的时间长得多。我需要中断SSH连接,但如果我这样做,如果进程试图在破损的管道的stdout/error上写入某些内容,它将会死亡。我希望我能用^Z暂停进程,然后执行

bg %1 >/tmp/stdout 2>/tmp/stderr 

很遗憾,这在我所知道的Shell中不起作用。

http://www.isi.edu/~yuri/dupx/


12

屏幕

如果进程在屏幕会话中运行,则可以使用屏幕的日志命令将该窗口的输出记录到文件中:

切换到脚本窗口,C-a H 开始记录。
现在您可以:

$ tail -f screenlog.2 | grep whatever

来自screen手册页面:

log [on|off]

启动/停止将当前窗口的输出写入到名为“screenlog.n”的文件中,其中n是当前窗口的编号,该文件位于窗口的默认目录中。可以使用“logfile”命令更改此文件名。如果未给出参数,则切换日志记录的状态。如果文件已经存在,则会将会话日志附加到文件的前一个内容中。当前内容和回滚历史记录的内容不包括在会话日志中。默认值为“off”。

我确信tmux也有类似的东西。


1

我在互联网上收集了一些信息,并准备了一个不需要外部工具的脚本:在这里查看我的回复。希望对您有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接