В чем преимущество использования $ () вместо обратных кавычек в сценариях оболочки?

175

Есть два способа получить выходные данные командной строки bash:

  1. Задняя часть корпуса Bourne ``:

    var=`command`
  2. $() синтаксис (который, насколько я знаю, специфичен для Bash, или, по крайней мере, не поддерживается старыми оболочками, отличными от POSIX, такими как оригинальный Bourne)

    var=$(command)

Есть ли польза от использования второго синтаксиса по сравнению с обратными чертами? Или два полностью 100% эквивалентны?

DVK
источник
15
$()это POSIX и поддерживается всеми современными оболочками Bourne, например, ksh, bash, ash, dash, zsh, busybox, как вы его называете. (Не очень современный - Solaris /bin/sh, но /usr/xpg4/bin/shвместо него вы должны использовать современный ).
Дженс
27
Также заметка об использовании $()и обратных кавычек в псевдонимах. Если у вас есть alias foo=$(command)в вашем .bashrcтогда commandбудет выполняться , когда команда псевдоним сам запускается во время .bashrcинтерпретации. С alias foo=`command`, commandбудет выполняться каждый раз при запуске псевдонима. Но если вы избежать $с $()формой (например alias foo=\$(command)), он тоже будет выполняться каждый раз , когда псевдоним запуска, а не во время .bashrcинтерпретации. Во всяком случае, насколько я могу судить по тестированию; Я не могу найти ничего в документах Bash, которые объясняют это поведение.
dirtside
4
@dirtside Что это за оболочка, я тестировал оболочку bash и POSIX, обратный удар выполняется при исходном коде. Простой пример: псевдоним curDate = `date` После того, как я поставил его и запустил curDate, я получил сообщение, что он не может найти команду Mon (Sourced in Monday), например.
thecarpy
@dirtside Это не правда. Даже с псевдонимом foo = `command` commandвыполняется только один раз. Я проверил это: function aaa () {printf date; echo aaa >> ~ / test.txt; } alias test1 = aaa. Функция aaa выполняется только один раз (после каждого входа в систему) независимо от того, сколько раз test1был выполнен alias ( ). Я использовал .bashrc (на Debian 10).
Виталиев
Понятия не имею, это была та версия bash, которую я использовал пять с половиной лет назад, которая, вероятно, была довольно старой даже к этому моменту. Возможно, мое тестирование было неверным, но это вряд ли имеет значение сейчас, потому что я сомневаюсь, что кто-то все еще использует ту версию, которая была.
конец

Ответы:

156

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

Пример, хотя и несколько надуманный:

deps=$(find /dir -name $(ls -1tr 201112[0-9][0-9]*.txt | tail -1l) -print)

который предоставит вам список всех файлов в /dirдереве каталогов, которые имеют то же имя, что и самый ранний текстовый файл с датой от декабря 2011 года (а) .

Другим примером может быть получение имени (а не полного пути) родительского каталога:

pax> cd /home/pax/xyzzy/plugh
pax> parent=$(basename $(dirname $PWD))
pax> echo $parent
xyzzy

(а) Теперь, когда конкретная команда может не работать, я не проверял функциональность. Так что, если вы проголосуете за меня, вы упустили из виду намерение :-) Это всего лишь иллюстрация для иллюстрации того, как вы можете вкладывать, а не как готовый к использованию фрагмент без ошибок.

paxdiablo
источник
87
Я ожидаю, что весь код на SO будет готовым к использованию фрагментом кода, разработанным в соответствии со стандартами надежности НАСА. Все, что меньше, получает флаг и голосование за удаление.
ДВК
1
@DVK В случае, если вы не шутите, я не согласен с тем, что вклады кода должны быть помечены для неспособности автоматически перенастраиваться от значений по умолчанию SO (лицензии CC-BY-SA или MIT), чтобы обеспечить такие гарантии или соответствие назначению. Вместо этого я бы использовал код на SO на свой страх и риск, и проголосовал бы в соответствии с полезностью, техническими достоинствами и т. Д.
chrstphrchvz
9
@chrstphrchvz, если вы посмотрите на мой профиль, то увидите небольшой фрагмент: «Весь код, который я публикую в Stack Overflow, распространяется по лицензии« Делай, что хочешь, с ним », полный текст которой: Do whatever the heck you want with it.» :-) В любом случае, я уверен, что это был юмор от ДВК.
paxdiablo
1
но что делать, если это был "cd / home / pax / xyzzy / plover"? Вы оказались бы в лабиринте извилистых маленьких проходов, все разные?
Дункан
@wchargin Знаете ли вы, что этот инцидент был основным мотиватором для добавления используемых определенных литералов к языку C ++? ru.cppreference.com/w/cpp/language/user_literal
ThomasMcLeod
59

Предположим, вы хотите найти каталог lib, соответствующий месту gccустановки. У вас есть выбор:

libdir=$(dirname $(dirname $(which gcc)))/lib

libdir=`dirname \`dirname \\\`which gcc\\\`\``/lib

