命令行程序可以防止其输出被重定向吗?

命令行程序可以防止其输出被重定向吗?

我已经习惯这样做了: 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 时代也这样做过。我想这个原则仍然成立:

  1. 关闭标准输出
  2. 重新打开标准输出作为控制台
  3. 将输出写入标准输出

这确实破坏了任何管道。

相关内容