Почему \ $ сводится к $ внутри обратных кавычек [хотя не внутри $ (...)]? - PullRequest
21 голосов
/ 11 сентября 2011

Переходя к стандарту POSIX, я натолкнулся на еще один довольно технический / бессмысленный вопрос. Это состояния :

В стиле подстановки команд, заключенном в кавычки, <backslash> должен сохранять свое буквальное значение, за исключением случаев, когда следуют: $, ` или <backslash>.

Легко понять, почему '`' и '\' теряют свои буквальные значения: подстановка вложенных команд требует «другой» обратной цитаты внутри подстановки команд, что, в свою очередь, вынуждает «\» терять свое буквальное значение , Так, например, разумно выглядит следующее поведение:

$ echo $(echo \\\\)
\\
$ echo `echo \\\\`
\

А как насчет $? Т.е. в чем смысл или, более конкретно, возможная выгода от следующей разницы?

$ echo $(echo \$\$)
$$
$ echo `echo \$\$`
4735

Поскольку внутри обратных кавычек само по себе не исключено $, похоже, что вы все время будете использовать либо $, либо \\\ $, но никогда не используйте средний регистр \ $.

Подведем итог,

$ echo `echo $$` # PID, OK
4735
$ echo `echo \\\$\\\$` # literal "$$", OK
$$
$ echo `echo \$\$` # What's the point?
4735

PS: я знаю, что этот вопрос довольно технический ... Я сам все время иду на более современную замену $(...), но мне все еще интересно.

Ответы [ 4 ]

2 голосов
/ 19 сентября 2011

Базовый ответ

Рассмотрим следующую команду, которая находит базовый каталог, в котором был установлен gcc:

gcc_base=$(dirname $(dirname $(which gcc)))

С нотацией $(...) проблем ссинтаксический анализ;это тривиально и является одной из основных причин, почему рекомендуется использовать обозначения.Эквивалентная команда с использованием обратных тиков:

gcc_base=`dirname \`dirname \\\`which gcc\\\`\``

Когда оболочка впервые анализирует эту команду, она сталкивается с первым обратным ударом и должна найти соответствующий закрывающий обратный удар.Вот когда вступает в силу раздел, заключенный в кавычки:

В стиле замены команд, заключенном в кавычки, должен сохранять свое буквальное значение, за исключением случаев, когда следуют: '$', '`' или.

gcc_base=`dirname \`dirname \\\`which gcc\\\`\``
                  ^         ^ ^          ^ ^ ^
                  1         2 3          4 5 6
  1. backslash-backtick - специальное правило
  2. backslash-backslash - специальное правило
  3. backslash-backtick - специальное правило
  4. backslash-обратная косая черта - специальное правило
  5. обратная косая черта - обратная черта - специальное правило
  6. обратная косая черта - обратная черта - специальное правило

Таким образом, обратная черта без экранирования в конце отмечает конецвнешняя команда backtick.Под-оболочка, которая обрабатывает эту команду, видит:

dirname `dirname \`which gcc\``

Экранированию обратной косой черты снова предоставляется специальный режим, а под-оболочка видит:

dirname `which gcc`
  • Sub-sub-sub-shell получает значение which gcc и оценивает его (например, /usr/gcc/v4.6.1/bin/gcc).
  • Sub-sub-shell оценивает dirname /usr/gcc/v4.6.1/bin/gcc и выдает /usr/gcc/v4.6.1/bin.
  • Под-оболочка оценивает dirname /usr/gcc/v4.6.1/bin и выдает /usr/gcc/v4.6.1.
  • Оболочка назначает /usr/gcc/v4.6.1 на gcc_base.

В этом примере обратная косая чертаза ним следовали только специальные символы - обратная косая черта, обратная косая черта, доллар.Более сложный пример будет иметь, например, \" последовательности в команде, и тогда специальное правило не будет применяться;\" будет просто скопирован без изменений и передан в соответствующие вложенные оболочки.

Чрезвычайно сложные вещи

Например, предположим, что у вас есть команда с пробелом вимя (не дай бог; и это показывает, почему!), например totally amazing (два пробела; это более строгий тест, чем один пробел).Тогда вы могли бы написать:

$ cmd="totally  amazing"
$ echo "$cmd"
totally  amazing
$ which "$cmd"
/Users/jleffler/bin/totally  amazing
$ dirname $(which "$cmd")
usage: dirname path
$ # Oops!
$ dirname "$(which \"\$cmd\")"
"$cmd": not found
.
$ # Oops!
$ dirname "$(which \"$cmd\")"
"totally: not found
amazing": not found
.
$ dirname "$(eval which \"$cmd\")"
totally amazing: not found
.
$ dirname "$(eval which \"\$cmd\")"
/Users/jleffler/bin
$ # Ouch, but at least that worked!
$ # But how to extend that to the next level?
$ dirname "$(eval dirname \"\$\(eval which \\\"\\\$cmd\\\"\)\")"
/Users/jleffler
$

