test (Unix)
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
BearbeitenKonventionsgemäß 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
BearbeitenDatei-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
BearbeitenInteger-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
BearbeitenString-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
BearbeitenMehrere 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
BearbeitenEs 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
BearbeitenDer 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
Bearbeitentest
: evaluate expression – Open Group Base Specificationtest(1)
: condition evaluation utility – OpenBSD General Commands Manualtest(1)
: Dateitypen überprüfen und Werte vergleichen – Debian GNU/Linux Ausführbare Programme oder Shell-Befehle Handbuchseite
Einzelnachweise
Bearbeiten- ↑ a b test-Spezifikation der Open Group. Abgerufen am 13. April 2013 (englisch).
- ↑ 12.2 Utility Syntax Guidelines, auf pubs.opengroup.org
- ↑ a b test-Spezifikation der Open Group. Abgerufen am 6. August 2021 (englisch).
- ↑ Codes-Definitionen, Anhang zur IEEE Std 1003.1-2017-Spezifikation. Abgerufen am 6. August 2021 (englisch).