2010-09-26 5 views
11

warum tut:bash, wenn mit oder und Negation

#!/bin/bash 
wtf=false 
if [ $wtf ] || [ ! -f filethatexists.whatever ] 
then 
echo "WTF1" 
fi 
if [ ! -f filethatexists.whatever ] 
then 
echo "WTF2" 
fi 

Druck:

WTF1

statt nichts? Es ist besonders verwirrend, dass die zweite Form wie erwartet funktioniert und die erste nicht.

Antwort

10

Der Basistest

[ $wtf ] 

prüft, ob die Zeichenfolge in der Mitte leer ist oder nicht.

Da $wtf enthält die Zeichenfolge 'false', gibt der Test wahr oder Exit-Status 0 für den Erfolg, weil 'false' nicht die gleiche wie die leere Zeichenkette ist '' - und damit erhalten Sie WTF1 als Antwort.

Versuchen mit:

wtf='' 

Wie von Gordon Davisson (und Dennis Williamson), ist es eine gute Idee, mit Streichern, vorsichtig zu sein, die Sie testen. In der Tat hätte ich sagen sollen, dass ich immer [ -n "$wtf" ] oder [ -z "$wtf" ] verwenden würde, um zu testen, ob eine Variable gesetzt ist, denn das war notwendig, als ich vor einem Vierteljahrhundert Shell gelernt habe. Ich habe Gegengeschichten von Bash Afficionados gehabt, dass Sie sich in Bash nicht darum kümmern müssen - aber ich denke, dass der Code hier ein Gegenbeispiel dafür darstellt, dass Sie sich tatsächlich immer noch darum kümmern müssen. So

, Praktiken einige am besten:

  • Enclose Variablen in doppelte Anführungszeichen getestet oder
  • (In Bash), verwenden [[ $wtf ]], die nicht wissen, wie die variable Expansion zu handhaben.
  • Verwenden Sie die Tests -n oder -z, um nicht leere oder leere Werte zu testen.

Es kann Ausnahmen zu den Regeln geben - aber Sie werden nicht weit gehen, ihnen zu folgen.

Betrachten Sie den Code:

wtf="1 -eq 0" 
[ $wtf ] && echo "WTF0" 
[[ $wtf ]] && echo "WTF1" 
wtf="false" 
[ $wtf ] && echo "WTF2" 
[[ $wtf ]] && echo "WTF3" 
wtf="" 
[ $wtf ] && echo "WTF4" 
[[ $wtf ]] && echo "WTF5" 
wtf="false" 
[ "$wtf" ] && echo "WTF6" 
[[ "$wtf" ]] && echo "WTF7" 
wtf="" 
[ "$wtf" ] && echo "WTF8" 
[[ "$wtf" ]] && echo "WTF9" 

Das erzeugt:

WTF1 
WTF2 
WTF3 
WTF6 
WTF7 

sowohl mit bash und ksh (wie auf MacOS X 10.6.4, gefunden bei der Ausführung mit 'bash testcode.sh' oder 'ksh testcode.sh'). Eine echte Bourne-Shell (wenn Sie immer noch so etwas finden) würde den Double-Bracket-Operationen widersprechen - es wäre nicht möglich, den Befehl '[[' unter $PATH zu finden.

Sie können die Tests erweitern, um mehr Fälle bis zum Überdruss zu erfassen.

+0

ok, macht Sinn. Die Syntaxhervorhebung machte mich zu der Ansicht, dass Boolesche Werte gegeben waren. Ich schätze, ich habe zu viel erwartet. – i30817

+0

Eigentlich ist es sogar noch seltsamer - wenn $ wtf Leerzeichen enthält, wird es als Ausdruck ausgewertet. Versuchen Sie es mit 'wtf =" 1 -eq 0 "und' wtf = "1 -eq 1", um den Effekt zu sehen. Für noch mehr Komik versuchen Sie 'wtf =" 1 -eq "' –

+0

Test 4/5 ist a Duplikate von Test 8/9 Ich glaube, du wolltest 4/5 ohne Anführungszeichen um die Variable machen (aber das Ergebnis ist das gleiche - nichts wird wiederholt) Auch der Test 0/1, der durch das Anführen der Variablen verändert wurde, bewertet nicht die '1 -eq 0', so dass beide Bracket-Typen wahr sind (wie in Test 2/3) Interessanterweise in Bash und ksh,' [''] ',' [[']] 'und' [] ' funktionieren und geben false zurück, jedoch erzeugt '[[]]' einen Fehler. –

0

if [ $wtf = true ] || [ ! -f . .

+0

oder sogar "if [[$ wtf == true]] || [! -f ... " – dimba

3

Hier ist ein handlicher kleiner Trick:

wtf=false 
if $wtf || [ ! -f filethatexists.whatever ] 

In dieser Form werden die Inhalte der Variablen ausgeführt und der Rückgabewert bestimmt, ob der Test bestanden oder nicht bestanden. Es passiert, dass true und false Bash-Builtins sind, die den entsprechenden Wert zurückgeben.

+1

Dieser" handliche kleine Trick "ist kaum eine gute Übung. Wenn Sie einen Codepath haben, bei dem' wtf' etwas anderes als 'true' oder' false' enthält, ergibt sich ein überraschendes Verhalten. Leere Zeichenfolge? Vorheriger Wert von ' $? 'überträgt sich!' rm -rf/'? Err ... –