我已经习惯这样做了:
someprogram >output.file
每当我想将程序生成的输出保存到文件中时,我都会这样做。我也知道这个的两个变体IO重定向:
someprogram 2>output.of.stderr.file
(对于标准错误)someprogram &>output.stderr.and.stdout.file
(对于 stdout+stderr 组合)
今天我遇到了一个我认为不可能的情况。我使用以下命令xinput test 10
,正如预期的那样,我有以下输出:
用户@主机名:~$ xinput 测试 10 按键 30 释放钥匙 30 按键 40 钥匙释放 40 按键 32 按键释放 32 按键 65 钥匙释放 65 按键 61 钥匙释放 61 按键 31 ^C 用户@主机名:~$
我期望这个输出可以像往常一样保存到文件中,例如使用xinput test 10 > output.file
.但与我的预期相反,文件 output.file 仍然为空。这也是为了xinput test 10 &> output.file
确保我不会错过 stdout 或 stderr 上的某些内容。
我真的很困惑,因此在这里询问xinput
程序是否有办法避免其输出被重定向?
更新
我已经看过源码了。看起来输出是由这段代码生成的(参见下面的代码片段)。在我看来,输出将由普通的 printf 生成
//在文件test.c中 静态无效 print_events(显示 *dpy) { XEvent 事件; 而(1){ XNextEvent(dpy,&Event); // [...这里列出了一些其他事件类型...] if ((Event.type == key_press_type) || (Event.type == key_release_type)) { 整数循环; XDeviceKeyEvent *key = (XDeviceKeyEvent *) &Event; printf("key %s %d ", (Event.type == key_release_type) ? "release" : "按下 ", key->keycode); 对于(循环=0;loopaxes_count;循环++){ printf("a[%d]=%d ", key->first_axis + 循环, key->axis_data[loop]); } printf("\n"); } } }
我将源代码修改为这个(请参见下面的下一个代码片段),这使我能够在 stderr 上获得输出的副本。我可以重定向此输出:
//在文件test.c中 静态无效 print_events(显示 *dpy) { XEvent 事件; 而(1){ XNextEvent(dpy,&Event); // [...这里列出了一些其他事件类型...] if ((Event.type == key_press_type) || (Event.type == key_release_type)) { 整数循环; XDeviceKeyEvent *key = (XDeviceKeyEvent *) &Event; printf("key %s %d ", (Event.type == key_release_type) ? "release" : "按下 ", key->keycode); fprintf(stderr,"key %s %d ", (Event.type == key_release_type) ? "release" : "press ", key->keycode); 对于(循环=0;loopaxes_count;循环++){ printf("a[%d]=%d ", key->first_axis + 循环, key->axis_data[loop]); } printf("\n"); } } }
我目前的想法是,也许通过进行重定向,程序会失去监视按键释放事件的能力。
答案1
只是当 stdout 不是终端时,输出会被缓冲。
当您按下 时Ctrl-C,该缓冲区将丢失,因为/如果它尚未被写入。
使用任何东西都会得到相同的行为stdio
。尝试例如:
grep . > file
输入一些非空行并按Ctrl-C,您将看到该文件是空的。
另一方面,输入:
xinput test 10 > file
然后在键盘上输入足够的内容缓冲得到完整的(至少 4k 的输出),你会看到文件一次以 4k 为单位增长。
使用grep
,您可以键入Ctrl-Dforgrep
在刷新缓冲区后正常退出。对于xinput
,我认为没有这样的选择。
请注意,默认情况下stderr
不缓冲,这解释了为什么您会得到不同的行为fprintf(stderr)
如果在 中xinput.c
添加一个signal(SIGINT, exit)
,即告诉它xinput
在收到 时优雅退出SIGINT
,您将看到file
不再为空(假设它不会崩溃,因为从信号处理程序调用库函数不能保证安全:考虑什么如果 printf 写入缓冲区时有信号进入,则可能会发生)。
如果可用,您可以使用以下stdbuf
命令来更改stdio
缓冲行为:
stdbuf -oL xinput test 10 > file
有这个网站上有很多问题该覆盖禁用标准输入输出类型缓冲,您将在其中找到更多替代解决方案。
答案2
命令可以直接写入以/dev/tty
防止发生常规重定向。
$ cat demo
#!/bin/ksh
LC_ALL=C TZ=Z date > /dev/tty
$ ./demo >demo.out 2>demo.err
Fri Dec 28 10:31:57 2012
$ ls -l demo*
-rwxr-xr-x 1 jlliagre jlliagre 41 2012-12-28 11:31 demo
-rw-r--r-- 1 jlliagre jlliagre 0 2012-12-28 11:31 demo.err
-rw-r--r-- 1 jlliagre jlliagre 0 2012-12-28 11:31 demo.out
答案3
它看起来像xinput
拒绝输出到文件,但不拒绝输出到终端。为了实现这一点,可能xinput
使用系统调用
int isatty(int fd)
检查要打开的文件描述符是否引用终端。
不久前,我在一个名为 的程序中偶然发现了同样的现象dpic
。在我查看源代码并进行一些调试后,我删除了相关的行isatty
,一切都再次按预期工作。
但我同意你的看法,这种经历非常令人不安;)
答案4
是的。当我用 pascal 编程时,我什至在 DOS 时代也这样做过。我想这个原则仍然成立:
- 关闭标准输出
- 重新打开标准输出作为控制台
- 将输出写入标准输出
这确实破坏了任何管道。