¿Cómo puedo asignar la salida de un comando a una variable de shell?


76

Quiero asignar el resultado de una expresión a una variable y concatenarlo con una cadena, luego repetirlo. Esto es lo que tengo:

#!/bin/bash
cd ~/Desktop;
thefile= ls -t -U | grep -m 1 "Screen Shot";
echo "Most recent screenshot is: "$thefile;

Pero eso produce:

Screen Shot 2011-07-03 at 1.55.43 PM.png
Most recent screenshot is: 

Entonces, parece que eso no se está asignando $thefiley se está imprimiendo mientras se ejecuta.


Respuestas:


108

Una asignación de shell es una sola palabra, sin espacio después del signo igual. Entonces, lo que escribió le asigna un valor vacío thefile; Además, dado que la asignación se agrupa con un comando, convierte thefileuna variable de entorno y la asignación es local para ese comando en particular, es decir, solo la llamada para lsver el valor asignado.

Desea capturar la salida de un comando, por lo que debe usar la sustitución de comandos :

thefile=$(ls -t -U | grep -m 1 "Screen Shot")

(Parte de la literatura muestra una sintaxis alternativa thefile=`ls …`; la sintaxis de las comillas inversas es equivalente a la sintaxis entre paréntesis en dólares, excepto que a veces es raro citar dentro de las comillas inversas, así que solo úselo $(…)).

Otras observaciones sobre su script:

  • Combinar -t(ordenar por tiempo) con -U(no ordenar) no tiene sentido; solo usa -t.
  • En lugar de usar greppara hacer coincidir las capturas de pantalla, es más claro pasar un comodín lsy usar headpara capturar el primer archivo:

    thefile=$(ls -t *"Screen Shot"* | head -n 1)
  • Generalmente es una mala idea analizar la salida dels . Esto podría fallar bastante si tiene nombres de archivo con caracteres no imprimibles. Sin embargo, ordenar archivos por fecha es difícil sin ellos ls, por lo que es una solución aceptable si sabe que no tendrá caracteres no imprimibles o barras invertidas en los nombres de archivo.

  • Siempre use comillas dobles alrededor de sustituciones variables , es decir, aquí escriba

    echo "Most recent screenshot is: $thefile"

    Sin comillas dobles, el valor de la variable se vuelve a ampliar, lo que causará problemas si contiene espacios en blanco u otros caracteres especiales.

  • No necesita punto y coma al final de una línea. Son redundantes pero inofensivos.
  • En un script de shell, a menudo es una buena idea incluirlo set -e. Esto le dice al shell que salga si falla algún comando (al devolver un estado distinto de cero).

Si tiene GNU find (en particular si está ejecutando Linux o Cygwin no incrustado), hay otro enfoque para encontrar el archivo más reciente: haga una findlista de los archivos y sus fechas, y use sorty tailpara extraer el archivo más joven.

thefile=$(find -maxdepth 1 -type f -name "*Screen Shot*" -printf "%T@ %p" |
          sort -k 1n | tail -n 1)

Si está dispuesto a escribir este script en zsh en lugar de bash, hay una manera mucho más fácil de capturar el archivo más nuevo, porque zsh tiene calificadores globales que permiten coincidencias con comodines no solo en los nombres sino también en los metadatos del archivo. La (om[1])parte que sigue al patrón son los calificadores glob; omclasifica las coincidencias al aumentar la edad (es decir, por el tiempo de modificación, la más nueva primero) y [1]extrae solo la primera coincidencia. Toda la coincidencia debe estar entre paréntesis porque técnicamente es una matriz, ya que el globbing devuelve una lista de archivos, incluso si [1]eso significa que en este caso particular la lista contiene (como máximo) un archivo.

#!/bin/zsh
set -e
cd ~/Desktop
thefile=(*"Screen Shot"*(om[1]))
echo "Most recent screenshot is: $thefile"

77
¡Guauu! Más información de la que posiblemente podría haber esperado. Muchas gracias por tu respuesta; ¡Realmente aprecio todo el esfuerzo que pusiste en esto! Me acabas de mostrar que realmente tengo mucho que aprender. Me voy a comprar un libro en bash: P.
Nathan G.

3
¡Eso es lo que yo llamaría la respuesta definitiva ! :)
alex

4

Si desea hacerlo con comandos / s multilínea / múltiples, puede hacerlo:

output=$( bash <<EOF
#multiline/multiple command/s
EOF
)

O:

output=$(
#multiline/multiple command/s
)

Ejemplo:

#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"

Salida:

first
second
third

Esta es una buena respuesta. ¿Hay alguna manera de que pueda especificar una variable como parte del comando? por ejemplooutput=$(echo $someVariable)
Zach Smith
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.