Necesito leer la salida de un comando en mi script en una matriz. El comando es, por ejemplo:
ps aux | grep | grep | x
y da la salida línea por línea así:
10
20
30
Necesito leer los valores de la salida del comando en una matriz, y luego trabajaré un poco si el tamaño de la matriz es menor que tres.
Respuestas:
Las otras respuestas se romperá si la salida del comando contiene espacios (lo cual es bastante frecuente) o Glob caracteres como
*
,?
,[...]
.Para obtener el resultado de un comando en una matriz, con una línea por elemento, existen esencialmente 3 formas:
Con el uso de Bash≥4
mapfile
, es el más eficiente:De lo contrario, un bucle que lee la salida (más lento, pero seguro):
Como sugirió Charles Duffy en los comentarios (¡gracias!), Lo siguiente podría funcionar mejor que el método de bucle en el número 2:
Asegúrese de utilizar exactamente este formulario, es decir, asegúrese de tener lo siguiente:
IFS=$'\n'
en la misma línea que laread
declaración: esto solo establecerá la variable de entorno soloIFS
para laread
declaración. Por lo tanto, no afectará en absoluto al resto del guión. El propósito de esta variable es indicarread
que se rompa la secuencia en el carácter EOL\n
.-r
: esto es importante. Diceread
que no interprete las barras invertidas como secuencias de escape.-d ''
: tenga en cuenta el espacio entre la-d
opción y su argumento''
. Si no deja un espacio aquí,''
nunca se verá, ya que desaparecerá en el paso de eliminación de citas cuando Bash analice la declaración. Esto le diceread
que deje de leer en el byte nulo. Algunas personas lo escriben como-d $'\0'
, pero no es realmente necesario.-d ''
es mejor.-a my_array
le diceread
que llene la matrizmy_array
mientras lee la secuencia.printf '\0'
instrucción aftermy_command
, para queread
regrese0
; en realidad, no es gran cosa si no lo hace (solo obtendrá un código de retorno1
, lo cual está bien si no lo usaset -e
, que de todos modos no debería), pero téngalo en cuenta. Es más limpio y semánticamente correcto. Tenga en cuenta que esto es diferente deprintf ''
, que no genera nada.printf '\0'
imprime un byte nulo, necesarioread
para dejar felizmente de leer allí (¿recuerdas la-d ''
opción?).Si puede, es decir, si está seguro de que su código se ejecutará en Bash≥4, utilice el primer método. Y puedes ver que también es más corto.
Si desea usar
read
, el ciclo (método 2) podría tener una ventaja sobre el método 3 si desea realizar algún procesamiento a medida que se leen las líneas: tiene acceso directo a él (a través de la$line
variable en el ejemplo que di), y también tiene acceso a las líneas ya leídas (a través de la matriz${my_array[@]}
en el ejemplo que di).Tenga en cuenta que
mapfile
proporciona una manera de que se evalúe una devolución de llamada en cada línea leída y, de hecho, incluso puede decirle que solo llame a esta devolución de llamada cada N líneas leídas; eche un vistazo ahelp mapfile
las opciones-C
y-c
allí. (Mi opinión sobre esto es que es un poco torpe, pero se puede usar a veces si solo tienes cosas simples que hacer; ¡realmente no entiendo por qué esto se implementó en primer lugar!).Ahora les voy a decir por qué el siguiente método:
se rompe cuando hay espacios:
Entonces, algunas personas recomendarán usarlo
IFS=$'\n'
para solucionarlo:Pero ahora usemos otro comando, con globs :
Eso es porque tengo un archivo llamado
t
en el directorio actual ... y este nombre de archivo coincide con el glob[three four]
... en este punto, algunas personas recomendarían usarloset -f
para deshabilitar el globbing: pero míralo: tienes que cambiarIFS
y usarset -f
para poder arreglar un técnica rota (y ni siquiera la estás arreglando realmente)! al hacer eso, realmente estamos luchando contra el caparazón, no trabajando con el caparazón .aquí estamos trabajando con el caparazón!
fuente
mapfile
antes, es exactamente lo que me he perdido durante años. Supongo que las versiones recientes debash
tienen tantas características nuevas y agradables que debería pasar unos días leyendo los documentos y escribiendo una buena hoja de referencia.< <(command)
en scripts de shell, la línea shebang debería ser#!/bin/bash
- si se ejecuta como#!/bin/sh
, bash saldrá con un error de sintaxis.bash my_script.sh
y no con el comando shsh my_script.sh
sh
ydash
no saben nada sobre arreglos, excepto, por supuesto, para la$@
matriz de parámetros posicionales ).IFS=$'\n' read -r -d '' -a my_array < <(my_command && printf '\0')
: funciona correctamente en bash 3.xy también pasa por un estado de salida fallido demy_command
aread
.Puedes usar
para almacenar la salida del comando
<command>
en la matrizmy_array
.Puede acceder a la longitud de esa matriz usando
Ahora la longitud se almacena en formato
my_array_length
.fuente
VAR="$(<command>)"
y luegomy_array=("$VAR")
omy_array+=("$VAR")
Imagine que va a poner los archivos y nombres de directorio (debajo de la carpeta actual) en una matriz y contar sus elementos. El guión sería como;
O puede iterar sobre esta matriz agregando el siguiente script:
Tenga en cuenta que este es el concepto central y la entrada se considera desinfectada antes, es decir, eliminar caracteres adicionales, manejar cadenas vacías, etc. (que está fuera del tema de este hilo).
fuente