blog · git · desktop · images · contact & privacy · gopher


Ist an „stdout“ ein Terminal?

Ich habe hier gerade den Fall, dass ich in einem Shellskript testen muss, ob stdout ein Terminal ist oder nicht. Hintergrund ist: Ich will bestimmte Kontrollsequenzen an XTerm senden. Das soll natürlich nur passieren, wenn die Ausgabe auch an einem XTerm landet und nicht in einer Pipe oder Datei -- die Sequenzen gehören auch unmittelbar zur Ausgabe und dürfen nicht einfach über /dev/tty an Umleitungen "vorbeigeschmuggelt" werden.

Was eigentlich passieren muss, ist ein Aufruf von isatty(1). Da ich nun eine halbe Stunde an der falschen Stelle gesucht hatte, kam ich zuerst hierauf:

#include <unistd.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
    if (argc < 2)
        return 1;

    return !isatty(atoi(argv[1]));
}

Das funktioniert auch und man könnte es sich als isatty in ~/bin ablegen oder ähnlich. Dann würde "~/bin/isatty 1" prüfen, ob stdout ein Terminal ist. Anhand des Exit-Codes könnte man im Skript entsprechend fortfahren.

In einem C-Programm ist das in Ordnung, aber natürlich für ein Shellskript viel zu umständlich. Der "Königsweg" verbirgt sich hinter dem test-Befehl. Es ist nämlich -- wie immer -- eigentlich ganz einfach und die Bash-Lösung sieht so aus:

#!/bin/bash
if [[ -t 1 ]]
then
    echo "stdout ist ein TTY."
else
    echo "stdout ist kein TTY."
fi

Die 1 in [[ -t 1 ]] bezieht sich auf stdout. Das könnte man auch mit 0 für stdin, 2 für stderr oder einem beliebigen anderen Deskriptor machen. Für einzelne Aufrufe kann man sich das if-Konstrukt sparen und folgendes würde den "Auto Wraparound"-Modus beim XTerm abschalten, aber eben nur, wenn es auch ein XTerm ist:

[[ "$TERM" == xterm* ]] && [[ -t 1 ]] && echo -n $'\e[?7l'

Übrigens: Selbst dann, wenn die Kontrollsequenzen, die ich brauche, in der Terminfo-Datenbank enthalten wären und ich tput nutzen würde, käme ich nicht um diesen Test herum. tput sendet seine Codes unabhängig davon, was auf der Gegenseite sitzt. Das kann man leicht mit einem

$ (tput setaf 1; echo hi) > /tmp/foo
$ xxd foo
0000000: 1b5b 3331 6d68 690a                      .[31mhi.

nachvollziehen. Wie man sieht, ist der Farbcode hier enthalten.