Perché controllare se esiste una cartella usando -d su una stringa vuota restituisce vero?


8

Stavo scrivendo alcuni script e ho scritto qualcosa del genere

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

Quello che è successo è che per stupidità ho eseguito la seconda riga senza eseguire la prima. Si è scoperto che [-d ""] restituisce true e l'espressione diventa

rm -rf /*

Fortunatamente era solo una macchina di prova e non ero un sudo, ma sebbene avessi perso alcuni dati

La mia domanda è: perché [-d ""] restituisce true ?? la documentazione indica chiaramente che esiste un percorso ed è una cartella

Ho risolto il problema utilizzando

[ -e $ARTIFACTS ]
che sembra funzionare

Saluti


5
O forse hai eseguito entrambe le righe. Nell'esempio di codice sopra, non hai mai impostato ARTIFCATS.
Buhb

2
Lo scriverei solo rm -rf $ARTIFACTSsenza /*. Questo eliminerebbe anche la $ARTIFACTSdirectory, il che va bene, perché se voglio essere sicuro che esista prima di inserire qualcosa in esso, eseguirò mkdir -p $ARTIFACTScomunque. Eliminerà anche i file nascosti all'interno $ARTIFACTS, il che va bene, perché non scriverei rm -rf $ARTIFACTS/*se $ARTIFACTScontenesse qualcosa che volevo salvare.
Christoffer Hammarström,

@ ChristofferHammarström molto vero
Moataz Elmasry

Risposte:


9

1. Questi due test tornano veri :

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

secondo POSIX (come spiegato da @Tim).

2. Ma questo restituisce false ( non vero come indicato nella domanda)

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

perché testviene chiamato con due argomenti (sebbene il secondo sia una stringa vuota).

3. Ecco perché è buona norma utilizzare al [[ … ]]posto di test( [ … ]), fornito dalla maggior parte delle shell (tutte?) Correnti. Questo costrutto verifica se si forniscono argomenti sufficienti (altrimenti genera un errore e si interrompe)

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

o semplicemente si comporta come ci si aspetterebbe:

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

4. E, come sottolineato da @Gilles, ancora più importante è raddoppiare la sostituzione delle citazioni. Quindi si -d "$SOME_UNSET_VAR"espande -d ""e restituisce false anche con test(uguale al caso 2). Quindi questo è anche compatibile con la shell Bourne sh:

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

testato con bash 3.00.16 (1) e 4.1.5 (1)


1
Bash, ksh e zsh forniscono [[ … ]]ma non sono chiari sh. La pratica veramente importante è quello di mettere le virgolette intorno sostituzioni di comando : [ -d "$ARTIFACTS" ].
Gilles 'SO- smetti di essere malvagio'

6

A questa domanda è già stata data una risposta su StackOverflow. Dice che secondo lo standard POSIX, testdovrebbe sempre tornare a buon fine se viene chiamato con esattamente un argomento non vuoto (e nessun altro argomento).

Questo dovrebbe valere anche per test -e(e in effetti è sul mio sistema), quindi fai attenzione.

Invece usa:

[ -d "$ARTIFACTS" ]

test verrà quindi chiamato con due argomenti anche se la variabile è vuota e restituisce false in questo caso.


"esattamente un argomento non vuoto." - questo è un sommario fuorviante - la pagina che hai citato menziona essere chiamata con esattamente un argomento e quell'argomento non è vuoto; nulla del caso di un argomento non vuoto seguito da uno vuoto. La soluzione corretta dovrebbe essere quella di racchiudere il nome della variabile tra virgolette.
Casuale 832

@ Random832 Grazie! Ho implementato entrambe le modifiche suggerite.
Tim

[ ! -z $ARTIFACTS ] && [ -d $ARTIFACTS ]non è migliore: si rivolge al caso particolare solo quando $ARTIFACTSè vuoto, ma fallisce quando $ARTIFACTSpuò contenere spazi bianchi o \[?*. [ -d "$ARTIFACTS" ]è il modo corretto di farlo (o [[ -d $ARTIFACTS ]]nelle shell che hanno [[ … ]]).
Gilles 'SO- smetti di essere cattivo'

@Gilles Hai ragione, l'ho rimosso. Grazie!
Tim

4

Ho risolto il problema usando [-e $ ARTIFACTS] che sembra funzionare

Ti sbagli . Funziona perché $ARTIFACTSora è impostato su qualcosa.

Quando una variabile non è impostata, quindi dire

[ -d $SOMEVAR ]

o

[ -e $SOMEVAR ]

sarebbe sia valutata come trueperché implica dicendo

[ -d ]

e

[ -e ]

rispettivamente. (Dire [ foobar ]sarebbe sempre vero).

Detto

set -u

è utile in tali situazioni. help setti direi:

  -u  Treat unset variables as an error when substituting.

[-e ""] && echo "STAMPA QUALCOSA" non stampa nulla, quindi come può essere sbagliato?
Moataz Elmasry

2
ahh merda [-e $ ARTIFACTS] con manufatti vuoti produce [-e] non [-e ""], capito
Moataz Elmasry

4

Vedi che hai impostato la variabile ARTIFACTS e che stavi controllando ARTIFCATS. Probabilmente errori di digitazione?

Comunque, -d e -e produrrebbe gli stessi risultati su variabili non impostate.

Quindi usa le virgolette doppie e ti aiuterà.

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

NOTA: Se "/ SOME / PATH" ha una cartella con spazio, lo script che hai citato si romperà con l'errore "operatore binario previsto".

Esempio:

ARTIFACTS="/home backup/"

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

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

andrà bene. Non dimenticare di inserire anche le virgolette rmnell'invocazione ( rm -rf $ARTIFACTSrimuoverà allegramente e /homepoi si lamenterebbe di backup/*non esistere).

Inoltre, incluso il -Lcontrollo farà in modo che si tratti di una directory e non solo di un collegamento simbolico a una directory.

Quindi in poche parole,

[ -d "$ARTIFACTS" && ! -L "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS"/*
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.