Почему в shell script часто используются x $VAR = xyes?
Я часто вижу это в сценариях сборки проектов, использующих autotools (autoconf, automake). Когда кто-то хочет проверить значение переменной оболочки, они часто используют эту идиому:
if test "x$SHELL_VAR" = "xyes"; then
...
В чем преимущество этого, просто проверяя значение, подобное этому:
if test $SHELL_VAR = "yes"; then
...
Я полагаю, что должна быть какая-то причина, что я вижу это так часто, но я не могу понять, что это такое.
Ответы
Ответ 1
Если вы используете оболочку, которая выполняет простую подстановку, а переменная SHELL_VAR
не существует (или является пустой), то вам нужно следить за крайними случаями. Произойдут следующие переводы:
if test $SHELL_VAR = yes; then --> if test = yes; then
if test x$SHELL_VAR = xyes; then --> if test x = xyes; then
Первое из них приведет к ошибке, поскольку первый аргумент test
пропал. Второй не имеет этой проблемы.
Ваш случай переводится следующим образом:
if test "x$SHELL_VAR" = "xyes"; then --> if test "x" = "xyes"; then
x
, по крайней мере для POSIX-совместимых оболочек, на самом деле является избыточным, поскольку из кавычек следует, что как пустой аргумент, так и один содержащий пробелы интерпретируются как один объект.
Ответ 2
Другая причина, о которой еще никто не упомянул, касается обработки опций. Если вы пишете:
if [ "$1" = "abc" ]; then ...
и $1 имеет значение '-n', синтаксис тестовой команды неоднозначен; неясно, что вы тестировали. "X" на передней панели предотвращает возникновение проблем с ведущим тире.
Вы должны смотреть на действительно древние раковины, чтобы найти ту, где тестовая команда не поддерживает -n
или -z
; команда Версии 7 (1978) test
включила их. Это не совсем неуместно - некоторые версии UNIX версии 6 ускользнули в BSD, но в наши дни вам будет крайне сложно найти что-нибудь древнее в текущем использовании.
Не использование двойных кавычек вокруг значений опасно, как указывал ряд других людей. В самом деле, если есть вероятность, что имена файлов могут содержать пробелы (MacOS X и Windows в любом случае поощряют это, и Unix всегда поддерживала его, хотя инструменты, такие как xargs
, усложняют), тогда вы должны заключать имена файлов в двойные цитирует каждый раз, когда вы их используете. Если вы не отвечаете за значение (например, во время обработки параметров, и вы устанавливаете переменную "нет" при запуске и "да", когда флаг включен в командную строку), небезопасно использовать некотируемые формы переменных пока вы не докажете их в безопасности - и вы также можете делать это все время для многих целей. Или документ о том, что ваши скрипты будут терпеть неудачу, если пользователи попытаются обработать файлы с пробелами в именах. (И есть и другие персонажи, о которых можно беспокоиться - например, backticks может быть довольно неприятным.)
Ответ 3
Есть две причины, по которым я знаю это соглашение:
http://tldp.org/LDP/abs/html/comparison-ops.html
В сложном тесте даже цитирование строковой переменной может оказаться недостаточным. [-n "$ string" -o "$ a" = "$ b" ] может привести к ошибке с некоторыми версиями Bash, если $string пуст. Безопасный способ - добавить дополнительный символ в возможно, пустые переменные, [ "x $string"!= x -o "x $a" = "x $b" ] ( "x's" отменяется).
Во-вторых, в других оболочках, чем Bash, особенно более старые, условия теста, такие как '-z' для проверки пустой переменной, не существовали, поэтому, если это:
if [ -z "$SOME_VAR" ]; then
echo "this variable is not defined"
fi
будет работать отлично в Bash, если вы нацелены на переносимость в различных средах UNIX, где вы не можете быть уверены, что оболочка по умолчанию будет Bash и поддерживает ли она условие -z test, это безопаснее использовать форму, если [ "x $SOME_VAR" = "x" ], поскольку это всегда будет иметь предполагаемый эффект. По сути, это старый трюк для сценариев для поиска пустой переменной, и он по-прежнему используется сегодня для обратной совместимости, несмотря на наличие доступных доступных методов.
Ответ 4
Вместо этого я рекомендую:
if test "yes" = "$SHELL_VAR"; then
поскольку он устраняет уродливый x
и по-прежнему решает проблему, упомянутую fooobar.com/questions/134219/..., что $SHELL_VAR
может начинаться с -
и быть читать как вариант.
Ответ 5
Я считаю, что благодаря
SHELLVAR=$(true)
if test $SHELLVAR = "yes" ; then echo "yep" ; fi
# bash: test: =: unary operator expected
а также
if test $UNDEFINEDED = "yes" ; then echo "yep" ; fi
# bash: test: =: unary operator expected
и
SHELLVAR=" hello"
if test $SHELLVAR = "hello" ; then echo "yep" ; fi
# yep
однако это обычно должно работать
SHELLVAR=" hello"
if test "$SHELLVAR" = "hello" ; then echo "yep" ; fi
#<no output>
но когда он жалуется на выход где-то в другом месте, его трудно сказать, на что он жалуется, я думаю, поэтому
SHELLVAR=" hello"
if test "x$SHELLVAR" = "xhello" ; then echo "yep" ; fi
работает так же хорошо, но будет легче отлаживать.
Ответ 6
Я использовал это в DOS, когда SHELL_VAR может быть undefined.
Ответ 7
Если вы не делаете "x $SHELL_VAR", то если $SHELL_VAR undefined, вы получите сообщение об ошибке "=", не являющемся монадическим оператором или чем-то вроде этого.