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

hum*_*ace 50 io-redirection xinput

我已经习惯了这样做: 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中

static void print_events(Display *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" : "press ", key->keycode);

        for(loop=0;loopaxes_count;loop++){
        printf("a[%d]=%d", key->first_axis + loop, key->axis_data[loop]);
        }
        printf("\n");
    } 
    }
}

我将源修改为这个(见下面的下一个片段),这允许我在 stderr 上拥有输出的副本。这个输出我能够重定向:

 //在文件test.c中

static void print_events(Display *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" : "press ", key->keycode);
        fprintf(stderr,"key %s %d", (Event.type == key_release_type) ? "release" : "press ", key->keycode);

        for(loop=0;loopaxes_count;loop++){
        printf("a[%d]=%d", key->first_axis + loop, key->axis_data[loop]);
        }
        printf("\n");
    } 
    }
}

我目前的想法是,通过执行重定向,程序可能会失去监视按键释放事件的能力。

Sté*_*las 55

只是当 stdout 不是终端时,输出被缓冲。

当您按下 时Ctrl-C,该缓冲区将丢失,因为/如果它尚未写入。

使用stdio. 尝试例如:

grep . > file
Run Code Online (Sandbox Code Playgroud)

输入几行非空行并按Ctrl-C,您将看到文件为空。

另一方面,键入:

xinput test 10 > file
Run Code Online (Sandbox Code Playgroud)

并在键盘上输入足够的内容以使缓冲区变满(至少 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
Run Code Online (Sandbox Code Playgroud)

此站点上许多问题涉及禁用stdio类型缓冲,您可以在其中找到更多替代解决方案。

  • @ user1146332,是的,这就是 `stdbuf -o0` 所做的,而 `stdbug -oL` 恢复 _line_ 缓冲,就像输出到终端时一样。`stdbuf` 确实强制应用程序使用 `LD_PRELOAD` 技巧调用 `setvbuf`。 (4认同)
  • 哇 :) 成功了。谢谢你。所以最后我对这个问题的看法是错误的。没有任何东西可以禁止重定向,很简单 Ctrl-C 在刷新数据之前停止了它。谢谢你 (2认同)

jll*_*gre 23

命令可以直接写入以/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
Run Code Online (Sandbox Code Playgroud)


use*_*332 9

看起来xinput拒绝输出到文件但不拒绝输出到终端。为了实现这一点,可能xinput使用系统调用

int isatty(int fd)
Run Code Online (Sandbox Code Playgroud)

检查要打开的文件描述符是否指向终端。

不久前,我在一个名为dpic. 在我查看源代码和一些调试之后,我删除了相关的行isatty,一切都再次按预期工作。

但我同意你的看法,这种经历非常令人不安;)