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

Есть два способа записать вывод командной строки в bash:

  1. Обратные кавычки устаревшей оболочки Bourne ``:

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

    var=$(command)
    

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


person DVK    schedule 26.02.2012    source источник
comment
$() является POSIX и поддерживается всеми современными оболочками Борна, например ksh, bash, ash, dash, zsh, busybox, что угодно. (Не такой современный - Solaris /bin/sh, но в Solaris вы должны использовать вместо него современный /usr/xpg4/bin/sh).   -  person Jens    schedule 17.05.2012
comment
Также обратите внимание на использование $() и обратных кавычек в псевдонимах. Если у вас есть alias foo=$(command) в вашем .bashrc, то command будет выполняться при запуске самой команды псевдонима во время интерпретации .bashrc. С alias foo=`command`, command будет выполняться каждый раз при запуске псевдонима. Но если вы экранируете $ с формой $() (например, alias foo=\$(command)), он тоже будет выполняться каждый раз при запуске псевдонима, а не во время интерпретации .bashrc. Во всяком случае, насколько я могу судить по тестированию; Я не могу найти в документации bash ничего, что объясняло бы такое поведение.   -  person dirtside    schedule 27.02.2014
comment
@dirtside Что это за оболочка, я тестировал оболочку bash и POSIX, обратная кавычка выполняется, когда я использую исходный код. Простой пример: alias curDate = `date`. После того, как я отправлю его и запустил curDate, я получаю сообщение, например, что он не может найти команду Mon (получено в понедельник).   -  person thecarpy    schedule 02.09.2017
comment
@dirtside Это неправда. Даже с псевдонимом foo = `command` command выполняется только один раз. Я проверил: function aaa () {printf date; echo aaa ›› ~ / test.txt; } псевдоним test1 = aaa. Функция aaa выполняется только один раз (после каждого входа в систему) независимо от того, сколько раз выполнялся псевдоним (test1). Я использовал .bashrc (в Debian 10).   -  person vitaliydev    schedule 18.09.2019
comment
Понятия не имею, это была та версия bash, которую я использовал пять с половиной лет назад, которая, вероятно, была довольно старой даже к тому моменту. Возможно, мое тестирование было некорректным, но сейчас это не имеет значения, потому что я сомневаюсь, что кто-то до сих пор использует ту версию, которая была раньше.   -  person dirtside    schedule 19.09.2019
comment
Отвечает ли это на ваш вопрос? В чем разница между $ (command) и `command` в программировании оболочки?   -  person John Kugelman    schedule 26.01.2020


Ответы (9)


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

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

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

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

Другой пример - это что-то вроде получения имени (а не полного пути) родительского каталога:

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

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

person paxdiablo    schedule 26.02.2012
comment
Я ожидаю, что весь код SO будет готовыми к производству фрагментами, разработанными в соответствии со стандартами надежности кода шаттла NASA. Все, что меньше, получает отметку и голосование за удаление. - person DVK; 26.02.2012
comment
@DVK Если вы не шутите, я не согласен с тем, что дополнения кода должны быть помечены за невозможность автоматического перелицензирования от значений по умолчанию SO (CC-BY-SA или лицензии MIT), чтобы обеспечить такие гарантии или соответствие назначению. Вместо этого я бы повторно использовал код SO на свой страх и риск и голосовал бы за вклады в соответствии с полезностью, техническими достоинствами и т. Д. - person chrstphrchvz; 11.02.2017
comment
@chrstphrchvz, если вы посмотрите мой профиль, вы увидите этот небольшой фрагмент: Весь код, который я публикую на Stack Overflow, покрывается лицензией Do what, heck you want with it, полный текст которой: Do whatever the heck you want with it. :-) В любом случае, я почти уверен, что это был юмор от ДВК. - person paxdiablo; 11.02.2017
comment
а что если это был cd / home / pax / xyzzy / plover? Вы бы оказались в лабиринте извилистых маленьких коридоров, совершенно разных? - person Duncan; 07.06.2017
comment
@wchargin знаете ли вы, что этот инцидент был основным мотиватором для добавления используемых определенных литералов в язык C ++? en.cppreference.com/w/cpp/language/user_literal - person ThomasMcLeod; 25.03.2019

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

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

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

