Mengapa memeriksa apakah ada folder menggunakan -d pada string kosong mengembalikan true?

8

Saya sedang menulis beberapa skrip dan menulis sesuatu seperti

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

Apa yang terjadi adalah karena kebodohan saya mengeksekusi baris kedua tanpa mengeksekusi yang pertama. Ternyata [-d ​​""] mengembalikan true dan ekspresi menjadi

rm -rf /*

Untungnya itu hanya mesin uji dan saya bukan sudo, tetapi meskipun saya kehilangan beberapa data

Pertanyaan saya adalah, mengapa [-d ​​""] kembali benar ?? dokumentasi dengan jelas menyatakan memeriksa apakah ada jalur dan folder

Saya memecahkan masalah dengan menggunakan

[ -e $ARTIFACTS ]
yang tampaknya berhasil

Bersulang

Moataz Elmasry
sumber
5
Atau mungkin Anda mengeksekusi kedua baris. Dalam contoh kode di atas, Anda tidak pernah menetapkan ARTIFCATS.
Buhb
2
Saya hanya akan menulis itu rm -rf $ARTIFACTStanpa /*. Ini juga akan menghapus $ARTIFACTSdirektori, yang baik-baik saja, karena jika saya ingin memastikan bahwa itu ada sebelum memasukkan sesuatu ke dalamnya, saya akan mkdir -p $ARTIFACTStetap menjalankannya . Ini juga akan menghapus file tersembunyi di dalamnya $ARTIFACTS, yang juga baik-baik saja, karena saya tidak akan menulis rm -rf $ARTIFACTS/*jika $ARTIFACTSberisi sesuatu yang ingin saya simpan.
Christoffer Hammarström
@ ChristofferHammarström sangat benar
Moataz Elmasry

Jawaban:

9

1. Dua tes ini menghasilkan true :

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

menurut POSIX (seperti yang dijelaskan oleh @Tim).

2. Tetapi ini mengembalikan false ( tidak benar seperti yang dinyatakan dalam pertanyaan)

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

karena testdipanggil dengan dua argumen (walaupun yang kedua adalah string kosong).

3. Itulah mengapa ini merupakan praktik yang baik untuk digunakan [[ … ]]alih-alih test( [ … ]), yang disediakan oleh kebanyakan (semua?) Shell saat ini. Konstruk ini memeriksa apakah Anda memberikan cukup argumen (jika tidak menimbulkan kesalahan dan batal)

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

atau hanya berperilaku seperti yang diharapkan:

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

4. Dan, seperti yang ditunjukkan oleh @Gilles, yang lebih penting adalah menggandakan substitusi kutipan. Jadi -d "$SOME_UNSET_VAR"perluas -d ""dan kembalikan false dengan test(sama dengan case 2). Karenanya ini juga kompatibel dengan shell Bourne sh:

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

diuji dengan bash 3.00.16 (1) dan 4.1.5 (1)

mpy
sumber
1
Bash, ksh dan zsh menyediakan [[ … ]]tapi tidak polos sh. Praktek benar-benar penting adalah untuk menaruh tanda kutip ganda di sekitar substitusi perintah : [ -d "$ARTIFACTS" ].
Gilles 'SANGAT berhenti menjadi jahat'
6

Pertanyaan ini sudah dijawab di StackOverflow. Dikatakan bahwa menurut standar POSIX, testharus selalu kembali berhasil jika dipanggil dengan tepat satu argumen tidak kosong (dan tidak ada argumen lain).

Ini juga harus menjadi kasus dengan test -e(dan sebenarnya itu ada di sistem saya), jadi berhati-hatilah.

Alih-alih gunakan:

[ -d "$ARTIFACTS" ]

test kemudian akan dipanggil dengan dua argumen bahkan jika variabelnya kosong dan mengembalikan false dalam kasus ini.

Tim
sumber
"Persis satu argumen kosong." - ini adalah ringkasan yang menyesatkan - halaman yang Anda tautkan disebut dengan tepat satu argumen dan argumen itu tidak kosong; tidak ada tentang kasus argumen kosong yang diikuti oleh argumen kosong. Solusi yang benar harus mengelilingi nama variabel dalam tanda kutip.
Random832
@ Random832 Terima kasih! Saya menerapkan kedua perubahan yang Anda sarankan.
Tim
[ ! -z $ARTIFACTS ] && [ -d $ARTIFACTS ]tidak lebih baik: itu hanya untuk kasus tertentu ketika $ARTIFACTSkosong, tetapi gagal ketika $ARTIFACTSmungkin berisi spasi atau \[?*. [ -d "$ARTIFACTS" ]adalah cara yang benar untuk melakukannya (atau [[ -d $ARTIFACTS ]]dalam cangkang yang dimiliki [[ … ]]).
Gilles 'SANGAT berhenti menjadi jahat'
@Gilles Anda benar, saya menghapusnya. Terima kasih!
Tim
4

Saya memecahkan masalah dengan menggunakan [-e $ ARTIFACTS] yang sepertinya berhasil

Anda salah . Ini berfungsi karena $ARTIFACTSsekarang diatur ke sesuatu.

Ketika variabel tidak disetel, lalu ucapkan

[ -d $SOMEVAR ]

atau

[ -e $SOMEVAR ]

akan sama - sama mengevaluasi truekarena itu menyiratkan perkataan

[ -d ]

dan

[ -e ]

masing-masing. (Mengatakan [ foobar ]akan selalu dinilai benar.)

Pepatah

set -u

berguna dalam situasi seperti itu. help setakan memberi tahu Anda:

  -u  Treat unset variables as an error when substituting.
devnull
sumber
[-e ""] && echo "PRINT SOMETHING" tidak mencetak apa pun, jadi bagaimana ini bisa salah?
Moataz Elmasry
2
ahh omong kosong [-e $ ARTIFACTS] dengan artefak kosong menghasilkan [-e] bukan [-e ""], mendapatkannya
Moataz Elmasry
4

Lihat bahwa Anda menetapkan variabel ARTIFACTS dan Anda memeriksa ARTIFCATS. Mungkin salah ketik?

Bagaimanapun, -d dan -e akan menghasilkan hasil yang sama pada variabel yang tidak disetel.

Karenanya gunakan tanda kutip ganda dan itu akan membantu Anda.

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

CATATAN: Jika "/ SOME / PATH" Anda memiliki folder dengan spasi, skrip yang Anda sebutkan akan terputus dengan kesalahan "operator biner yang diharapkan".

Contoh:

ARTIFACTS="/home backup/"

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

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

akan baik-baik saja. Jangan lupa untuk menaruh tanda kutip dalam rmdoa juga ( rm -rf $ARTIFACTSakan dengan senang hati menghapus /homekemudian mengeluh tentang backup/*tidak ada).

Juga, termasuk -Lcek akan memastikan bahwa itu adalah direktori dan bukan hanya tautan simbolik ke direktori.

Pada dasarnya,

[ -d "$ARTIFACTS" && ! -L "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS"/*
thiruvenkadam
sumber