我们有一个构建脚本,可以为多种电路板类型构建嵌入式系统,并且以一种方式捕获输出时,它似乎可以正常工作。另一种方法是,它部分通过输出,然后在继续操作之前将文件截断。
工作方式:
time ( cd ~ ; builder.sh 2>&1 | tee ~/builder.out )
以及截断方式:
time ( cd ~ ; builder.sh > ~/builder.out 2>&1 )
截断似乎发生在非常特定的位置,截断后文件中的第一行始终是DEFAULT_INCDIRS=...
中的qmake
。它是在过程中的某个特定时刻而不是在文件达到特定大小时,这一事实似乎表明它不是某个外部文件检查器在执行截断。
无论如何,如果脚本被删除,脚本将继续写入已删除文件的索引节点,直到关闭为止,然后将其删除。
实际发生的情况是该文件似乎被截断,然后从头开始继续写入。但是我知道,如果没有程序实际上无法访问文件句柄本身,就无法做到这一点。
在上述两种情况下,构建器脚本实际上并不了解其输出文件,它只是将输出和错误消息都写入stdout
中,并让shell重定向来处理它
所以我的问题是:在文件I / O的UNIX模型中有没有办法做到这一点(例如,从C文件API调用)?换句话说,当通过重定向设置了要写入的文件时,是否可以截断该文件?为什么tee
变体有效?是什么阻止它被截断?
答案 0 :(得分:3)
是的,正如您已经注意到的,显然有人在lseek()
上呼叫ftruncate()
或stdout
。
要追踪罪犯,strace -f
当然会有所帮助。当做这样奇特的事情时,您可能需要做strace -f sh -c 'build.sh 2>&1 | cat > output' > log 2>&1
,因为它也会很高兴地扼杀您的strace输出。
有了日志后,找到对lseek(1,
,lseek(2,
,ftruncate(1,
或ftruncate(2,
的调用。从那里,向后搜索到前一个exec
,您应该知道。
可以合法使用stdout
进行游戏的程序是cdrecord
,其中至少有些版本希望将CD刻录机输出到标准输出中。
答案 1 :(得分:1)
好吧,事实证明程序可以搜索标准输出(尽管我不知道这样做的程序是否合理)。
以下程序对此进行了说明:
#include <stdio.h>
int main(void){
for (int i = 3; i > 0; --i) {
//rewind(stdout);
printf("Hello, world %d !\n", i);
}
return 0;
}
运行此命令以捕获输出,您将得到一个包含以下内容的文件:
Hello, world 3 !
Hello, world 2 !
Hello, world 1 !
但是,如果取消注释rewind
行,则只会在输出文件中以 final 行结尾。
有趣的是,由于我无法控制截断stdout
的程序,因此对于“猫的无用使用”奖,这实际上可能是有用的用猫。而不是执行:
myprog >outfile 2>&1
并用myProg
截断文件,我可以改为:
myprog 2>&1 | cat >outfile
并且管道将保护cat
输出文件不被截断。
就实际问题而言,看来qt5base
(buildroot
的一部分)出于某种原因正在播放某种带有输出文件句柄的恶作剧。由于我们没有时间追赶buildroot(或创建补丁文件)来正确修复它,因此我们已经通过上面的cat
方法解决了该问题。