Tengo una variable que contiene la salida multilínea de un comando. ¿Cuál es la forma más eficiente de leer la salida línea por línea de la variable?
Por ejemplo:
jobs="$(jobs)"
if [ "$jobs" ]; then
# read lines from $jobs
fi
Puede usar un ciclo while con sustitución de proceso:
while read -r line
do
echo "$line"
done < <(jobs)
Una forma óptima de leer una variable multilínea es establecer una IFS
variable en blanco y printf
la variable con una nueva línea final:
# Printf '%s\n' "$var" is necessary because printf '%s' "$var" on a
# variable that doesn't end with a newline then the while loop will
# completely miss the last line of the variable.
while IFS= read -r line
do
echo "$line"
done < <(printf '%s\n' "$var")
Nota: Según shellcheck sc2031 , el uso de la sustitución de procesos es preferible a una tubería para evitar [sutilmente] crear una subshell.
Además, tenga en cuenta que al nombrar la variable jobs
puede causar confusión, ya que ese también es el nombre de un comando de shell común.
while IFS= read
... Si desea evitar la interpretación, useread -r
echo
aprintf %s
, para que su script funcione incluso con entradas no mansas./tmp
directorio se pueda escribir, ya que se basa en poder crear un archivo de trabajo temporal. Si alguna vez te encuentras en un sistema restringido con/tmp
solo lectura (y no cambiable por ti), estarás contento con la posibilidad de usar una solución alternativa, por ejemplo, con laprintf
tubería.printf "%s\n" "$var" | while IFS= read -r line
Para procesar la salida de un comando línea por línea ( explicación ):
Si ya tiene los datos en una variable:
printf %s "$foo"
es casi idénticoecho "$foo"
, pero se imprime$foo
literalmente, mientras queecho "$foo"
podría interpretarse$foo
como una opción para el comando echo si comienza con un-
, y podría expandir las secuencias de barra invertida$foo
en algunos shells.Tenga en cuenta que en algunos shells (ash, bash, pdksh, pero no ksh o zsh), el lado derecho de una tubería se ejecuta en un proceso separado, por lo que se pierde cualquier variable que establezca en el bucle. Por ejemplo, el siguiente script de conteo de líneas imprime 0 en estos shells:
Una solución alternativa es colocar el resto del script (o al menos la parte que necesita el valor del
$n
bucle) en una lista de comandos:Si actuar sobre las líneas no vacías es lo suficientemente bueno y la entrada no es enorme, puede usar la división de palabras:
Explicación: establecer
IFS
una nueva línea hace que la división de palabras se produzca solo en las nuevas líneas (en oposición a cualquier carácter de espacio en blanco en la configuración predeterminada).set -f
desactiva el globbing (es decir, la expansión de comodines), que de otro modo ocurriría con el resultado de una sustitución de comando$(jobs)
o un sustitutino variable$foo
. Elfor
bucle actúa sobre todas las piezas de$(jobs)
, que son todas las líneas no vacías en la salida del comando. Finalmente, restaure el globbing y laIFS
configuración.fuente
local IFS=something
. No afectará el valor del alcance global. IIRC,unset IFS
no lo devuelve al valor predeterminado (y ciertamente no funciona si no era el valor predeterminado de antemano).Problema: si usa el bucle while, se ejecutará en subshell y se perderán todas las variables. Solución: usar para bucle
fuente
while read
bucle en bash significa que el bucle while está en una subshell, por lo que las variables no son globales.while read;do ;done <<< "$var"
hace que el cuerpo del bucle no sea una subshell. (Bash reciente tiene una opción para poner el cuerpo de uncmd | while
bucle no en una subcapa, como ksh siempre ha tenido.)En versiones recientes de bash, use
mapfile
oreadarray
para leer eficientemente la salida del comando en matricesDescargo de responsabilidad: un ejemplo horrible, pero puede encontrar un mejor comando para usar que usted mismo
fuente
readarray
una función y llama a la función varias veces.Referencias
while
IFS
read
fuente
-r
es una buena idea también; Previene\` interpretation... (it is in your links, but its probably worth mentioning, just to round out your
IFS = `(que es esencial para evitar la pérdida de espacios en blanco)Puede usar <<< para simplemente leer la variable que contiene los datos separados por nueva línea:
fuente