it-swarm.com.ru

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

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

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

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

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

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

54
jonner

Если вы используете оболочку, которая выполняет простую замену, а переменная Shell_VAR не существует (или является пустой), то вам нужно следить за случаями Edge. Произойдут следующие переводы:

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.

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

80
paxdiablo

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

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

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

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

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

19
Jonathan Leffler

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

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

10
Jay

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

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 

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

2
Kent Fredric

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

1
Ken

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

1
Paul Tomblin