Can a command line program prevent its output being redirected

io-redirectionxinput

I have become so used to do this:
someprogram >output.file

I do it whenever I want to save the output that a program generates to a file. I am also aware of the two variants of this IO redirection:

  • someprogram 2>output.of.stderr.file (for stderr)
  • someprogram &>output.stderr.and.stdout.file (for both stdout+stderr combined)

Today I have run across a situation I have not thought possible. I use the following command xinput test 10 and as expected I have the following output:

user@hostname:~$ xinput test 10
key press   30 
key release 30 
key press   40 
key release 40 
key press   32 
key release 32 
key press   65 
key release 65 
key press   61 
key release 61 
key press   31 
^C
user@hostname:~$ 

I expected that this output could as usual be saved to a file like using xinput test 10 > output.file. But when contrairy to my expectation the file output.file remains empty. This is also true for xinput test 10 &> output.file just to make sure I do not miss something on stdout or stderr.

I am really confused and hence ask here if the xinput program might have a way to avoid its output to be redirected?

update

I have looked at the source. It seems the output is generated by this code (see snippet below). It appears to me the output would be generated by an ordinary printf

//in file test.c

static void print_events(Display    *dpy)
{
    XEvent        Event;

    while(1) {
    XNextEvent(dpy, &Event);

    // [... some other event types are omnited here ...]

        if ((Event.type == key_press_type) ||
           (Event.type == key_release_type)) {
        int loop;
        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");
    } 
    }
}

I modified the source to this (see next snippet below), which allows me to have a copy of the output on stderr. This output I am able to redirect:

 //in file test.c

static void print_events(Display    *dpy)
{
    XEvent        Event;

    while(1) {
    XNextEvent(dpy, &Event);

    // [... some other event types are omnited here ...]

        if ((Event.type == key_press_type) ||
           (Event.type == key_release_type)) {
        int loop;
        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");
    } 
    }
}

My idea at present is that maybe by doing the redirect the program looses its ability to monitor the key-press key-release events.

Best Answer

It's just that when stdout is not a terminal, output is buffered.

And when you press Ctrl-C, that buffer is lost as/if it has not been written yet.

You get the same behavior with anything using stdio. Try for instance:

grep . > file

Enter a few non-empty lines and press Ctrl-C, and you'll see the file is empty.

On the other hand, type:

xinput test 10 > file

And type enough on the keyboard for the buffer to get full (at least 4k worth of ouput), and you'll see the size of file grow by chunks of 4k at a time.

With grep, you can type Ctrl-D for grep to exit gracefully after having flushed its buffer. For xinput, I don't think there's such an option.

Note that by default stderr is not buffered which explains why you get a different behaviour with fprintf(stderr)

If, in xinput.c, you add a signal(SIGINT, exit), that is tell xinput to exit gracefully when it receives SIGINT, you'll see the file is no longer empty (assuming it doesn't crash, as calling library functions from signal handlers isn't guaranteed safe: consider what could happen if the signal comes in while printf is writing to the buffer).

If it's available, you could use the stdbuf command to alter the stdio buffering behaviour:

stdbuf -oL xinput test 10 > file

There are many questions on this site that cover disabling stdio type buffering where you'll find even more alternative solutions.

Related Question