Базовый ответ
Рассмотрим следующую команду, которая находит базовый каталог, в котором был установлен 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
- backslash-backtick - специальное правило
- backslash-backslash - специальное правило
- backslash-backtick - специальное правило
- backslash-обратная косая черта - специальное правило
- обратная косая черта - обратная черта - специальное правило
- обратная косая черта - обратная черта - специальное правило
Таким образом, обратная черта без экранирования в конце отмечает конецвнешняя команда 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
(которые всегда усложняют ситуацию).