我正在尝试调试一个可以通过 stdin 接收命令的 Linux 应用程序,并且能够查看写入 stdin 的所有内容将非常有用。
我的第一种方法是执行sudo cat /proc/$pid/fd/0
,但事实证明这是一种无效的方法,因为cat
我的进程和我的进程都试图消耗该文件描述符的内容,并且只有一个进程可以赢得比赛。
另一种方法是使用 tail,但这不起作用,原因如下这里。
我可以轻松地将调试日志记录添加到我的应用程序中,这可以解决问题,但我有兴趣知道是否有我缺少的更通用的方法。
答案1
strace
?
下面的例子。启动一个 cat 进程,该进程读取 stdin 并写入 /tmp/foofile。找到 pid,对其进行跟踪。在原来的猫窗口中,输入一些文本,嘿,快点。
# cat >/tmp/foofile
# ps -ef|grep cat
steve 2134 1801 0 22:25 pts/2 00:00:00 cat
# strace -fp 2134
Process 2134 attached
read(0, "test\n", 65536) = 5
write(1, "test\n", 5) = 5
read(0,
要从文件描述符 0 中取出读取内容:
strace -fp 2134 -e trace=read -o "|grep read.0,"
答案2
在 Linux 上,您可以将应用程序的文件描述符作为/dev/fd/[0-9]
.您绝对可以使用命名文件和输入流执行tee
该文件的输入和到标准输出。所以当我发现自己处于你的处境时我通常会做什么(就像我经常做的那样)是tee
我的阅读应用程序和 - stderr 的输入/dev/fd/2
。
像这样:
seq 10 | tee /dev/fd/2 | wc -c
1
2
3
4
5
6
7
8
9
10
21
当然,即使你不在 Linux 系统上,同样的事情也可以通过移植来完成 - 如果在某些情况下不太具体 - 只需这样做...| tee /dev/tty | ...
如果你说的是终端stdin
(如您的链接所示),那么您可能仍然会做同样的事情,尽管由于内核的行缓冲,这种方式可能会变得更加棘手。所以在这种情况下我会做的是tty
通过包装我的命令来记录所有 i/o luit
- 因为我发现它是两者中更方便的 - 尽管script
也可以以大致相同的方式工作。
luit
可能已经安装在您的系统上 - 它通常打包在一起xterm
- 它是一个非常简单的 cli 工具,旨在进行 UTF-8 翻译(可以通过 cli 开关完全禁用该功能,但我从未找到这样做的理由)对于不理解它的终端应用程序。
它的工作原理是将自己的 pty(它拥有主 fd)分层在当前 tty 层下方,并将所有 I/O 从当前会话复制到其子层,在子层中执行您请求的应用程序。因为它拥有主端,所以它可以轻松地在其他地方复制它读/写的所有 I/O,并且它提供了一种要求它这样做的便捷方法:
luit -olog /dev/fd/2 sh -c 'read var; echo "$var"'
eecchhoo tthhiiss vvaarr??????
echo this var???
echo this var???
正如您所看到的,一旦收到所有终端输入,就会luit
将其记录到其命名文件中。-olog
在这种情况下,使用/dev/fd/2
几乎没有那么有用 - 因为所有 I/O 都会在同一个地方出现两次。我通常更喜欢打开第二个终端,使用命令查询其名称tty
,并使用该/dev/pts/[0-9]
名称作为luit
/script
的命名输出文件 - 这将同时将所有 i/o 复制到两个终端 - 这样我就可以在一个终端上读取/查看它,并在另一端与之交互。tee
在大多数情况下可以用来做同样的事情,但它通常不具有 pty 的主端推荐它的优势。
如果您的目的正如您所说的那样 - 复制某些流程的所有输入以供您审阅 - 那么您可能会最好专注于输入。strace
对于很多事情都很有用,但是如果您想获得典型行为的准确报告,那么您应该在收集报告时尽可能少地修改该行为,这是理所当然的。换句话说,如果您需要输入,请复制输入,不要插入调试父进程,该进程将在-TRAP
每次进行系统调用时暂停您的进程。
答案3
一种可能性(如果这不会破坏其他事情)是插入一个调用tee
。该命令tee
会复制数据,因此您可以将一份副本发送到您的应用程序,并将一份副本发送到调试输出。不要调用,而是your_application
安排调用tee input.log | your_application
。如果应用程序的输入是文件,则这不是侵入性更改,但如果它不是管道,则它会成为管道,从而产生后果(例如,管道不可查找)。
另一种可能性是跟踪应用程序执行的文件读取操作。你可以这样做斯特雷斯:
strace -e read=0 -e trace=read -e signal=none your_application 2>&1 |
grep '^ |'