test (/bin/test) ist ein Unix-Werkzeug, mit dem logische Vergleiche angestellt werden können. Es gehört zur grundlegenden Ausstattung Unix-artiger Systeme, für UNIX-Systeme ist sein Verhalten durch den POSIX-Standard genormt.[1]

Viele Shells implementieren es mittlerweile als built-in-Kommando, aber diese Implementierungen leiten sich alle von der ursprünglichen Version als stand-alone-Programm ab.

Aus Usability-Gründen existiert das Programm auch unter dem Namen /bin/[, das sich genau wie /bin/test verhält, nur dass es als letztes Argument ] erwartet. Siehe Geschichte.

Arbeitsweise Bearbeiten

Konventionsgemäß werden von Unix-Programmen (Rückgabe-)Werte von 0 als logisch wahr, Werte ungleich 0 als unwahr bzw. falsch interpretiert. test analysiert den in den übergebenen Argumenten beschriebenen einfachen oder zusammengesetzten logischen Ausdruck und weist das Ergebnis desselben als Return-Code (RC) 0 oder 1 aus. Da die Verzweigungsoperationen der Shell-Skriptsprachen typischerweise von diesem Rückgabewert abhängen, ist damit eine generalisierte Methode, Verzweigungen zu parametrieren, geschaffen.

Im folgenden Anwendungsbeispiel wird die do..while-Schleife solange durchlaufen, wie der Wert Counter kleiner als oder gleich 100 ist:

#! /bin/ksh
typeset Counter=0

while test $Counter -le 100 ; do
   (( Counter += 1 ))
done

Parameter Bearbeiten

