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

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

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

в чем преимущество этого над просто проверкой значения, как это:

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

7 ответов


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

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

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

и имеет значение -n или -z или любые другие допустимые параметры для test command, синтаксис неоднозначен. The x at фронт предотвращает ведущую черточку от быть выбранным как вариант к test.

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


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

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

и $1 имеет значение '- n', синтаксис тестовой команды неоднозначен; неясно, что вы тестировали. "X" на фронте предотвращает ведущую черточку от причинять тревогу.

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

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


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

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 из.)

во-вторых, в других оболочках, кроме Bash, особенно более старых, условия тестирования, такие как'- z ' для проверки пустой переменной, не существовали, поэтому, пока это:

if [ -z "$SOME_VAR" ]; then
  echo "this variable is not defined"
fi

будет отлично работать в BASH, если вы стремитесь к переносимости в различных средах UNIX, где вы не можете быть уверены, что оболочка по умолчанию будет Bash и поддерживает ли она условие теста-z, безопаснее использовать форму, если ["x$SOME_VAR" = "x"], так как это всегда будет иметь предполагаемый эффект. По сути, это старый трюк сценариев оболочки для поиска пустой переменной, и он по-прежнему используется сегодня для обратной совместимости, несмотря на наличие более чистых методов.


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

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

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


Я считаю, что это из-за

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 

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


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


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