ОК - ну, это "легкий"!Вам нужна более веская причина, чтобы избежать пробелов в именах команд или путевых имен?К своему удовлетворению я также продемонстрировал, что он правильно работает с путевыми именами, которые содержат пробелы.

Итак, можем ли мы сжать цикл обучения для обратных галочек?Да ...

$ cat x3.sh
cmd="totally  amazing"
which "$cmd"
dirname "`which \"$cmd\"`"
dirname "`dirname \"\`which \\"\$cmd\\\"\`\"`"
$ sh -x x3.sh
+ cmd='totally  amazing'
+ which 'totally  amazing'
/Users/jleffler/bin/totally  amazing
++ which 'totally  amazing'
+ dirname '/Users/jleffler/bin/totally  amazing'
/Users/jleffler/bin
+++ which 'totally  amazing'
++ dirname '/Users/jleffler/bin/totally  amazing'
+ dirname /Users/jleffler/bin
/Users/jleffler
$

Это все еще ужасный, пугающий, неинтуитивный набор escape-последовательностей.На самом деле она короче версии для записи $(...) и не использует никаких команд eval (которые всегда усложняют ситуацию).

2 голосов
/ 16 сентября 2011

Добавляя \, вы заставляете внутреннюю подоболочку расширять ее вместо внешней оболочки.Хорошим примером может быть фактическое начало новой оболочки, например:

$ echo $$
4988
$ echo `sh -c 'echo $$'`
4988
$ echo `sh -c 'echo \$\$'`
4990
$ echo `sh -c 'echo \\\$\\\$'`
$$
1 голос
/ 10 ноября 2011

Это, вероятно, связано со странным способом, которым оболочка Борна анализирует замены (настоящая оболочка Корна немного похожа, но большинство других оболочек вообще не демонстрирует странного поведения).

По существу, оболочка БорнаПарсер не интерпретирует подстановки ($ и `) внутри двойных кавычек или подстановку параметров ($) в любом месте.Это делается только во время расширения.Кроме того, во многих случаях несопоставимые кавычки (одинарные, двойные или обратные кавычки) не являются ошибкой;заключительная кавычка предполагается в конце.

Одним из следствий этого является то, что если подстановка параметра словом, содержащим пробелы, например ${v+a b}, происходит за пределами двойных кавычек, она не анализируется правильно и вызовет ошибку раскрытия, когдаказнены.Пространство должно быть указано.Другие оболочки не имеют этой проблемы.

Еще одно следствие - двойные кавычки внутри обратных кавычек внутри двойных кавычек не работают надежно.Например,

v=0; echo "`v=1; echo " $v "`echo b"

будет печатать

 1 echo b

в большинстве оболочек (замена одной команды), но

 0 b

в оболочке Bourne и реальном Kornshell (ksh93) (две подстановки команд).

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

Настоящая оболочка Корна (ksh93) пытается сохранить большую часть странного поведения оболочки Борна, но выполняет синтаксический анализ подстановок во время анализа.Таким образом, ${v+a b} принимается, но приведенный выше пример имеет "странное" поведение.Еще одна странная вещь заключается в том, что принимается что-то вроде

echo "`${v+pwd"

(результат аналогичен отсутствующей закрывающей скобке).И откуда берется открывающая фигурная скобка в сообщении об ошибке от

echo "`${v+pwd`"

?

В приведенном ниже сеансе показан неясный случай, когда $ и \$ отличаются неочевиднымпуть:

$ echo ${.sh.version}
Version JM 93u 2011-02-08
$ v=0; echo "`v=1; echo "${v+p q}"`echo b" 
p qecho b
$ v=0; echo "`v=1; echo "\${v+p q}"`echo b" 
p{ q}b
0 голосов
/ 29 сентября 2011

По сути, обратная косая черта - это escape-символ. Вы помещаете это перед другим символом, чтобы представить что-то особенное. 'N', 't', '$' и '\' - это специальные символы.

"\n" --> newline
"\t" --> tab (indent)
"\$" --> $ (because a $ before a word in shell denotes a variable)
"\\" --> \ 

Обратная косая черта перед символами интерпретируется вышеуказанным способом только тогда, когда она находится внутри кавычек.

Если вы хотите найти больше информации или другие символы перехода, зайдите здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...