我在调试segfaulting程序时遇到了麻烦,因为我需要的是segfault之前的输出,但是如果将输出管道传输到文件中,则会丢失此信息。根据以下答案:https : //unix.stackexchange.com/a/17339/22615,这是因为程序的输出缓冲区在连接到终端时会立即刷新,但在连接到管道时才在特定点刷新。这里有几个问题:
程序如何确定其stdout连接到什么?
“脚本”命令如何产生与程序写入终端时相同的行为?
如果没有script命令,可以实现吗?
我在调试segfaulting程序时遇到了麻烦,因为我需要的是segfault之前的输出,但是如果将输出管道传输到文件中,则会丢失此信息。根据以下答案:https : //unix.stackexchange.com/a/17339/22615,这是因为程序的输出缓冲区在连接到终端时会立即刷新,但在连接到管道时才在特定点刷新。这里有几个问题:
程序如何确定其stdout连接到什么?
“脚本”命令如何产生与程序写入终端时相同的行为?
如果没有script命令,可以实现吗?
Answers:
程序可以使用isatty()
标准的C函数来判断文件描述符是否与tty设备相关联(通常在下面执行无害的tty特定的ioctl()
系统调用,当fd不指向tty设备时,该调用会返回错误) 。
该[
/ test
实用程序可以用它做-t
运营商。
if [ -t 1 ]; then
echo stdout is open to a terminal
fi
在GNU / Linux系统上跟踪libc函数调用:
$ ltrace [ -t 1 ] | cat
[...]
isatty(1) = 0
[...]
跟踪系统调用:
$ strace [ -t 1 ] | cat
[...]
ioctl(1, TCGETS, 0x7fffd9fb3010) = -1 ENOTTY (Inappropriate ioctl for device)
[...]
要确定某个fd是否与管道/ fifo相关联,可以使用fstat()
系统调用,该调用返回一个结构,该结构的st_mode
字段包含在该fd上打开的文件的类型和权限。的S_ISFIFO()
标准C宏可以对使用st_mode
字段,以确定是否fd是一个管/ FIFO。
没有可以使用的标准实用程序fstat()
,但是stat
可以使用命令的几种不兼容的实现。zsh
的stat
内建函数,stat -sf "$fd" +mode
以字符串形式返回模式,该模式的第一个字符表示类型(p
用于管道)。GNU stat
可以使用进行相同的操作stat -c %A - <&"$fd"
,但是还必须单独stat -c %F - <&"$fd"
报告类型。使用BSD stat
:stat -f %St <&"$fd"
或stat -f %HT <&"$fd"
。
应用程序通常并不关心stdout是否是管道。他们可能会在乎它是否可取(尽管通常不决定是否缓冲)。
为了测试fd是否可搜索(管道,套接字,tty设备不可搜索,常规文件以及大多数块设备通常都可以),可以尝试使用偏移量为0(无害)的相对lseek()
系统调用。dd
是一个标准实用程序,是lseek()
与之的接口,但不能用于该测试,因为lseek()
如果您要求偏移量为0 ,则实现将根本不会调用该实用程序。
在zsh
与ksh93
壳有内置寻求运营商,但:
$ strace -e lseek ksh -c ': 1>#((CUR))' | cat
lseek(1, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
ksh: 1: not seekable
$ strace -e lseek zsh -c 'zmodload zsh/system; sysseek -w current -u 1 0 || syserror'
lseek(1, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
Illegal seek
该script
命令使用伪终端对来捕获程序的输出,因此程序的stdout(以及stdin和stderr)将成为伪终端设备。
当标准输出到终端设备时,通常仍然会有一些缓冲,但是它是基于行的。printf
/ puts
和co将不会写入任何内容,直到要输出换行符为止。对于其他类型的文件,按块(几千字节)缓冲。
有几种选择来禁用其以数Q&如这里所讨论的缓冲(搜索非缓冲或stdbuf,不重定向切输出可通过使用伪终端作为可以这样做给出了几个方法)或者socat
/ script
/ expect
/ unbuffer
(一个expect
脚本)/ zsh
的zpty
或通过在可执行代码注入禁用缓冲由GNU的或FreeBSD的完成stdbuf
。
/proc
目录,并针对每个/proc/<integer>/
目录查找/proc/<integer>/fd/
并在pipefs
serverfault.com/q/48330/363611中查找具有相同inode编号的文件描述符, 但是,这仅在脚本中不能使用所描述的syscall时才有用在斯蒂芬(Stephane)的答案中,它不仅仅是适当的解决方案,而是一种解决方法。恕我直言
lseek
将在终端和其他字符设备上成功,并且只需重新/设置一个计数器,该计数器将在每次成功的read()上增加。我不知道这是否使他们“可以寻找”。