shellterminaltput

Redirecting stderr changes the output of tput but only when capturing the output to a variable?


I'm trying to get the width of the current terminal window in a script and use 80 as a fallback in case that isn't possible. I thought that's pretty simple:

cols=$( tput cols || echo 80 ) ; echo $cols
# -> 100

100 is correct, I made the terminal 100 chars wide. As cols is no POSIX conform argument to tput, not all systems will support it, thus the fallback. Now let's test the fallback:

cols=$( tput colsx || echo 80 ) ; echo $cols
# -> tput: unknown terminfo capability 'colsx'
# -> 80

Hmmmm... not so nice. I don't want to see that error. It's printed on stderr, so let's just suppress it:

cols=$( tput colsx 2>/dev/null || echo 80 ) ; echo $cols
# -> 80

Yes, that's much better. So the final code is

cols=$( tput cols 2>/dev/null || echo 80 ) ; echo $cols 
# -> 80

80? WTF? Why does it run into the fallback now? Let's try this:

cols=$( tput cols 2>/dev/null ) ; echo $cols  
# -> 80

Ahhhh... redirecting stderr changes the output of tput? How's that possible? Let's confirm that:

tput cols 2>/dev/null
# -> 100

Okay, now I'm lost! Can someone please explain me what's going on here?


Solution

  • There's a partial answer here

    tput cols can lead to data from different sources based on these cases:

    When running tput cols : terminal setting columns may be fetched with an ioctl() using fd 1,2 if possible, otherwise get terminfo capability cols.

    This session sets 99 for terminal columns,

    $ stty columns 99; stty -a | grep columns
            speed 38400 baud; rows 24; columns 99; line = 0;
    

    Only fd 1 redirected (to a non-tty):

    $ tput cols > out; cat out
    99
    

    Only fd 2 redirected:

    $ tput cols 2> err; wc -c err
    99
    0 err
    

    Both fd 1,2 redirected:

    $ tput cols > out 2>&1; cat out
    80
    

    fd 1 not a tty:

    $ echo $(tput cols || echo 100)
    99
    

    fd 1,2 not a tty:

    $ echo $(tput cols 2> /dev/null || echo 100)
    80
    

    To show cols cabability being fetched when fd 1,2 are redirected, a terminfo named tmp with different cols was created and installed, then:

    $ export TERM=tmp TERMINFO=$HOME/.ti
    $ infocmp | grep cols
            colors#8, cols#132, it#8, lines#24, pairs#64,
    

    fd 1,2 not a tty:

    $ echo $(tput cols 2> /dev/null || echo 100)
    132
    

    fake cap, tput exits non-zero:

    $ echo $(tput kol 2> /dev/null || echo 100)
    100