¿Cuál es el beneficio de usar $ () en lugar de backticks en scripts de shell?

Hay dos formas de capturar el resultado de la línea de comando en bash :

  1. Legacy Bourne shell backticks `` :

      var=`command` 
  2. $() syntax (que hasta donde sé es específico de Bash)

      var=$(command) 

¿Hay algún beneficio en usar la segunda syntax en comparación con los backticks? ¿O son los dos completamente 100% equivalentes?

El principal es la capacidad de anidarlos , comandos dentro de los comandos, sin perder la cordura tratando de descubrir si alguna forma de escape funcionará en los backticks.

Un ejemplo, aunque algo artificial:

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

que le proporcionará una lista de todos los archivos en el árbol de directorios /dir que tienen el mismo nombre que el archivo de texto con fecha más antigua de diciembre de 2011 (a) .

Otro ejemplo sería algo así como obtener el nombre (no la ruta completa) del directorio principal:

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

(a) Ahora que ese comando específico puede no funcionar, no he probado la funcionalidad. Entonces, si me votas por ello, has perdido de vista la intención 🙂 Se trata solo de ilustrar cómo puedes anidar, no como un fragmento listo para producción sin errores.

Supongamos que desea encontrar el directorio lib correspondiente a donde está instalado gcc . Tienes una opción:

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

El primero es más fácil que el segundo: use el primero.

De hombre 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. 

Los backticks ( `...` ) son la syntax heredada requerida solo por los más antiguos bourne-shells no compatibles con POSIX y $(...) es POSIX y más preferido por varias razones:

  • Las barras diagonales inversas ( \ ) dentro de las barras traseras se manejan de una manera no obvia:

     $ 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 \\ 
  • Anidado citando dentro de $() es mucho más conveniente:

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

    en lugar de:

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

    o escribiendo algo como:

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

    porque $() usa un contexto completamente nuevo para citar

    que no es portátil, ya que los caparazones de Bourne y Korn requerirían estas barras invertidas, mientras que Bash y Dash no lo hacen.

  • La syntax para las sustituciones de comandos de anidamiento es más fácil:

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

    que:

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

    porque $() impone un contexto completamente nuevo para la cotización, por lo que cada sustitución de comando está protegida y puede tratarse por sí misma sin una preocupación especial por citar y escapar. Cuando se usan los backticks, se vuelve más feo y feo después de los niveles dos y superiores.

    Pocos ejemplos más:

     echo `echo `ls`` # INCORRECT echo `echo \`ls\`` # CORRECT echo $(echo $(ls)) # CORRECT 
  • Resuelve un problema de comportamiento incoherente al usar las comillas inversas:

    • echo '\$x' outputs \$x
    • echo `echo '\$x'` salidas $x
    • echo $(echo '\$x') outputs \$x
  • La syntax de Backticks tiene restricciones históricas sobre el contenido del comando incrustado y no puede manejar algunos scripts válidos que incluyen comillas invertidas, mientras que el formulario más reciente $() puede procesar cualquier tipo de script incrustado válido.

    Por ejemplo, estos scripts incrustados válidos de otro modo no funcionan en la columna izquierda, pero funcionan en el IEEE correcto:

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

