Warum gibt die Überprüfung, ob ein Ordner mit -d für eine leere Zeichenfolge vorhanden ist, true zurück?

8

Ich habe einige Skripte geschrieben und so etwas geschrieben

ARTIFACTS="/SOME/PATH"
[ -d $ARTIFCATS ] && rm -rf $ARTIFACTS/*

Was passiert ist, ist, dass ich aus Dummheit die zweite Zeile ausgeführt habe, ohne die erste auszuführen. Es stellte sich heraus, dass [-d ""] true zurückgibt und der Ausdruck wurde

rm -rf /*

Zum Glück war es nur eine Testmaschine und ich war kein Sudo, aber obwohl ich einige Daten verloren habe

Meine Frage ist, warum [-d ""] true zurückgibt? In der Dokumentation wird eindeutig angegeben, ob ein Pfad vorhanden ist und ein Ordner ist

Ich habe das Problem mit gelöst

[ -e $ARTIFACTS ]
das scheint zu funktionieren

Prost

Moataz Elmasry
quelle
5
Oder vielleicht haben Sie beide Zeilen ausgeführt. Im obigen Codebeispiel setzen Sie niemals ARTIFCATS.
Buhb
2
Ich würde das einfach so schreiben wie rm -rf $ARTIFACTSohne das /*. Dies würde auch das $ARTIFACTSVerzeichnis löschen , was in Ordnung ist, denn wenn ich sicher sein möchte, dass es existiert, bevor ich etwas hineinlege, werde ich es mkdir -p $ARTIFACTStrotzdem ausführen . Es werden auch versteckte Dateien darin gelöscht $ARTIFACTS, was auch in Ordnung ist, da ich nicht schreiben würde, rm -rf $ARTIFACTS/*wenn etwas$ARTIFACTS enthalten wäre, das ich speichern wollte.
Christoffer Hammarström
@ ChristofferHammarström sehr wahr
Moataz Elmasry

Antworten:

9

1. Diese beiden Tests geben true zurück :

# [ -d ] && echo true || echo false
true
# [ -d $SOME_UNSET_VAR ] && echo true || echo false
true

gemäß POSIX (wie von @Tim erklärt).

2. Dies gibt jedoch false zurück ( nicht true, wie in der Frage angegeben).

# [ -d "" ] && echo true || echo false
false

weil testwird mit zwei Argumenten aufgerufen (obwohl das zweite eine leere Zeichenfolge ist).

3. Deshalb ist es eine gute Praxis, [[ … ]]anstelle von test( [ … ]) zu verwenden, was die meisten (alle?) Aktuellen Shells bieten. Dieses Konstrukt prüft, ob Sie genügend Argumente angeben (andernfalls wird ein Fehler ausgegeben und abgebrochen).

# [[ -d ]] && echo true || echo false
bash: unexpected argument `]]' to conditional unary operator
bash: syntax error near `]]'

oder verhält sich einfach so, wie man es erwarten würde:

# [[ -d $SOME_UNSET_VAR ]] && echo true || echo false
false

4. Und wie von @Gilles hervorgehoben, ist es noch wichtiger, Substitutionen in doppelten Anführungszeichen zu setzen. So -d "$SOME_UNSET_VAR"dehnt sich auf -d ""und kehrt falsch sogar mit test(entspricht Fall 2). Daher ist dies auch mit der Bourne-Shell kompatibel sh:

# [ -d "$SOME_UNSET_VAR" ] && echo true || echo false
false

getestet mit Bash 3.00.16 (1) und 4.1.5 (1)

mpy
quelle
1
Bash, ksh und zsh bieten [[ … ]]aber nicht einfach sh. Die wirklich wichtige Vorgehensweise besteht darin , Befehlsersetzungen in doppelte Anführungszeichen zu setzen : [ -d "$ARTIFACTS" ].
Gilles 'SO - hör auf böse zu sein'
6

Diese Frage wurde bereits auf StackOverflow beantwortet . Es heißt, dass gemäß dem POSIX-Standard testimmer erfolgreich zurückgegeben werden sollte, wenn es mit genau einem nicht leeren Argument (und keinen anderen Argumenten) aufgerufen wird.

Dies sollte auch bei test -e(und tatsächlich auf meinem System) der Fall sein , seien Sie also vorsichtig.

Verwenden Sie stattdessen:

[ -d "$ARTIFACTS" ]

test wird dann mit zwei Argumenten aufgerufen, auch wenn die Variable leer ist und in diesem Fall false zurückgibt.

Tim
quelle
"genau ein nicht leeres Argument." - Dies ist eine irreführende Zusammenfassung. - Die Seite, die Sie verlinkt haben, erwähnt, dass genau ein Argument aufgerufen wurde und dieses Argument nicht leer ist. nichts über den Fall eines nicht leeren Arguments, gefolgt von einem leeren. Die richtige Lösung sollte darin bestehen, den Variablennamen in Anführungszeichen zu setzen.
Random832
@ Random832 Danke! Ich habe beide von Ihnen vorgeschlagenen Änderungen umgesetzt.
Tim
[ ! -z $ARTIFACTS ] && [ -d $ARTIFACTS ]ist nicht besser: Es ist nur für den speziellen Fall geeignet, wenn $ARTIFACTSes leer ist, schlägt jedoch fehl, wenn $ARTIFACTSLeerzeichen oder enthalten können \[?*. [ -d "$ARTIFACTS" ]ist der richtige Weg, dies zu tun (oder [[ -d $ARTIFACTS ]]in Muscheln, die haben [[ … ]]).
Gilles 'SO - hör auf böse zu sein'
@ Gilles Du hast recht, ich habe es entfernt. Danke!
Tim
4

Ich habe das Problem mit [-e $ ARTIFACTS] gelöst, was zu funktionieren scheint

Du liegst falsch . Es funktioniert, weil $ARTIFACTSjetzt auf etwas eingestellt ist.

Wenn eine Variable nicht gesetzt ist, dann sagen

[ -d $SOMEVAR ]

oder

[ -e $SOMEVAR ]

würde beide bewerten, trueweil es sagt

[ -d ]

und

[ -e ]

beziehungsweise. (Sprichwort [ foobar ]würde immer als wahr bewertet.)

Sprichwort

set -u

ist in solchen Situationen nützlich. help setwürde dir sagen:

  -u  Treat unset variables as an error when substituting.
devnull
quelle
[-e ""] && echo "PRINT SOMETHING" druckt nichts. Wie kann das also falsch sein?
Moataz Elmasry
2
ahh Mist [-e $ ARTIFACTS] mit leeren Artefakten ergibt [-e] nicht [-e ""], verstanden
Moataz Elmasry
4

Stellen Sie sicher, dass Sie die Variable ARTIFACTS gesetzt und nach ARTIFCATS gesucht haben. Wahrscheinlich falsch tippen?

Auf jeden Fall würden -d und -e bei nicht gesetzten Variablen die gleichen Ergebnisse liefern.

Verwenden Sie daher doppelte Anführungszeichen und es wird Ihnen helfen.

ARTIFACTS="/SOME/PATH"
[ -d "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS/"*

HINWEIS: Wenn Ihr "/ SOME / PATH" einen Ordner mit Speicherplatz hat, wird das von Ihnen erwähnte Skript mit dem Fehler "Binäroperator erwartet" unterbrochen.

Beispiel:

ARTIFACTS="/home backup/"

1) [ -d $ARTIFACTS ] && rm -rf $ARTIFACTS/*
bash: [: /home: binary operator expected

2) [ -d "$ARTIFACTS" ] && rm -rf "$ARTIFACTS"/*

wird gut tun. Vergessen Sie nicht, auch Anführungszeichen in den rmAufruf aufzunehmen ( rm -rf $ARTIFACTSwürde fröhlich entfernen und sich /homedann über backup/*nicht existierende beschweren ).

Durch die -LÜberprüfung wird auch sichergestellt, dass es sich um ein Verzeichnis handelt und nicht nur um einen symbolischen Link zu einem Verzeichnis.

Also im Grunde genommen,

[ -d "$ARTIFACTS" && ! -L "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS"/*
Thiruvenkadam
quelle