Почему сравнения сценариев оболочки часто используют x $ VAR = xyes? - PullRequest
54 голосов
/ 06 октября 2008

Я часто вижу это в скриптах сборки проектов, которые используют автоинструменты (autoconf, automake). Когда кто-то хочет проверить значение переменной оболочки, он часто использует эту идиому:

if test "x$SHELL_VAR" = "xyes"; then
...

В чем преимущество перед простой проверкой значения следующим образом:

if test $SHELL_VAR = "yes"; then
...

Я полагаю, что должна быть какая-то причина, по которой я вижу это так часто, но не могу понять, что это такое.

Ответы [ 7 ]

80 голосов
/ 06 октября 2008

Если вы используете оболочку, которая выполняет простую подстановку , а переменная 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", но оно также будет обрабатывать переменную с пробелами в нем, не задавая это в качестве двух аргументов на команду test.

Другая причина (кроме пустых переменных) связана с обработкой опций. Если вы напишите:

if test "$1" = "abc" ; then ...

и $1 имеет значение -n или -z или любые другие допустимые параметры для команды test, синтаксис неоднозначен. x на передней панели предотвращает возможность выбора передней черты в качестве опции test.

Имейте в виду, что это зависит от оболочки. Некоторые оболочки (на мой взгляд, csh) будут горько жаловаться, если переменная окружения не существует, а просто возвращает пустую строку).

19 голосов
/ 08 октября 2008

Другая причина, о которой еще никто не упомянул, связана с обработкой опций. Если вы напишите:

if [ "$1" = "abc" ]; then ...

и $ 1 имеет значение '-n', синтаксис команды test неоднозначен; Непонятно, что вы тестировали. Буква «х» на передней панели предотвращает возникновение проблем с ведущей чертой.

Вы должны смотреть на действительно древние оболочки, чтобы найти такую, в которой команда тестирования не поддерживает -n или -z; команда версии 7 (1978) test включала их. Это не совсем неважно - некоторые версии UNIX версии 6 сбежали в BSD, но в наши дни вам будет крайне сложно найти что-то древнее в современном использовании.

Не использовать двойные кавычки вокруг значений опасно, как указали многие другие люди. Действительно, если есть вероятность того, что имена файлов могут содержать пробелы (MacOS X и Windows оба поддерживают это в некоторой степени, и Unix всегда поддерживал это, хотя такие инструменты, как xargs усложняют его), тогда вы должны заключать имена файлов в двойные цитаты каждый раз, когда вы используете их тоже. Если вы не отвечаете за это значение (например, во время обработки опций, и вы устанавливаете переменную на «нет» при запуске и «да», когда флаг включен в командную строку), тогда небезопасно использовать формы переменных без кавычек пока вы не доказали их безопасность - и вы можете делать это все время для многих целей. Или документируйте, что ваши сценарии ужасно потерпят неудачу, если пользователи попытаются обработать файлы с пробелами в именах. (Есть и другие персонажи, о которых нужно беспокоиться - например, кавычки тоже могут быть довольно неприятными.)

10 голосов
/ 06 октября 2008

Есть две причины, по которым я знаю это соглашение:

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, безопаснее использовать форму if ["x $ SOME_VAR" = "x"], поскольку это всегда будет иметь ожидаемый эффект. По сути, это старый прием сценариев оболочки для поиска пустой переменной, и он все еще используется сегодня для обратной совместимости, несмотря на то, что доступны более чистые методы.

4 голосов

Я рекомендую вместо:

if test "yes" = "$SHELL_VAR"; then

, поскольку он устраняет уродливое x и все еще решает проблему, упомянутую в https://stackoverflow.com/a/174288/895245, что $SHELL_VAR может начинаться с - и читаться как опция.

2 голосов
/ 06 октября 2008

Я верю, что это из-за

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 

работает так же хорошо, но было бы легче отлаживать.

1 голос
/ 06 октября 2008

Если вы не выполните «x $ SHELL_VAR», то, если $ SHELL_VAR не определено, вы получите ошибку о том, что «=» не является монадическим оператором или чем-то подобным.

1 голос
/ 06 октября 2008

Я делал это в DOS, когда SHELL_VAR мог быть неопределенным.

...