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. .
IFS=
evitar eliminar los espacios en blanco del comienzo o el final de las líneas de entrada. Puede probar esto fácilmente comparando la salida deread var <<<' abc '; echo ">$var<"
con la salida deIFS= read var <<<' abc '; echo ">$var<"
. En el primer caso,abc
se eliminan los espacios antes y después . En este último, no lo son. Los nombres de archivo que comienzan o terminan con espacios en blanco pueden ser inusuales pero, si existen, queremos que se procesen correctamente.<'
hecho <<(encuentre aaa / -not -newermt "$ last_build_timestamp_v" -type f -print0) '''
se puede usar en lugar de$'\0'
:n=0; while IFS= read -r -d '' line || [ "$line" ]; do echo "$((++n)):$line"; done < <(printf 'first\nstill first\0second\0third')
BLAH=$(find . -name '*.php')
. Como se discutió en la respuesta, ese enfoque funcionará en casos limitados, pero no funcionará en general con todos los nombres de archivo y no produce, como esperaba el OP, una matriz .Bash 4.4 introdujo una
-d
opción parareadarray
/mapfile
, por lo que ahora se puede resolver conreadarray -d '' array < <(find . -name "$input" -print0)
para un método que funciona con nombres de archivo arbitrarios, incluidos espacios en blanco, nuevas líneas y caracteres globales. Esto requiere que su
find
soporte-print0
, como por ejemplo GNU find lo hace.Del manual (omitiendo otras opciones):
Y
readarray
es solo un sinónimo demapfile
.fuente
Si está usando
bash
4 o posterior, puede reemplazar su uso defind
conshopt -s globstar nullglob array=( **/*"$input"* )
El
**
patrón habilitado porglobstar
coincide con 0 o más directorios, lo que permite que el patrón coincida con una profundidad arbitraria en el directorio actual. Sin elnullglob
opción, el patrón (después de la expansión de parámetros) se trata literalmente, por lo que sin coincidencias tendría una matriz con una sola cadena en lugar de una matriz vacía.Agregue la
dotglob
opción a la primera línea también si desea recorrer directorios ocultos (como.ssh
) y hacer coincidir archivos ocultos (como.bashrc
) también.fuente
nullglob
también ...dotglob
se establezca (esto puede ser deseado o no, pero también vale la pena mencionarlo).puedes probar algo como
, y para imprimir los valores de la matriz, puede probar algo como echoarray=(`find . -type f | sort -r | head -2`)
"${array[*]}"
fuente
Lo siguiente parece funcionar tanto para Bash como para Z Shell en macOS.
#! /bin/sh IFS=$'\n' paths=($(find . -name "foo")) unset IFS printf "%s\n" "${paths[@]}"
fuente
En bash,
$(<any_shell_cmd>)
ayuda a ejecutar un comando y capturar la salida. Pasar esto aIFS
with\n
como delimitador ayuda a convertir eso en una matriz.IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")
fuente
find
en la matriz.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
fuente
Para mí, esto funcionó bien en cygwin:
declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")") for nm in "${names[@]}" do echo "$nm" done
Esto funciona con espacios, pero no con comillas dobles (") en los nombres de los directorios (que de todos modos no están permitidos en un entorno Windows).
Tenga cuidado con el espacio en la opción -printf.
fuente