Почему проверка наличия папки с использованием -d в пустой строке возвращает true?

8

Я писал некоторые сценарии и написал что-то вроде

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

Случилось так, что по глупости я выполнил вторую строку, не выполняя первую. Оказалось, что [-d ""] возвращает true, и выражение стало

rm -rf /*

К счастью, это была только тестовая машина, и я не был sudo, но хотя я потерял некоторые данные

Мой вопрос, почему [-d ""] возвращает true ?? документация ясно заявляет, что проверяет, существует ли путь и является ли папка

Я решил проблему с помощью

[ -e $ARTIFACTS ]
который, кажется, работает

ура

Моатаз Эльмасри
источник
5
Или, может быть, вы выполнили обе строки. В приведенном выше примере кода вы никогда не устанавливаете ARTIFCATS.
Буб
2
Я бы просто написал это как rm -rf $ARTIFACTSбез /*. Это также удалит $ARTIFACTSкаталог, и это нормально, потому что если я хочу быть уверенным, что он существует, прежде чем что-то в него помещать, я все mkdir -p $ARTIFACTSравно выполню. Он также удалит скрытые файлы внутри $ARTIFACTS, что тоже хорошо, потому что я бы не стал писать, rm -rf $ARTIFACTS/*если бы $ARTIFACTSсодержал что-то, что хотел сохранить.
Кристофер Хаммарстрем
@ ChristofferHammarström очень верно
Моатаз Эльмасри

Ответы:

9

1. Эти два теста возвращают true :

# [ -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

протестировано с bash 3.00.16 (1) и 4.1.5 (1)

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

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

Это также должно быть в случае test -e(и на самом деле это в моей системе), поэтому будьте осторожны.

Вместо этого используйте:

[ -d "$ARTIFACTS" ]

test будет вызван с двумя аргументами, даже если переменная пуста и в этом случае вернет false.

Тим
источник
«ровно один непустой аргумент». - это вводящее в заблуждение резюме - на странице, на которую вы ссылаетесь, упоминается, что она вызывается ровно с одним аргументом, и этот аргумент является непустым; ничего о случае непустого аргумента, за которым следует пустой. Правильное решение должно заключаться в том, чтобы заключить имя переменной в кавычки.
Random832
@ Random832 Спасибо! Я применил оба предложенных вами изменения.
Тим
[ ! -z $ARTIFACTS ] && [ -d $ARTIFACTS ]не лучше: он обслуживает только тот конкретный случай, когда $ARTIFACTSон пуст, но не работает, когда $ARTIFACTSможет содержать пробелы или \[?*. [ -d "$ARTIFACTS" ]это правильный способ сделать это (или [[ -d $ARTIFACTS ]]в оболочках, которые имеют [[ … ]]).
Жиль "ТАК - перестань быть злым"
@ Жиль Вы правы, я удалил это. Спасибо!
Тим
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.
devnull
источник
[-e ""] && echo "PRINT SOMETHING" ничего не печатает, так как это может быть неправильно?
Моатаз Эльмасри
2
аааа хрень [-e $ ARTIFACTS] с пустыми артефактами дает [-e] не [-e ""], получил
Моатаз Эльмасри
4

Убедитесь, что вы установили переменную ARTIFACTS и проверяли ARTIFCATS. Возможно опечатка?

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

Поэтому используйте двойные кавычки, и это поможет вам.

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

ПРИМЕЧАНИЕ. Если в вашей папке «/ SOME / PATH» есть пробел, упомянутый вами скрипт сломается с ошибкой «ожидается двоичный оператор».

Пример:

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"/*
thiruvenkadam
источник