¿Cómo puedo almacenar el resultado del comando de búsqueda como matrices en Bash?

Estoy tratando de guardar el resultado como matrices. Aquí está mi código:

#!/bin/bash echo "input : " read input echo "searching file with this pattern '${input}' under present directory" array=`find . -name ${input}` len=${#array[*]} echo "found : ${len}" i=0 while [ $i -lt $len ] do echo ${array[$i]} let i++ done 

Obtengo 2 archivos .txt en el directorio actual. Así que espero ‘2’ como resultado de ${len} . Sin embargo, imprime 1. La razón por la cual es toma todos los resultados de encontrar como uno elementos. ¿Cómo puedo arreglar esto? Gracias.

PD. Encontré varias soluciones en StackOverFlow sobre un problema similar. Sin embargo, es un poco diferente para poder aplicar al mío. Necesito almacenar el resultado en la variable antes del ciclo. Gracias de nuevo.

Aquí hay una solución para obtener la salida de find en una matriz de bash :

 array=() while IFS= read -r -d $'\0'; do array+=("$REPLY") done < <(find . -name "${input}" -print0) 

Esto es complicado porque, en general, los nombres de archivo pueden tener espacios, nuevas líneas y otros caracteres hostiles a las secuencias de comandos. La única forma de usar find y tener los nombres de los archivos separados de forma segura es usar -print0 que imprime los nombres de los archivos separados con un carácter nulo. Esto no sería una gran inconveniencia si las funciones readarray / mapfile bash readarray cadenas separadas por null, pero no es así. La read de Bash sí y eso nos lleva al ciclo de arriba.

Cómo funciona

  1. La primera línea crea una matriz vacía: array=()

  2. Cada vez que se ejecuta la instrucción de read , se read un nombre de archivo separado nulo de la entrada estándar. La opción -r le dice a la read que deje solo caracteres de barra invertida. El -d $'\0' dice que la entrada será nula-separada. Como omitimos el nombre para read , el shell pone la entrada en el nombre predeterminado: REPLY .

  3. La instrucción array+=("$REPLY") agrega el nuevo nombre de archivo a la matriz de array .

  4. La línea final combina la redirección y la sustitución de comandos para proporcionar la salida de find a la entrada estándar del ciclo while.

¿Por qué usar la sustitución de procesos?

Si no usamos la sustitución del proceso, el ciclo se podría escribir como:

 array=() find . -name "${input}" -print0 >tmpfile while IFS= read -r -d $'\0'; do array+=("$REPLY") done  

En lo anterior, la salida de find se almacena en un archivo temporal y ese archivo se usa como entrada estándar para el ciclo while. La idea de la sustitución de procesos es hacer que esos archivos temporales sean innecesarios. Entonces, en lugar de tener el ciclo while obtiene su stdin de tmpfile , podemos hacer que obtenga su stdin de < (find . -name ${input} -print0) .

La sustitución de procesos es ampliamente útil. En muchos lugares donde un comando quiere leer desde un archivo, puede especificar la sustitución del proceso, < (...) , en lugar de un nombre de archivo. Existe una forma análoga, >(...) , que se puede usar en lugar de un nombre de archivo donde el comando desea escribir en el archivo.

Al igual que las matrices, la sustitución de procesos es una característica de bash y otros shells avanzados. No es parte del estándar POSIX.

Notas adicionales

El siguiente comando crea una variable de shell, no una matriz de shell:

 array=`find . -name "${input}"` 

Si quisiera crear una matriz, necesitaría poner parens alrededor de la salida de find. Entonces, ingenuamente, uno podría:

 array=(`find . -name "${input}"`) # don't do this 

El problema es que el intérprete de comandos realiza la división de palabras en los resultados de find para garantizar que los elementos del conjunto no garanticen que sean los que usted desea.

Si está utilizando bash 4 o posterior, puede reemplazar su uso de find con

 shopt -s globstar nullglob array=( **/*"$input"* ) 

El ** patrón habilitado por globstar coincide con 0 o más directorios, lo que permite que el patrón coincida con una profundidad arbitraria en el directorio actual. Sin la opción nullglob , el patrón (después de la expansión del parámetro) se trata literalmente, por lo que sin coincidencias se tendría una matriz con una sola cadena en lugar de una matriz vacía.

Agregue la opción dotglob a la primera línea también si desea recorrer directorios ocultos (como .ssh ) y hacer coincidir los archivos ocultos (como .bashrc ) también.

puedes probar algo como

 array=(`find . -type f | sort -r | head -2`) 

, y para imprimir los valores de matriz, puede intentar algo como echo "${array[*]}"

En bash, $() ayuda a ejecutar un comando y capturar el resultado. Pasar esto a IFS con \n como delímetro ayuda a convertir eso en una matriz.

 IFS='\n' read -r -a txt_files < << $(find /path/to/dir -name "*.txt") 

Podrías hacer así:

 #!/bin/bash echo "input : " read input echo "searching file with this pattern '${input}' under present directory" array=(`find . -name '*'${input}'*'`) for i in "${array[@]}" do : echo $i done 

Para mí, esto funcionó bien en cygwin:

 declare -a names=$(echo "("; find   -printf '"%p" '; echo ")") for nm in "${names[@]}" do echo "$nm" done 

Esto funciona con espacios, pero no con comillas dobles (“) en los nombres de directorio.

Cuidado con el espacio en la opción -printf.