POSIX way of getting columns in tty?

Is there a POSIX compatible way of getting the number of columns in a tty? On Linux, I can do something like:

stty -a  | awk -F'[ ;]' '/columns/ { print $9 }'

But will that work on AIX, Solaris, etc?

Not sure about POSIX compliance but you might try that one which seems to work for me under Solaris and Linux:

LC_ALL=C stty -a  | grep columns | sed -e 's/.*columns //' -e 's/;.*$//' -e 's/= //'

stty is available on AIX, Solaris, HP-UX. However, IEEE std 1003.1-2008 POSIX) states that the ouput format is unspecified by the standard. This means that your method of parsing the output which you showed us may not work on all platforms.

Don't have access to Solaris at the moment. Would you mind posting the output from 'stty', please!

What do you think about jlligare's solution, above? What about with TERMCAP?

I guess you have never used a tty. There is no standard way to obtain columns or rows. There are lots of situations still in existence where it cannot be done. Most true terminals never had a way to inform the host about parameters like that because columns were pretty much locked to 80. A real teletype can't even reasonably have a notion of rows. It can't back up to the "top", you might have ripped off the paper. And how far it can go down depends only on how much paper is left. Real teletypes always had exactly 80 columns though. The tty driver was designed for real teletypes. HP had a super printing terminal that could support up to 132 "columns", (but we used the term "print positions"). It used pin feed style paper. You varied the columns by changing the paper. But the host could not enquire about paper size. Posix does not require that stty know about columns.

It is not part of POSIX.1, but most shells initialize a COLUMNS variable.

Nobody is using reals teletypes nowadays, and few people still use true terminals. In the latter case, there are usually ways to get the actual width and it is safe to default to 80 characters should it fails.

I'm quite sure all the tty drivers available in modern Unix variants handle the non POSIX tty wxh geometry (termios TIOCGWINSZ) and the event triggered when it changes (SIGWINCH) when using a terminal emulator on a windowing system.

One of the issues is stty output isn't specified and for example Solaris reports "columns = xx" while Linux reports "columns xx".

You should check a sample stty output with the other Unix variants you plan to support and modify my script should other discrepancies appear then you'll have a reasonably "stable" solution.

I hope you will agree that Solaris 10 counts as a modern Unix variant. I wanted to use the serial system console on ttya. I connected a serial cable to second Solaris 10 system. On that 2nd system, I opened a terminal window and in that window in used tip to connect. That all works fine. But when I vary the window size on the 2nd system, the first system does not seem to be informed. And no fair telling me that I manually type "stty cols 100" (or whatever) after I change my window size.

The Solaris driver used to control ttya (and ttyb) is indeed a real terminal. The other kind of terminal is a pseudo terminal. In the case of a real terminal, where a real tty driver is truely controlling a real serial port, this isn't going to work. It's true that the old printing teletypes are almost completely gone. Ditto those "ascii terminals" too. But the serial port is still alive and well. We either use another unix system as above or a laptop running putty on XP or something like that to connect to a serial port.

This isn't to say that I dislike your solution. I think your solution is as good as it gets. I am arguing that the search for an even more generic solution is futile and that there no help to be had from Posix.

And also note that jlliagre replaced awk with a grep/sed combo. Solutions involving awk are very dangerous on Solaris. Sun absolutely refuses to retire the antique version of awk so you must either use nawk or fiddle with the path on Solaris.

Thanks Perderabo, fpmurphy, and notably jlliagre.

So ultimately the answer: there is none. That's fine; then the question turns, "what's the best portable way to get the number of columns for the current tty". The answer seems to be: check the COLUMNS variable, and if not available, check the "non POSIX tty wxh geometry (termios TIOCGWINSZ)" and then fall-back to 80. Does that sound right?

Concerning "non POSIX tty wxh geometry (termios TIOCGWINSZ)", how can I do this from the shell?

I think that jlliagre has the best solution available and that is what I would use. But it only tells you the window size when you check it. A program like vi can arrange to be notified of a change in window size and then redraw the screen. That is too much to try via a shell script.

Sure, I would even say the most modern :slight_smile:

That case is indeed ruling out window size handling.

Assuming your terminal use a xterm compatible emulator (dtterm, gnome-terminal, rxvt and the likes are ok) I think you could write a script that set these tty settings to their correct values. Of course that wouldn't be very useful.

You are correct although I doubt the OP was concerned about that case.

Its use is slowly fading though with modern LOM based on ethernet vs serial lines.

Agreed. POSIX is currently of no help here.

What specific risks involved with awk are you thinking of ?

I would say that differently: Sun is extremely reluctant to put its existing customers/users at risk by breaking compatibility from older Solaris releases to newer ones with providing a new and incompatible version of a commited standard command. That is the same reasoning that makes /bin/sh such a featureless and disappointing shell.
Of course, everyone is free to use nawk and ksh(93) like I do.

That is not that difficult to write although not particularly reliable if the shell script is interactive. eg:

$ cat winch.ksh
#!/bin/ksh

function winch
{
    eval $(/usr/openwin/bin/resize)
    echo new width=$COLUMNS
    echo new height=$LINES
}

trap winch WINCH
echo "Resize the window to exercise the handler:"
for i in 1 2 3 4 5 6 7 8 9
do
    echo $i
    sleep 2
done

I'm going to keep it simple: I'll use the COLUMNS variable. Is there any environment or shell where this will not work? Should I call resize beforehand (just in case)?

There will be environments where:

  • the resize command is missing or not installed
  • the terminal (emulator) used isn't handling the escape sequence used to query its size

The resize command is expecting a Bourne compatible shell too.