Первый легче второго - используйте первый.

Джонатан Леффлер
источник
4
Было бы хорошо увидеть некоторые цитаты вокруг этих командных замен!
Том Фенек
1
По крайней мере, для bash комментарий @TomFenech не относится к назначениям. x=$(f); x=`f` вести себя так же, как x="$(f)"; x="`f`". В отличие от этого , присваивание массива x=($(f)); x=(`f`) делают производить разбиение на $IFSсимволы , как ожидается , при вызове команды. Это удобно ( x=1 2 3 4не имеет смысла), но противоречиво.
КДБ
@kdb ты прав насчет x=$(f)работы без кавычек. Я должен был быть более конкретным; Я предлагал использовать libdir=$(dirname "$(dirname "$(which gcc)")")/lib(кавычки вокруг внутренних подстановок команд). Если оставить его без кавычек, вы все равно будете подвергаться обычному расщеплению слов и расширению глобуса.
Том Фенек
38

Backticks ( `...`) - это унаследованный синтаксис, который требуется только самым старым из не POSIX-совместимых bourne-shell. Он $(...)является POSIX и более предпочтителен по нескольким причинам:

  • Обратные косые черты ( \) внутри обратных кавычек обрабатываются неочевидным способом:

    $ echo "`echo \\a`" "$(echo \\a)"
    a \a
    $ echo "`echo \\\\a`" "$(echo \\\\a)"
    \a \\a
    # Note that this is true for *single quotes* too!
    $ foo=`echo '\\'`; bar=$(echo '\\'); echo "foo is $foo, bar is $bar" 
    foo is \, bar is \\
  • Вложенное цитирование внутри $()гораздо удобнее:

    echo "x is $(sed ... <<<"$y")"

    вместо того:

    echo "x is `sed ... <<<\"$y\"`"

    или писать что-то вроде:

    IPs_inna_string=`awk "/\`cat /etc/myname\`/"'{print $1}' /etc/hosts`

    потому что $()использует совершенно новый контекст для цитирования

    который не является переносимым, так как оболочкам Борна и Корна потребуются эти обратные слеши, в то время как Bash и dash не требуют.

  • Синтаксис для вложения подстановок команд проще:

    x=$(grep "$(dirname "$path")" file)

    чем:

    x=`grep "\`dirname \"$path\"\`" file`

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

    Еще несколько примеров:

    echo `echo `ls``      # INCORRECT
    echo `echo \`ls\``    # CORRECT
    echo $(echo $(ls))    # CORRECT
  • Это решает проблему противоречивого поведения при использовании обратных кавычек:

    • echo '\$x' выходы \$x
    • echo `echo '\$x'` выходы $x
    • echo $(echo '\$x') выходы \$x
  • Синтаксис Backticks имеет исторические ограничения на содержание встроенной команды и не может обрабатывать некоторые допустимые сценарии, которые включают в себя обратные кавычки, в то время как более новая $()форма может обрабатывать любые допустимые встроенные сценарии.

    Например, эти действительные встроенные сценарии не работают в левом столбце, но работают в правом IEEE :

    echo `                         echo $(
    cat <<\eof                     cat <<\eof
    a here-doc with `              a here-doc with )
    eof                            eof
    `                              )
    
    
    echo `                         echo $(
    echo abc # a comment with `    echo abc # a comment with )
    `                              )
    
    
    echo `                         echo $(
    echo '`'                       echo ')'
    `                              )

Поэтому синтаксис для $подстановки команд с префиксом должен быть предпочтительным методом, потому что он визуально понятен с чистым синтаксисом (улучшает удобочитаемость для человека и машины), он является вложенным и интуитивно понятным, его внутренний анализ является отдельным, и он также более согласован (с все другие расширения, которые анализируются из двойных кавычек), где обратные пометки являются единственным исключением, а `символ легко маскируется, когда его рядом, что "делает его еще более трудным для чтения, особенно с маленькими или необычными шрифтами.

Источник: Почему $(...)предпочтительнее `...`(backticks)? на BASFAQ

Смотрите также:

kenorb
источник
1
Вложенные кавычки в подстановке в стиле акцент-гравис на самом деле не определены, вы можете использовать двойные кавычки как снаружи, так и внутри, но не оба одновременно, переносимо. Снаряды интерпретируют их по-разному; некоторые требуют обратной косой черты, чтобы избежать их, некоторые требуют, чтобы они не избежали обратной косой черты.
Мирабилось
23

От человека Баш:

          $(command)
   or
          `command`

   Bash performs the expansion by executing command and replacing the com-
   mand  substitution  with  the  standard output of the command, with any
   trailing newlines deleted.  Embedded newlines are not deleted, but they
   may  be  removed during word splitting.  The command substitution $(cat
   file) can be replaced by the equivalent but faster $(< file).

   When the old-style backquote form of substitution  is  used,  backslash
   retains  its  literal  meaning except when followed by $, `, or \.  The
   first backquote not preceded by a backslash terminates the command sub-
   stitution.   When using the $(command) form, all characters between the
   parentheses make up the command; none are treated specially.
DGW
источник
9

В дополнение к другим ответам,

$(...)

визуально лучше, чем

`...`

Обратные пальцы слишком похожи на апострофы; это зависит от используемого вами шрифта.

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

Кит Томпсон
источник
2
у вас должна быть странная клавиатура (или у меня?). Для меня намного проще набирать обратные метки - это клавиша в верхнем левом углу, не требуется SHIFT или ALT.
ДВК
1
@DVK: я говорил об их внешности, а не о простоте набора текста. (Моя клавиатура, вероятно, такая же, как ваша.) Тем не менее, теперь, когда вы упомянули об этом, я думаю, что у меня лучше мышечная память $ (и )чем у меня спина; YMMV.
Кит Томпсон
никогда не программируется в bash (пропущено из старого ksh в Perl), поэтому определенно нет памяти для этого конкретного синтаксиса :)
DVK
1
@DVK, я думал, что Кит имел в виду тот факт, что неблокированный код здесь (блочный код означает использование четырех пробелов в начале строки) использует обратные черты для обозначения этого, затрудняя вставку обратных черт в них, еще одна иллюстрация трудности вложения :-) FWIW, вы можете найти код и / code тэги (другой способ сделать неблокированный код) могут легче содержать обратные пометки.
paxdiablo
@Pax - понял. Duh! Я действительно мысленно застрял на блок-кодах по какой-то причине.
ДВК
7

$() позволяет вложение.

out=$(echo today is $(date))

Я думаю, что backticks не позволяет это.

Шиплу Мокаддим
источник
2
Вы можете вкладывать галочки; это намного сложнее out=`echo today is \`date\`` .
Джонатан Леффлер
4

Это стандарт POSIX, который определяет $(command)форму подстановки команд. Большинство оболочек, используемых сегодня, являются POSIX-совместимыми и поддерживают эту предпочтительную форму по сравнению с архаичной нотацией обратного удара. Раздел подстановки команд (2.6.3) документа Shell Language описывает это:

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

$(command)

или (версия в кавычках):

`command`

Оболочка должна расширить подстановку команд, выполнив команду в среде подоболочек (см. Среда выполнения оболочки ) и заменив подстановку команд (текст команды плюс заключенные в нее "$ ()" или обратные кавычки) стандартным выводом команды, удалив последовательности одного или нескольких <newline>символов в конце подстановки. Встроенные <newline>символы до конца вывода не должны быть удалены; однако они могут рассматриваться как разделители полей и исключаться при разделении полей, в зависимости от значения IFS и действующего цитирования. Если выходные данные содержат нулевые байты, поведение не определено.

В стиле замещения команды, заключенном в кавычки, <backslash>должно сохранять свое буквальное значение, за исключением случаев, когда следуют: ' $', ' `' или <backslash>. Поиск соответствующей обратной цитаты должен удовлетворяться первой нецитированной обратной цитатой; во время этого поиска, если в комментарии к оболочке встречается неэкранированная обратная кавычка, документ здесь, встроенная подстановка команд формы $ ( command ) или строка в кавычках, возникают неопределенные результаты. Строка в одинарных или двойных кавычках, которая начинается, но не заканчивается в последовательности " `...`", приводит к неопределенным результатам.

В форме $ ( команда ) все символы, следующие за открывающей скобкой и совпадающей с закрывающей скобкой, составляют команду . Любой допустимый сценарий оболочки может быть использован для команды , кроме сценария, состоящего исключительно из перенаправлений, который приводит к неопределенным результатам.

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

Подстановка команд может быть вложенной. Чтобы указать вложение в версии с обратными кавычками, приложение должно предшествовать внутренним обратным кавычкам с <backslash>символами; например:

\`command\`

Синтаксис языка команд оболочки имеет неоднозначность для расширений, начинающихся с "$((", который может ввести арифметическое расширение или подстановку команды, которая начинается с подоболочки. Арифметическое расширение имеет приоритет; то есть оболочка должна сначала определить, может ли она анализировать расширение как арифметическое расширение, и должна только анализировать расширение как команду подстановка, если она определяет, что она не может проанализировать расширение как арифметическое расширение. Оболочке не нужно оценивать вложенные расширения при выполнении этого определения. Если она встречает конец ввода, еще не определив, что она не может проанализировать расширение как арифметическое расширение, shell должен рассматривать расширение как неполное арифметическое расширение и сообщать о синтаксической ошибке. Соответствующее приложение должно гарантировать, что оно разделяет " $(" и "('в два токена (то есть разделить их пробелами) в подстановке команд, которая начинается с подоболочки. Например, подстановка команд, содержащая одну подоболочку, может быть записана как:

$( (command) )

JRFerguson
источник
4

Это унаследованный вопрос, но я придумал вполне обоснованный пример $(...)завершения `...`.

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

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

Петр Цирхоффер
источник