/bin/test (bzw. /bin/[) nimmt als Argumente einerseits Werte (Strings, Integers und Dateinamen), andererseits logische Operatoren (Vergleiche, Existenzbedingungen von Datei-Metadaten und logische Verknüpfungen) entgegen. Daraus wird der Wahrheitswert einer einfachen oder zusammengesetzten logischen Funktion ermittelt und über den Return Code an den aufrufenden Prozess zurückübergeben.

Grundsätzlich gilt für test der Utility Syntax Guidelines-Teil der POSIX-Spezifikation, allerdings mit der Ausnahme Guideline 10,[2] dass nämlich --, sofern kein Options-Argument, als abschließende Option interpretiert werden soll, auf die lediglich Operanden folgen – selbst, wenn diese mit - eingeleitet werden.[3]

Die Operatoren gliedern sich in:

Dateibezogene Operatoren Bearbeiten

Datei-Operatoren erwarten einen einzelnen Pfadnamen als Argument. Es kann die Existenz einer Datei (-e <file> : die Datei existiert), das Bestehen bestimmter Zugriffsrechte (-r <file> : die Datei existiert und ist lesbar) oder auf einen bestimmten Dateityp (-d <file> : die Datei existiert und ist ein Directory) geprüft werden.

Integer-Operatoren Bearbeiten

Integer-Operatoren erwarten zwei Integer-Werte als Argumente, die gemäß dem Operator miteinander verglichen werden (<Int1> -ge <Int2> : Int1 ist größer oder gleich Int2). Dies entspricht den Vergleichsoperatoren >, >=, == etc. der meisten Programmiersprachen. Da die Shells gewöhnlich nicht zwischen Datentypen unterscheiden, muss das aufrufende Programm (meist ein Script) selbst Sorge dafür tragen, dass die übergebenen Werte auch als Integer interpretierbar sind. Argumente, auf die dies nicht zutrifft, führen zu einem Laufzeitfehler.

String-Operatoren Bearbeiten

String-Operatoren erwarten zwei Strings als Argumente und prüfen auf Gleichheit oder Ungleichheit ("<String1>" = "<String2>" : String1 ist identisch mit String2). Wegen der oben erwähnten mangelnden Typgenauigkeit der Shell-Sprachen können zwar Integer-Werte als Strings aber nicht umgekehrt miteinander verglichen werden. Die aus anderen Programmiersprachen gewohnte Mehrdeutigkeit von Vergleichsoperatoren, dass also etwa == sowohl die Gleichheit von Strings wie von numerischen Werten feststellt, ist ausdrücklich nicht gegeben.

Logische Operatoren Bearbeiten

Mehrere der oben genannten Einzelbedingungen können darüber hinaus noch durch logisches UND bzw. ODER miteinander verknüpft werden (<Int1> -ne <Int2> -a -r <file>  : <Int1> ist nicht gleich <Int2> UND der ausführende Prozeß hat Leserechte auf <file>). Allerdings schränkt der POSIX-Standard das Verhalten von test als "unspecified" bei der Übergabe von mehr als 4 Argumenten ein.[1]

Sowohl -a als auch -o sind linksassoziativ, wobei -a eine höhere Präzedenz als -o hat. Diese Regelungen sind allerdings beide als obsolescent gekennzeichnet.[3][4]

Operator-Präzedenz, Gruppierung Bearbeiten

Es gilt implizit die auch in anderen Programmiersprachen übliche Präzedenz logischer Operatoren, aber auch ein davon abweichendes Verhalten kann mit Hilfe von Gruppierungssymbolen (\(...\)) erzwungen werden. Dies hat zwar durch die Limitierungen des POSIX-Standards kaum eine Bedeutung, aber die allermeisten realen Implementierungen können deutlich mehr (und eine für praktische Zwecke unbegrenzte Anzahl) an Argumenten als die spezifizierten 4 verarbeiten:

#! /bin/ksh
typeset -i x=0
typeset -i y=1
typeset    z=""

if test \( $x -gt 0 -a $y   -ne 0 \) -o "$z" != "" ; then
      print - "( falsch UND wahr ) ODER wahr -> wahr"
fi
if test $x    -gt 0 -a \( $y -ne 0   -o "$z" != "" \) ; then
      :
else
      print - "falsch UND ( wahr ODER falsch ) -> falsch"
fi

Geschichte Bearbeiten

Der ursprüngliche Entwurf sah test vor, wie es heute existiert. Da sich aber damit das Erscheinungsbild von Shell-Programmen deutlich von dem anderer Programmiersprachen unterschied, wurde test mit einem Link auch als [ verfügbar gemacht. Das änderte das Erscheinungsbild von Scripten zu:

#! /bin/ksh
typeset -i Counter=0

while [ $Counter -le 100 ; do
      (( Counter += 1 ))
done

hätte aber zu dem etwas ungewöhnlichen Umstand geführt, dass scheinbar eine geöffnete Klammer nicht geschlossen wird. Deshalb wurde das Binary dahingehend verändert, ein abschließendes ] als Argument zu erwarten, wenn es als [ aufgerufen wird. Damit war das heute übliche Erscheinungsbild in Scriptsprachen festgelegt. Das ist auch der Grund dafür, warum [ und ] stets von Leerzeichen (dem Internal Field Separator der Shell) umschlossen sein müssen:

#! /bin/ksh
typeset -i Counter=0

while [ $Counter -le 100 ] ; do
      (( Counter += 1 ))
done

Viele heutige Betriebssystemversionen ersetzen den ursprünglichen Link von /bin/test auf /bin/[ durch ein eigenes Binary, was allerdings keinen Einfluss auf die Funktion hat.

test als Shell-Built-In Bearbeiten

Die meisten modernen Shells stellen aus Performance-Gründen ein ähnlich funktionierendes Built-in-Kommando zur Verfügung. Die Kornshell und die Bourne-again shell implementieren dieses test als [[, das folgerichtig ]] als abschließendes Argument erwartet. Allerdings wird (zum Unterschied zum herkömmlichen test) weder filename expansion noch field splitting durchgeführt. Dies beeinflusst etwa Prüfungen auf Dateinamen, welche Leerzeichen enthalten, wie das Beispiel illustriert: es existiere im working directory ein Subdirectory mit Namen test dir, dann wird der erste Test positiv ausfallen (wegen des nicht erfolgten field splittings wird "test dir" trotz weggelassenem Quoting als Bezeichner verwendet), der zweite hingegen nach durchgeführtem field splitting fehlschlagen, weil nur nach "test" gesucht wird.

#! /bin/ksh
typeset FOO="test dir"

if [[ -d $FOO ]] ; then
   print - "internal test: dir $FOO found"
fi
if [ -d $FOO ] ; then
   print - "regular test: dir $FOO found"
fi

Weblinks Bearbeiten

Wikibooks: Linux-Praxisbuch: Shellprogrammierung – Lern- und Lehrmaterialien

Einzelnachweise Bearbeiten

  1. a b test-Spezifikation der Open Group. Abgerufen am 13. April 2013 (englisch).
  2. 12.2 Utility Syntax Guidelines, auf pubs.opengroup.org
  3. a b test-Spezifikation der Open Group. Abgerufen am 6. August 2021 (englisch).
  4. Codes-Definitionen, Anhang zur IEEE Std 1003.1-2017-Spezifikation. Abgerufen am 6. August 2021 (englisch).