Actualización 2020 para usuarios de Linux:
Si usted tiene una versión puesta al día de fiesta (4,4-alfa o mejor), ya que es probable que hacer si se encuentra en Linux, entonces usted debe estar usando la respuesta de Benjamin W. .
Si está en Mac OS, que —la última vez que lo comprobé— todavía usa bash 3.2, o está usando un bash anterior, continúe con la siguiente sección.
Respuesta para bash 4.3 o anterior
Aquí hay una solución para obtener la salida de find
una bash
matriz:
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 al script. La única forma de utilizar find
y tener los nombres de los archivos separados de forma segura entre sí es utilizar -print0
que imprima los nombres de los archivos separados con un carácter nulo. Esto no sería un gran inconveniente si las funciones readarray
/ de bash admitieran mapfile
cadenas separadas por nulos, pero no lo hacen. Bash's lo read
hace y eso nos lleva al bucle anterior.
[Esta respuesta se escribió originalmente en 2014. Si tiene una versión reciente de bash, consulte la actualización a continuación].
Cómo funciona
La primera línea crea una matriz vacía: array=()
Cada vez que read
se ejecuta la instrucción, se lee un nombre de archivo separado por nulos de la entrada estándar. La -r
opción dice read
que deje los caracteres de barra invertida solos. El -d $'\0'
indica read
que la entrada estará separada por nulos. Desde omitimos el nombre read
, la cáscara pone la entrada en el nombre por defecto: REPLY
.
La array+=("$REPLY")
declaración agrega el nuevo nombre de archivo a la matriz array
.
La línea final combina la redirección y la sustitución de comandos para proporcionar la salida find
a la entrada estándar del while
bucle.
¿Por qué utilizar la sustitución de procesos?
Si no usamos la sustitución de procesos, el ciclo podría escribirse como:
array=()
find . -name "${input}" -print0 >tmpfile
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done <tmpfile
rm -f tmpfile
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 innecesarios esos archivos temporales. Entonces, en lugar de que el while
bucle obtenga su stdin tmpfile
, podemos hacer que obtenga su stdin <(find . -name ${input} -print0)
.
La sustitución de procesos es muy útil. En muchos lugares donde un comando quiere leer de 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 quiere 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 forma parte del estándar POSIX.
Alternativa: lastpipe
Si lo desea, lastpipe
se puede utilizar en lugar de la sustitución del proceso (punta de sombrero: Caesar ):
set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS= read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array
shopt -s lastpipe
le dice a bash que ejecute el último comando en la tubería en el shell actual (no en el fondo). De esta manera, los array
restos existen después de que se completa la canalización. Porque lastpipe
solo tiene efecto si el control de trabajos está desactivado, ejecutamos set +m
. (En una secuencia de comandos, a diferencia de la línea de comando, el control de trabajos está desactivado de forma predeterminada).
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}"`)
El problema es que el shell realiza la división de palabras en los resultados de, find
por lo que no se garantiza que los elementos de la matriz sean los que desea.
Actualización 2019
A partir de la versión 4.4-alpha, bash ahora admite una -d
opción para que el ciclo anterior ya no sea necesario. En su lugar, se puede utilizar:
mapfile -d $'\0' array < <(find . -name "${input}" -print0)
Para más información sobre esto, por favor ver (y upvote) La respuesta de Benjamin W. .