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

я часто вижу это в скриптах сборки проектов, которые используют autotools (autoconf, automake). когда кто-то хочет проверить значение переменной shell, он часто использует эту идиому:if test "x$shell\_var" = "xyes"; then ... </code></pre><p>что является преимуществом перед простой проверкой значения следующим образом:</p><pre><code>if test $shell_var = "yes"; then ... я полагаю, что должна быть какая-то причина, по которой я вижу это так часто, но я не могу понять, что это такое.

если вы используете оболочку, которая выполняет простую замену, а переменная 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 </code></pre><p>он может <em>кажется</em> немного избыточен, так как содержит кавычки и символ "x", но он также будет обрабатывать переменную с пробелами в ней, не давая это в качестве <em>двух</em> аргументов команда <code>test</code>.</p><p>другая причина (кроме пустых переменных) связана с обработкой опций. если вы напишите:</p><pre><code>if test "$1" = "abc" ; then ... и $1</code> имеет значение <code>-n</code> или <code>-z</code> или любые другие допустимые параметры команды <code>test</code>, синтаксис неоднозначен. <code>x</code> на передней панели предотвращает выбор передней черты в качестве опции <code>test</code>.</p><p>имейте в виду, что это зависит от shell. некоторые оболочки (я думаю, <code>csh</code> для одного) будут горько жаловаться, если переменная окружения не существует, а просто возвращает пустую строку).</p>

<p>другая причина, о которой еще никто не упомянул, связана с обработкой опций. если вы напишите:</p><pre><code>if [ "$1" = "abc" ]; then ... и $ 1 имеет значение '-n', синтаксис команды test неоднозначен; непонятно, что вы тестировали. буква "х" на передней панели предотвращает возникновение проблем с ведущей чертой.</p><p>вы должны взглянуть на действительно древние оболочки, чтобы найти такую, в которой тестовая команда не поддерживает <code>-n</code> или <code>-z</code>; команда <code>test</code> версии 7 (1978) включала их. это не совсем неважно - некоторые версии unix версии 6 сбежали в bsd, но в наши дни вам будет крайне сложно найти что-то древнее в текущем использовании.</p><p>не использовать двойные кавычки вокруг значений опасно, как отметили многие другие люди. действительно, если существует вероятность того, что имена файлов могут содержать пробелы (macos x и windows оба поддерживают это в некоторой степени, и unix всегда поддерживал это, хотя такие инструменты, как <code>xargs</code>, усложняют его), тогда вы должны заключать имена файлов в двойные кавычки каждый раз вы их тоже используете. если вы не отвечаете за это значение (например, во время обработки опций, и вы устанавливаете переменную на "нет" при запуске и "да", когда флаг включен в командную строку), тогда небезопасно использовать формы переменных без кавычек пока вы не доказали их безопасность - и вы можете делать это все время для многих целей. или документируйте, что ваши сценарии ужасно потерпят неудачу, если пользователи попытаются обработать файлы с пробелами в именах. (есть и другие персонажи, о которых нужно беспокоиться - к примеру, обратные трюки тоже могут быть довольно неприятными.)</p>

<p>есть две причины, которые я знаю для этого соглашения:</p><p> <a href="http://tldp.org/ldp/abs/html/comparison-ops.html">http://tldp.org/ldp/abs/html/comparison-ops.html</a> </p><blockquote> <p>в составном тесте даже цитирование строковой переменной может быть недостаточным. [-n "$ string" -o "$ a" = "$ b"] может вызвать ошибку в некоторых версиях bash, если $ string пусто. безопасным способом является добавление дополнительного символа к возможным пустым переменным, ["x $ string"! = x -o "x $ a" = "x $ b"] ("x's" отменяется). </blockquote>во-вторых, в других оболочках, кроме bash, особенно в старых, условий тестирования, таких как '-z', для проверки пустой переменной не существовало, поэтому пока:if [ -z "$some\_var" ]; then echo "this variable is not defined" fi </code></pre><p>будет отлично работать в bash, если вы стремитесь к переносимости между различными средами unix, где вы не можете быть уверены, что оболочкой по умолчанию будет bash и поддерживает ли она условие тестирования -z, безопаснее использовать форму, если [" x $ some_var "=" x "], поскольку это всегда будет иметь ожидаемый эффект. по сути, это старый прием сценариев shell для поиска пустой переменной, и он все еще используется сегодня для обратной совместимости, несмотря на то, что существуют более чистые методы.

я рекомендую вместо этого:if test "yes" = "$shell\_var"; then </code></pre><p>поскольку он устраняет уродливое <code>x</code> и по-прежнему решает проблему, указанную в <a href="https://stackoverflow.com/a/174288/895245">https://stackoverflow.com/a/174288/895245</a> то, что <code>$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 </code></pre><p>а также</p><pre><code>shellvar=" hello" if test $shellvar = "hello" ; then echo "yep" ; fi # yep тем не менее, это обычно должно работатьshellvar=" hello" if test "$shellvar" = "hello" ; then echo "yep" ; fi #<no output> </code></pre><p>но когда он жалуется на вывод где-то еще, трудно сказать, на что он жалуется, так что</p><pre><code>shellvar=" hello" if test "x$shellvar" = "xhello" ; then echo "yep" ; fi работает так же хорошо, но было бы легче отлаживать.

я делал это в dos, когда shell_var могла быть неопределенной.

если вы не делаете "x $ shell\_var", то, если $ shell\_var не определен, вы получите ошибку о том, что "=" не является монадическим оператором или чем-то в этом роде.</p>