Por lo tanto, la syntax para la sustitución del comando prefijada $ debe ser el método preferido, porque es visualmente clara con syntax limpia (mejora la legibilidad de los dispositivos y las personas), es anidable e intuitiva, su análisis interno es independiente y también más uniforme ( con todas las demás expansiones que se analizan desde comillas dobles) donde los retrocesos son la única excepción y el carácter ` se camufla fácilmente cuando está adyacente a " lo que hace que sea aún más difícil de leer, especialmente con fonts pequeñas o inusuales.

Fuente: ¿Por qué se prefiere $(...) a `...` (palos)? en BashFAQ

Ver también:

  • Sección estándar de POSIX "2.6.3 Sustitución de comandos"
  • Razonamiento de POSIX para incluir la syntax de $ ()
  • Sustitución de comando
  • bash-hackers: sustitución de comando

Además de las otras respuestas,

 $(...) 

se destaca visualmente mejor que

 `...` 

Los Backticks se parecen mucho a los apóstrofes; esto varía según la fuente que estés usando.

(Y, como acabo de notar, los backticks son mucho más difíciles de ingresar en las muestras de código en línea).

$() permite anidar.

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

Creo que los backticks no lo permiten.

Es el estándar POSIX el que define la forma $(command) de sustitución de comando. La mayoría de las conchas que se usan en la actualidad cumplen con POSIX y admiten esta forma preferida sobre la notación de retroceso arcaico. La sección de sustitución de comandos (2.6.3) del documento de Shell Language describe esto:

La sustitución de comando permite sustituir la salida de un comando en lugar del nombre del comando en sí. La sustitución del comando se producirá cuando el comando esté incluido de la siguiente manera:

$( command )

o (versión de backquoted):

` command `

El shell expandirá la sustitución del comando ejecutando el comando en un entorno subshell (ver Entorno de Ejecución del Shell ) y reemplazando la sustitución del comando (el texto del comando más el adjunto “$ ()” o las comillas inversas) con la salida estándar del comando, eliminando secuencias de uno o más caracteres al final de la sustitución. Los caracteres incrustados antes del final de la salida no se eliminarán; sin embargo, pueden tratarse como delimitadores de campo y eliminarse durante la división de campo, según el valor de IFS y las citas que estén vigentes. Si el resultado contiene bytes nulos, el comportamiento no está especificado.

Dentro del estilo backquoted de sustitución de comando, conservará su significado literal, excepto cuando esté seguido de: ‘ $ ‘, ‘ ` ‘ o . La búsqueda de la comilla inversa correspondiente se cumplirá con la primera comilla no escapada sin comillas; durante esta búsqueda, si se encuentra una comilla falsa no escapada dentro de un comentario de shell, se produce un documento aquí, una sustitución de comando incrustado del formulario $ ( comando ) o una cadena entre comillas, resultados no definidos. Una secuencia de una sola cita o una comilla doble que comienza, pero no termina, dentro de la secuencia ” `...` ” produce resultados indefinidos.

Con el formulario $ ( comando ), todos los caracteres que siguen el paréntesis abierto al paréntesis de cierre coincidente constituyen el comando . Cualquier script de shell válido se puede utilizar para el comando , excepto un script que consiste únicamente en redirecciones que produce resultados no especificados.

Los resultados de la sustitución de comando no se procesarán para una mayor expansión de tilde, expansión de parámetros, sustitución de comando o expansión aritmética. Si se produce una sustitución de comando dentro de las comillas dobles, la división del campo y la expansión del nombre de ruta no se realizarán en los resultados de la sustitución.

La sustitución de comando se puede anidar. Para especificar el anidamiento dentro de la versión backquoted, la aplicación debe preceder a las comillas posteriores internas con los caracteres ; por ejemplo:

\` command \`

La syntax del lenguaje de comandos del shell tiene una ambigüedad para las expansiones que comienzan con ” $(( “, que puede introducir una expansión aritmética o una sustitución de comando que comienza con una subcadena. La expansión aritmética tiene precedencia, es decir, el shell determinará primero si puede analizar la expansión como una expansión aritmética y solo analizará la expansión como una sustitución de comando si determina que no puede analizar la expansión como una expansión aritmética. El shell no necesita evaluar expansiones anidadas al realizar esta determinación. de entrada sin haber determinado previamente que no puede analizar la expansión como una expansión aritmética, el shell tratará la expansión como una expansión aritmética incompleta e informará un error de syntax. Una aplicación conforme se asegurará de que separe el ” $( ” y ‘ ( ‘en dos tokens (es decir, separarlos con espacios en blanco) en una sustitución de comando que comienza con una subshell. Por ejemplo, un comm y la sustitución que contiene una sola subcapa puede escribirse como:

$( ( command ) )

Esta es una pregunta heredada, pero se me ocurrió un ejemplo perfectamente válido de $(...) sobre `...` .

Estaba usando un escritorio remoto para Windows ejecutando cygwin y quería iterar sobre el resultado de un comando. Lamentablemente, el carácter de retroceso fue imposible de ingresar, ya sea por el escritorio remoto o por el propio cygwin.

Es sensato suponer que un signo de dólar y paréntesis serán más fáciles de escribir en configuraciones tan extrañas.

    Intereting Posts