Первое проще, чем второе - воспользуйтесь первым.

person Jonathan Leffler    schedule 26.02.2012
comment
Было бы хорошо увидеть цитаты вокруг этих подстановок команд! - person Tom Fenech; 31.07.2017
comment
По крайней мере, для bash комментарий @TomFenech не относится к заданиям. x=$(f); x=`f` ведут себя так же, как x="$(f)"; x="`f`". Напротив, присвоение массива x=($(f)); x=(`f`) действительно выполняет разделение на $IFS символов, как ожидалось, при вызове команд. Это удобно (x=1 2 3 4 не имеет смысла), но непоследовательно. - person kdb; 05.09.2019
comment
@kdb ты прав насчет x=$(f) работы без кавычек. Я должен был быть более конкретным; Я предлагал использовать libdir=$(dirname "$(dirname "$(which gcc)")")/lib (кавычки вокруг внутренних подстановок команд). Если оставить кавычки, вы все равно будете подвержены обычному разделению слов и расширению глобуса. - person Tom Fenech; 07.09.2019

Обратные кавычки (`...`) - это устаревший синтаксис, необходимый только для самых старых из несовместимых с POSIX оболочек Bourne, а $(...) - это 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`
    

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

    который не является переносимым, поскольку оболочки Bourne и Korn требуют этих обратных косых черт, а 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' outputs \$x
    • echo `echo '\$x'` выходов $x
    • echo $(echo '\$x') выходов \$x
  • Синтаксис обратных кавычек имеет исторические ограничения на содержимое встроенной команды и не может обрабатывать некоторые допустимые сценарии, содержащие обратные кавычки, в то время как более новая форма $() может обрабатывать любой допустимый встроенный сценарий.

    Например, эти действительные встроенные скрипты не работают в левом столбце, но работают в правом 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 ')'
    `                              )
    

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

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

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

person kenorb    schedule 23.10.2015
comment
Вложенные кавычки в подстановке в стиле грависа с акцентом на самом деле не определены, вы можете использовать двойные кавычки либо вне , либо внутри , но не обоих, переносимо. Снаряды интерпретируют их по-разному; некоторым требуется обратная косая черта, чтобы избежать их, некоторые требуют, чтобы они не экранировались обратной косой чертой. - person mirabilos; 27.08.2017

Из man bash:

       $(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.
person dgw    schedule 26.02.2012

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

$(...)

выделяется визуально лучше, чем

`...`

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

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

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

$() разрешает вложение.

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

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

person Shiplu Mokaddim    schedule 26.02.2012
comment
Вы можете вкладывать обратные кавычки; это намного сложнее: out=`echo today is \`date\`` . - person Jonathan Leffler; 06.07.2015

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

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

$(command)

или (версия с обратной кавычкой):

`command`

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

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

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

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

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

\`command\`

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

$( (command) )

person JRFerguson    schedule 26.02.2012

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

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

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

person Piotr Zierhoffer    schedule 04.12.2015

Здесь в 2021 году стоит упомянуть любопытный факт в качестве дополнения к другим ответам.

Сценарий Microsoft DevOps YAML для конвейеров может включать задачи bash. Однако обозначение $() используется для ссылки на переменные, определенные в контексте YAML, поэтому в этом случае следует использовать обратные кавычки для захвата вывода команд.

В основном это проблема при копировании кода сценария в сценарий YAML, поскольку препроцессор DevOps очень снисходительно относится к несуществующим переменным, поэтому сообщений об ошибках не будет.

person nielsen    schedule 11.03.2021