Qual é o benefício de usar $ () em vez de backticks em scripts de shell?

Existem duas maneiras de capturar a saída da linha de comando no bash :

  1. Backticks do shell Bourne legado `` :

      var=`command` 
  2. Sintaxe $() (que, até onde eu sei, é específica do Bash)

      var=$(command) 

Existe algum benefício em usar a segunda syntax em comparação com os backticks? Ou são os dois totalmente 100% equivalentes?

O principal deles é a capacidade de aninhar- los, comandos dentro de comandos, sem perder sua sanidade tentando descobrir se alguma forma de escaping funcionará nos backticks.

Um exemplo, embora um pouco inventado:

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

que lhe dará uma lista de todos os arquivos na tree de diretórios /dir que possuem o mesmo nome do arquivo de texto datado mais antigo de dezembro de 2011 (a) .

Outro exemplo seria algo como obter o nome (não o caminho completo) do diretório pai:

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

(a) Agora que um comando específico pode não funcionar, não testei a funcionalidade. Então, se você votar em mim, você perdeu de vista a intenção 🙂 Ele é apenas uma ilustração de como você pode aninhar, não como um snippet pronto para produção sem erros.

Suponha que você queira encontrar o diretório lib correspondente ao local onde o gcc está instalado. Você tem uma escolha:

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

O primeiro é mais fácil que o segundo – use o primeiro.

Do homem 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. 

Os backticks ( `...` ) é a syntax legada requerida apenas pelo mais antigo dos bourne-shells não compatíveis com POSIX e $(...) é POSIX e mais preferido por várias razões:

  • Barras invertidas ( \ ) dentro de backticks são tratadas de maneira não óbvia:

     $ 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 \\ 
  • Citações aninhadas dentro de $() são muito mais convenientes:

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

    ao invés de:

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

    ou escrevendo algo como:

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

    porque $() usa um contexto inteiramente novo para citar

    o que não é portátil, já que os shells de Bourne e Korn exigiriam essas barras invertidas, enquanto Bash e dash não.

  • Sintaxe para substituições de comandos de aninhamento é mais fácil:

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

    do que:

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

    porque $() impõe um contexto inteiramente novo para a cotação, de modo que cada substituição de comando é protegida e pode ser tratada por conta própria sem preocupação especial sobre cotação e escape. Ao usar backticks, fica mais feia e feia depois de dois níveis e acima.

    Mais alguns exemplos:

     echo `echo `ls`` # INCORRECT echo `echo \`ls\`` # CORRECT echo $(echo $(ls)) # CORRECT 
  • Ele resolve um problema de comportamento inconsistente ao usar backquotes:

    • echo '\$x' saídas \$x
    • echo `echo '\$x'` saídas $x
    • Saídas echo $(echo '\$x') \$x
  • A syntax de backticks possui restrições históricas no conteúdo do comando incorporado e não pode manipular alguns scripts válidos que incluem backquotes, enquanto o formulário $() mais recente pode processar qualquer tipo de script incorporado válido.

    Por exemplo, esses scripts integrados válidos de outra forma não funcionam na coluna da esquerda, mas funcionam no IEEE correto:

     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 ')' ` ) 

Portanto, a syntax para substituição de comando $ -prefixed deve ser o método preferido, porque é visualmente claro com syntax limpa (melhora a legibilidade humana e de máquina), é aninhada e intuitiva, sua análise interna é separada e também é mais consistente ( com todas as outras expansões que são analisadas a partir de dentro de aspas duplas) onde os backticks são a única exceção e ` caractere é facilmente camuflado quando adjacente a " tornando ainda mais difícil a leitura, especialmente com fonts pequenas ou incomuns.

Fonte: Por que $(...) preferido em relação `...` (backticks)? na BashFAQ

Veja também:

  • Seção padrão POSIX "2.6.3 Substituição de Comando"
  • Justificativa POSIX para include a syntax $ ()
  • Substituição de Comando
  • bash-hackers: substituição de comando

Além das outras respostas,

 $(...) 

se destaca visualmente melhor do que

 `...` 

Backticks parecem muito com apóstrofos; isso varia dependendo da fonte que você está usando.

