我在调试 segfaulting 程序时遇到了麻烦,因为我需要的是 segfault 之前的输出,但是如果我将输出通过管道传输到文件,这就会丢失。根据这个答案:https://unix.stackexchange.com/a/17339/22615,这是因为程序的输出缓冲区在连接到终端时会立即刷新,但仅在连接到管道时才会在某些点刷新。这里有几个问题:
程序如何确定其标准输出连接到什么?
“脚本”命令如何产生与程序写入终端时相同的行为?
这可以在没有脚本命令的情况下实现吗?
我在调试 segfaulting 程序时遇到了麻烦,因为我需要的是 segfault 之前的输出,但是如果我将输出通过管道传输到文件,这就会丢失。根据这个答案:https://unix.stackexchange.com/a/17339/22615,这是因为程序的输出缓冲区在连接到终端时会立即刷新,但仅在连接到管道时才会在某些点刷新。这里有几个问题:
程序如何确定其标准输出连接到什么?
“脚本”命令如何产生与程序写入终端时相同的行为?
这可以在没有脚本命令的情况下实现吗?
判断文件描述符是否指向终端设备
程序可以通过使用标准 C 函数来判断文件描述符是否与 tty 设备相关联
isatty()
(通常在下面执行一个无害的 tty 特定ioctl()
系统调用,当 fd 不指向 tty 设备时会返回错误) .[
/test
实用程序可以使用其运算符来完成-t
。在 GNU/Linux 系统上跟踪 libc 函数调用:
跟踪系统调用:
判断它是否指向管道
要确定 fd 是否与 pipe/fifo 相关联,可以使用
fstat()
系统调用,它返回一个结构,其st_mode
字段包含在该 fd 上打开的文件的类型和权限。可以在该字段上使用S_ISFIFO()
标准 C 宏st_mode
来确定 fd 是否为管道/fifo。没有可以执行的标准实用程序,但是可以执行的命令
fstat()
有几个不兼容的实现。的内置函数将模式作为字符串表示形式返回,其第一个字符表示类型(用于管道)。GNU可以对 做同样的事情,但也必须单独报告类型。使用 BSD :或.stat
zsh
stat
stat -sf "$fd" +mode
p
stat
stat -c %A - <&"$fd"
stat -c %F - <&"$fd"
stat
stat -f %St <&"$fd"
stat -f %HT <&"$fd"
判断它是否可搜索
应用程序通常不关心标准输出是否是管道。他们可能会关心它的可搜索性(尽管通常不会决定是否缓冲)。
要测试 fd 是否可搜索(管道、套接字、tty 设备不可搜索,常规文件和大多数块设备通常是可搜索的),可以尝试使用偏移量为 0 的相对
lseek()
系统调用(因此无害)。dd
是一个标准实用程序,它是一个接口,lseek()
但不能用于该测试,因为lseek()
如果您要求偏移量为 0,则实现根本不会调用。和shell 内置了搜索运算符
zsh
:ksh93
禁用缓冲
该
script
命令使用一对伪终端来捕获程序的输出,因此程序的 stdout(以及 stdin 和 stderr)将是一个伪终端设备。当标准输出到终端设备时,通常仍然有一些缓冲,但它是基于行的。
printf
/puts
并且 co 在要输出换行符之前不会写入任何内容。对于其他类型的文件,缓冲是按块(几千字节)。有几个选项可以禁用缓冲,这些选项在此处的许多问答中进行了讨论(搜索unbuffer或stdbuf,无法重定向剪切输出提供了一些方法),或者使用伪终端,可以通过
socat
/script
/expect
/unbuffer
(一个expect
脚本)/或zsh
通过zpty
在可执行文件中注入代码来禁用缓冲,就像 GNU 或 FreeBSD 所做的那样stdbuf
。