linuxttypty

re-enable on-screen carriage return behaviour without including them in the data stream


Typically, when displaying data to screen, a newline is treated as if there is also a carriage return.

Consider:

$ printf "aaa\nbbb\n"
aaa
bbb
$ printf "aaa\nbbb\n" | od -c
0000000   a   a   a  \n   b   b   b  \n
0000010
$ printf "aaa\nbbb\n" | tr -d '\r'
aaa
bbb
$

Now:

$ script -B/dev/null -fqc 'printf "aaa\nbbb\n"'
aaa
bbb
$ script -B/dev/null -fqc 'printf "aaa\nbbb\n"' | od -c
0000000   a   a   a  \r  \n   b   b   b  \r  \n
0000012
$ script -B/dev/null -fqc 'printf "aaa\nbbb\n"' | tr -d '\r' | od -c
0000000   a   a   a  \n   b   b   b  \n
0000010
$

But:

$ script -B/dev/null -fqc 'printf "aaa\nbbb\n"' | tr -d '\r'
aaa
   bbb
      $ script -B/dev/null -fqc 'printf "aaa\nbbb\n"' | tr -d '\r' >log
$ od -c <log
0000000   a   a   a  \n   b   b   b  \n
0000010
$ cat log
aaa
bbb
$

What is happening in the script pipeline that causes different display behaviour for apparently-identical data?

Is there a simple-ish command that could be used in place of (or in addition to) tr -d '\r' in the script pipeline that would remove the extraneous carriage returns from the data stream while also re-enabling the normal stty onlcr display behaviour when the pipeline destination is the screen?


Solution

  • The implementations of script I have checked (util-linux and freebsd), both call cfmakeraw(3) if their stdin is a terminal.

    On linux, it is specified that:

    The cfmakeraw() function shall set the attributes of the termios structure referenced by termios_p as follows:

    termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
                            |INLCR|IGNCR|ICRNL|IXON);
    termios_p->c_oflag &= ~OPOST;
    termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
    termios_p->c_cflag &= ~(CSIZE|PARENB);
    termios_p->c_cflag |= CS8;
    

    FreeBSD is less specific, saying:

    The cfmakeraw() function sets the flags stored in the termios structure to a state disabling all input and output processing, giving a "raw I/O path"


    To reverse these changes, calling stty with desired settings in an appropriate part of the pipeline (as per @philippe's answer), is one option.

    Another way may be to avoid the changes happening in the first place, by redirecting stdin from a non-terminal (eg. /dev/null) so that cfmakeraw() is never called. However, the util-linux documentation warns that running with stdin not connected to a terminal may cause problems in some situations.