(E, como acabei de notar, os backticks são muito mais difíceis de inserir em amostras de código embutidas.)

$() permite aninhamento.

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

Eu acho que backticks não permitem isso.

É o padrão POSIX que define a forma $(command) da substituição do comando. A maioria dos shells em uso atualmente são compatíveis com POSIX e suportam essa forma preferida em relação à notação arcaica de backtick. A seção de substituição de comandos (2.6.3) do documento da linguagem Shell descreve isso:

A substituição de comandos permite que a saída de um comando seja substituída no lugar do próprio nome do comando. A substituição de comando deve ocorrer quando o comando é colocado da seguinte forma:

$( command )

ou (versão backquoted):

` command `

O shell deve expandir a substituição do comando executando o comando em um ambiente de subshell (consulte Shell Execution Environment ) e substituindo a substituição de comando (o texto do comando mais o “$ ()” ou backquotes) pela saída padrão do comando, removendo seqüências de um ou mais caracteres no final da substituição. Caracteres incorporados antes do final da saída não devem ser removidos; no entanto, eles podem ser tratados como delimitadores de campo e eliminados durante a divisão de campo, dependendo do valor do IFS e da cotação que está em vigor. Se a saída contiver quaisquer bytes nulos, o comportamento não será especificado.

Dentro do estilo de substituição de comando, o deve manter seu significado literal, exceto quando seguido por: ‘ $ ‘, ‘ ` ‘, ou . A pesquisa da cota de correspondência deve ser satisfeita pela primeira cota não escapada sem numeração; durante esta pesquisa, se um backquote não-escapado for encontrado em um comentário de shell, um documento aqui, uma substituição de comando integrada do formulário $ ( comando ) ou uma cadeia entre aspas, ocorrerão resultados indefinidos. Uma sequência de aspas simples ou aspas duplas que começa, mas não termina, dentro da sequência ” `...` ” produz resultados indefinidos.

Com o formulário $ ( comando ), todos os caracteres após o parêntese aberto para o parêntese de fechamento correspondente constituem o comando . Qualquer script de shell válido pode ser usado para o comando , exceto um script que consiste apenas em redirecionamentos que produz resultados não especificados.

Os resultados da substituição do comando não devem ser processados ​​para expansão adicional do til, expansão de parâmetro, substituição de comando ou expansão aritmética. Se uma substituição de comando ocorrer dentro de aspas duplas, a divisão de campo e a expansão do nome do caminho não serão executadas nos resultados da substituição.

A substituição de comandos pode ser aninhada. Para especificar o aninhamento na versão backquoted, o aplicativo deve preceder os backquotes internos com caracteres ; por exemplo:

\` command \`

A syntax da linguagem de comando shell tem uma ambigüidade para expansões que começam com ” $(( “, que pode introduzir uma expansão aritmética ou uma substituição de comando que começa com uma subshell. A expansão aritmética tem precedência; isto é, a shell deve primeiro determinar se ele pode analisar a expansão como uma expansão aritmética e só analisará a expansão como uma substituição de comando se determinar que não é possível analisar a expansão como uma expansão aritmética.O shell não precisa avaliar as expansões aninhadas ao executar essa determinação. de input sem já ter determinado que não é possível analisar a expansão como uma expansão aritmética, o shell deve tratar a expansão como uma expansão aritmética incompleta e relatar um erro de syntax.Uma aplicação em conformidade deve garantir que separa o ” $( ” e ” ( “em dois tokens (ou seja, separe-os com espaço em branco) em uma substituição de comando que começa com uma subshell. Por exemplo, um comm e substituição contendo uma única subcamada poderia ser escrita como:

$( ( command ) )

Esta é uma questão legada, mas eu criei um exemplo perfeitamente válido de $(...) sobre `...` .

Eu estava usando um desktop remoto para o Windows executando o cygwin e queria iterar sobre o resultado de um comando. Infelizmente, o personagem de backtick era impossível de entrar, seja devido à coisa de área de trabalho remota ou ao próprio cygwin.

É sensato assumir que um sinal de dólar e parênteses serão mais fáceis de digitar em configurações tão estranhas.