重定向已运行进程的输出
1. 简介
在 Linux 中工作时,使用重定向 非常常见。例如,我们可以运行一个程序并在我们执行它时将其生成的输出静音。 但是,bash不提供任何直接方法来在进程运行后重定向输出。因此,在本文中,我们将学习重定向或复制正在运行进程的输出的不同方法。这可能很有用,例如,如果我们在执行程序时忘记重定向输出。
为此,我们将研究三种不同的方法——第一种使用gdb ,另一种使用strace ,最后,第三种使用*screen *。第三种方法会有点不同。
2. 使用gdb重定向输出
gdb是 Linux 中一个强大的代码调试工具,我们可以使用它在正在运行的进程上执行代码。在这种情况下,我们将使用它来重定向输出,方法是将gdb附加到正在运行的进程,重定向所需的输出,最后在完成后从正在运行的进程中分离出来。
我们可以重定向输出的方法是关闭当前文件描述符,然后重新打开它,指向新的输出。我们将使用*open *和dup2 函数来执行此操作。
Unix 系统中有两个默认输出,stdout和stderr。stdout与文件描述符1和stderr关联到2。因此,例如,如果我们想要重定向标准输出,我们需要对文件描述符1进行操作。
**假设我们要重定向 PID 为 14560 的进程的输出。**因此,我们应该首先将gdb附加到该 PID:
$ gdb -p 14560
一旦我们附加到进程,我们可以将文件描述符 1重定向到*/tmp/process_stdout*:
(gdb) p dup2(open("/tmp/process_stdout", 1089, 0777), 1)
请注意,我们使用了数字1089,因为它代表O_WRONY | O_CREAT | O_APPEND。
我们也可以通过使用2作为文件描述符以完全相同的方式重定向标准错误:
(gdb) p dup2(open("/tmp/process_stderr", 1089, 0777), 2)
此时,输出已被重定向。但是,该进程已停止并且gdb已附加到该进程。
由于我们不再需要gdb,我们可以从进程中分离并退出gdb。这也将使进程继续执行:
(gdb) q
或者,我们可以通过重定向到*/dev/null*来使输出静音:
(gdb) p dup2(open("/dev/null"), 1)
我们也可以将所有gdb命令写入一个“命令文件”,并使用参数-x*来执行它*。
让我们创建一个名为gdb_redirect的文件:
p dup2(open("/tmp/process_stdout", 1089, 0777), 1)
p dup2(open("/tmp/process_stderr", 1089, 0777), 2)
q
然后,我们只需要使用我们的文件gdb_redirect运行**gdb:
$ gdb -p 14560 -x gdb_redirect
3. 使用strace检查所有写调用
我们还可以使用strace来检查系统调用。使用这种方法,我们不会中断原始输出。相反,我们会将其复制到其他位置。
让我们检查对 PID 14560的stderr的所有写入:
$ strace -etrace=write -s 100000 -p 14560 2>&1 | grep --line-buffered '^write(2,'
write(2, "\r", 1) = 1
write(2, "17243001856 bytes (17 GB, 16 GiB) copied, 1185 s, 14.6 MB/s", 59) = 59
write(2, "\r", 1) = 1
write(2, "17249965568 bytes (17 GB, 16 GiB) copied, 1186 s, 14.5 MB/s", 59) = 59
使用这些参数,strace附加到 PID 14560,跟踪写入调用并将最大字符串大小设置为 100,000。然后,我们使用grep 仅打印对文件描述符2的写入。
我们可以将它们重定向到一个名为*/tmp/process_stderr*的文件:
$ strace -etrace=write -s 100000 -p 14560 2>&1 | grep --line-buffered '^write(2,' > /tmp/process_stderr
如果我们不喜欢strace的输出格式,我们可以使用*sed *只打印字符串:
$ strace -etrace=write -s 100000 -p 14560 2>&1 | sed -n -r 's/^write\(2,\s*"(.+)",\s*[[:digit:]]+\)\s*=\s*[[:digit:]]+$/\1/p'
\r
25754656256 bytes (26 GB, 24 GiB) copied, 1710 s, 15.1 MB/s
\r
25761747456 bytes (26 GB, 24 GiB) copied, 1711 s, 15.1 MB/s
此外,** strace可以打印正在写入任何文件描述符的十六进制和 ASCII 输出**。我们使用*-ewrite=fd*选项执行此操作:
$ strace -ewrite=2 -etrace=write -p 14560 2>&1 | grep --line-buffered '^ |'
| 00000 0d . |
| 00000 33 33 30 31 32 30 32 37 39 30 34 20 62 79 74 65 33012027904 byte |
| 00010 73 20 28 33 33 20 47 42 2c 20 33 31 20 47 69 42 s (33 GB, 31 GiB |
| 00020 29 20 63 6f 70 69 65 64 2c 20 32 31 36 32 20 73 ) copied, 2162 s |
| 00030 2c 20 31 35 2e 33 20 4d 42 2f 73 , 15.3 MB/s |
| 00000 0d . |
| 00000 33 33 30 31 39 36 32 31 38 38 38 20 62 79 74 65 33019621888 byte |
| 00010 73 20 28 33 33 20 47 42 2c 20 33 31 20 47 69 42 s (33 GB, 31 GiB |
| 00020 29 20 63 6f 70 69 65 64 2c 20 32 31 36 33 20 73 ) copied, 2163 s |
| 00030 2c 20 31 35 2e 33 20 4d 42 2f 73 , 15.3 MB/s |
4.使用*screen *将输出写入文件
如果进程在*screen *会话中运行,我们可以将当前窗口记录到文件中。与strace一样,我们不会替换输出 - 我们将复制它。但是,我们不能复制任意文件描述符。我们只能复制正在写入*screen *窗口的内容。
**为此,我们使用screen 命令log或其热键C-a H。**这将启用记录到名为screenlog.n的文件,其中n是screen 窗口编号。之后,我们可以脱离屏幕并让进程运行,我们将在screenlog.n中看到输出。我们可以随时再次使用log命令或其热键停止登录。
此外,我们可以使用命令logfile来指定写入输出的位置:
:logfile process_output
然后,当我们运行log时,输出将在process_output而不是screenlog.n中: