Чому перевірка наявності папки з використанням -d у порожній рядку повертає істинне?


8

Я писав кілька сценаріїв і писав щось подібне

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

Що сталося, це те, що з глупоти я виконав другий рядок, не виконавши першого. Виявилося, що [-d ""] повертає істину і вираз став

rm -rf /*

На щастя, це була лише тестова машина, і я не судо, але хоч і втратив деякі дані

Моє запитання, чому [-d ""] повертати справжню ?? в документації чітко зазначено, що вона перевіряє, чи існує шлях і чи папка

Я вирішив проблему, використовуючи

[ -e $ARTIFACTS ]
який, здається, працює

Ура


5
А може, ви виконали обидва рядки. У наведеному вище прикладі коду ви ніколи не встановлюєте ARTIFCATS.
Бух

2
Я б просто написав це як rm -rf $ARTIFACTSбез /*. Це також видалить $ARTIFACTSкаталог, що добре, тому що якщо я хочу бути впевненим, що він існує, перш ніж щось у нього вставити, я все mkdir -p $ARTIFACTSодно виконаю . Він також видалить приховані файли всередині $ARTIFACTS, що також добре, тому що я б не писав, rm -rf $ARTIFACTS/*якщо $ARTIFACTSмістив би щось, що хотів би зберегти.
Крістофер Хаммарстрем

@ ChristofferHammarström дуже правда
Moataz Elmasry

Відповіді:


9

1. Ці два тести повертають істину :

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

відповідно до POSIX (як пояснив @Tim).

2. Але це повертає помилку ( не відповідає дійсності, як зазначено в запитанні)

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

тому що testвикликається двома аргументами (хоча другий - порожній рядок).

3. Ось чому корисно використовувати [[ … ]]замість test( [ … ]), яку надає більшість (усіх?) Поточних оболонок. Ця конструкція перевіряє, чи надаєте достатньо аргументів (інакше викидає помилку та перериває)

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

або просто поводиться так, як можна було б очікувати:

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

4. І, як вказував @Gilles, ще важливішим є подвійне заміщення котирувань. Таким чином, -d "$SOME_UNSET_VAR"розширюється на -d ""і повертає false навіть з test(дорівнює випадку 2). Отже, це також сумісно з оболонкою Bourne sh:

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

випробуваний з басом 3.00.16 (1) та 4.1.5 (1)


1
Bash, ksh та zsh надають, [[ … ]]але не просто sh. Дійсно важлива практика , щоб помістити в подвійні лапки підстановки команд : [ -d "$ARTIFACTS" ].
Жил "ТАК - перестань бути злим"

6

На це питання вже відповіли в StackOverflow. У ньому йдеться про те, що згідно зі стандартом POSIX, testвін завжди повинен бути успішним, якщо він викликається точно одним не порожнім аргументом (і жодних інших аргументів).

Так має бути і у випадку test -e(і насправді це є в моїй системі), тому будьте обережні.

Замість цього використовуйте:

[ -d "$ARTIFACTS" ]

test Потім буде викликано два аргументи, навіть якщо змінна порожня і повертає помилкову в цьому випадку.


"точно один непустий аргумент." - це оманливе резюме - сторінка, на яку ви пов’язані, згадує, що викликається саме одним аргументом, а цей аргумент є не порожнім; нічого про випадок непорожнього аргументу, за яким слідує порожній. Правильним рішенням повинно бути оточення імені змінної в лапках.
Випадково832

@ Random832 Дякую! Я впровадив обидва запропоновані вами зміни.
Тім

[ ! -z $ARTIFACTS ] && [ -d $ARTIFACTS ]чи не краще: він тільки підходить для конкретного випадку , коли $ARTIFACTSпорожньо, але зазнає невдачі , коли $ARTIFACTSможе містити пробіли або \[?*. [ -d "$ARTIFACTS" ]це правильний спосіб зробити це (або [[ -d $ARTIFACTS ]]в оболонках, які є [[ … ]]).
Жил "ТАК - перестань бути злим"

@Gilles Ви маєте рацію, я її зняв. Дякую!
Тім

4

Я вирішив проблему, використовуючи [-e $ ARTIFACTS], який, здається, працює

Ви помиляєтесь . Це працює тому $ARTIFACTS, що тепер щось встановлено.

Коли змінна не встановлена, тоді говорять

[ -d $SOMEVAR ]

або

[ -e $SOMEVAR ]

б як оцінити, trueтак як це означає , кажучи

[ -d ]

і

[ -e ]

відповідно. (Сказання [ foobar ]завжди оцінюватиметься правдою.)

Говорячи

set -u

стане в нагоді в таких ситуаціях. help setсказав би вам:

  -u  Treat unset variables as an error when substituting.

[-e ""] && echo "PRINT SOMETHING" нічого не друкує, так як це може бути неправильним?
Моатаз Ельмасрі

2
ах лайно [-е $ АРТИФАКТИ] з порожніми артефактами дає [-е] не [-е ""], отримав
Моатаз Ельмасрі

4

Подивіться, що ви встановили змінну ARTIFACTS і перевіряли наявність ARTIFCATS. Мабуть, затуманившись?

У будь-якому випадку, -d так само, як -e, дають однакові результати і при незмінених змінних.

Тому використовуйте подвійні лапки, і це допоможе вам.

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

ПРИМІТКА. Якщо у вашому "/ ДЕЯКОМ / ПАТ" є будь-яка папка з простором, сценарій, який ви згадали, порушиться з помилкою "очікується бінарний оператор".

Приклад:

ARTIFACTS="/home backup/"

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

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

зробить добре. Не забудьте також поставити лапки у rmвиклику ( rm -rf $ARTIFACTSбадьоро видалить, /homeа потім скаржиться на backup/*відсутність).

Крім того, включення -Lперевірки переконається, що це каталог, а не просто символічне посилання на каталог.

Отже, в основному,

[ -d "$ARTIFACTS" && ! -L "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS"/*
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.