into how array arrays bash null find delimiter

arrays - how - split string by token bash



Captura de salida de find.-print0 en una matriz de bash (13)

Bash nunca ha sido bueno en el manejo de nombres de archivo (o de texto en realidad) porque usa espacios como un delimitador de lista.

Recomiendo usar python con la biblioteca sh lugar.

Usando find . -print0 find . -print0 parece ser la única forma segura de obtener una lista de archivos en bash debido a la posibilidad de nombres de archivos que contengan espacios, líneas nuevas, comillas, etc.

Sin embargo, estoy teniendo dificultades para hacer que la salida de find sea útil dentro de bash o con otras utilidades de línea de comandos. La única forma en que he podido utilizar la salida es conectarla a perl y cambiar IFS de perl a nulo:

find . -print0 | perl -e ''$/="/0"; @files=<>; print $#files;''

Este ejemplo imprime la cantidad de archivos encontrados, evitando el peligro de nuevas líneas en nombres de archivos que corrompen el recuento, como ocurriría con:

find . | wc -l

Como la mayoría de los programas de línea de comandos no admiten entrada delimitada nula, creo que lo mejor sería capturar la salida de find . -print0 find . -print0 en una matriz de bash, como he hecho en el fragmento de perl anterior, y luego continuar con la tarea, cualquiera que sea.

¿Cómo puedo hacer esto?

Esto no funciona:

find . -print0 | ( IFS=$''/0'' ; array=( $( cat ) ) ; echo ${#array[@]} )

Una pregunta mucho más general podría ser: ¿cómo puedo hacer cosas útiles con listas de archivos en bash?


Creo que existen soluciones más elegantes, pero lanzaré esta. Esto también funcionará para nombres de archivos con espacios y / o líneas nuevas:

i=0; for f in *; do array[$i]="$f" ((i++)) done

Puede, por ejemplo, enumerar los archivos uno por uno (en este caso en orden inverso):

for ((i = $i - 1; i >= 0; i--)); do ls -al "${array[$i]}" done

Esta página ofrece un buen ejemplo, y para obtener más información, consulte el Capítulo 26 en la Guía avanzada de scripts Bash .


Desde Bash 4.4, el mapfile incorporado tiene el mapfile -d (para especificar un delimitador, similar al -d de la instrucción read ), y el delimitador puede ser el byte nulo. Por lo tanto, una buena respuesta a la pregunta en el título

Captura de salida de find . -print0 find . -print0 en una matriz de bash

es:

mapfile -d '''' ary < <(find . -print0)


El principal problema es que el delimitador NUL (/ 0) es inútil aquí, porque no es posible asignar a IFS un valor NUL. Entonces, como buenos programadores, nos ocupamos de que la entrada de nuestro programa sea algo que pueda manejar.

Primero creamos un pequeño programa, que hace esta parte para nosotros:

#!/bin/bash printf "%s" "$@" | base64

... y llámalo base64str (no olvides chmod + x)

En segundo lugar, ahora podemos usar un bucle for simple y directo:

for i in `find -type f -exec base64str ''{}'' /;` do file="`echo -n "$i" | base64 -d`" # do something with file done

Entonces, el truco es que una cadena base64 no tiene señal que cause problemas para bash; por supuesto, un xxd o algo similar también puede hacer el trabajo.


Esto es similar a la versión de Stephan202, pero los archivos (y directorios) se ponen en una matriz, todo a la vez. El bucle for aquí es solo para "hacer cosas útiles":

files=(*) # put files in current directory into an array i=0 for file in "${files[@]}" do echo "File ${i}: ${file}" # do something useful let i++ done

Para obtener un conteo:

echo ${#files[@]}


Evita xargs si puedes:

man ruby | less -p 777 IFS=$''/777'' #array=( $(find ~ -maxdepth 1 -type f -exec printf "%s/777" ''{}'' /; 2>/dev/null) ) array=( $(find ~ -maxdepth 1 -type f -exec printf "%s/777" ''{}'' + 2>/dev/null) ) echo ${#array[@]} printf "%s/n" "${array[@]}" | nl echo "${array[0]}" IFS=$'' /t/n''


La respuesta de Gordon Davisson es genial para bash. Sin embargo, existe un atajo útil para los usuarios de zsh:

Primero, coloca tu cadena en una variable:

A="$(find /tmp -type f -print0)"

A continuación, divida esta variable y guárdela en una matriz:

B=( ${(s/^@/)A} )

Hay un truco: ^@ es el personaje NUL. Para hacerlo, debes presionar Ctrl + V seguido de Ctrl + @.

Puede verificar que cada entrada de $ B contenga el valor correcto:

for i in "$B[@]"; echo /"$i/"

Los lectores cuidadosos pueden notar que el comando llamar para find se puede evitar en la mayoría de los casos utilizando la sintaxis ** . Por ejemplo:

B=( /tmp/** )


Otra forma de contar archivos:

find /DIR -type f -print0 | tr -dc ''/0'' | wc -c


Robado sin vergüenza de Greg''s BashFAQ :

unset a i while IFS= read -r -d $''/0'' file; do a[i++]="$file" # or however you want to process each file done < <(find /tmp -type f -print0)

Tenga en cuenta que la construcción de redirección utilizada aquí ( cmd1 < <(cmd2) ) es similar, pero no exactamente igual a la tubería más habitual ( cmd2 | cmd1 ) - si los comandos son shell builtins (por ejemplo, while ), la versión de la tubería los ejecuta en subcapas, y cualquier variable que establezcan (por ejemplo, la matriz a ) se pierde cuando salen. cmd1 < <(cmd2) solo ejecuta cmd2 en una subcadena, por lo que la matriz vive más allá de su construcción. Advertencia: esta forma de redirección solo está disponible en bash, ni siquiera bash en modo sh-emulation; debe comenzar su script con #!/bin/bash .

Además, dado que el paso de procesamiento del archivo (en este caso, solo a[i++]="$file" , pero es posible que desee hacer algo más elegante directamente en el bucle) tiene su entrada redireccionada, no puede usar ningún comando que pueda leer stdin. Para evitar esta limitación, tiendo a usar:

unset a i while IFS= read -r -u3 -d $''/0'' file; do a[i++]="$file" # or however you want to process each file done 3< <(find /tmp -type f -print0)

... que pasa la lista de archivos a través de la unidad 3, en lugar de stdin.


Soy nuevo, pero creo que esta es una respuesta; Espero que ayude a alguien:

STYLE="$HOME/.fluxbox/styles/" declare -a array1 LISTING=`find $HOME/.fluxbox/styles/ -print0 -maxdepth 1 -type f` echo $LISTING array1=( `echo $LISTING`) TAR_SOURCE=`echo ${array1[@]}` #tar czvf ~/FluxieStyles.tgz $TAR_SOURCE


Tal vez estás buscando xargs:

find . -print0 | xargs -r0 do_something_useful

La opción -L 1 también podría serle útil, lo que hace que xargs exec do_something_useful solo tenga 1 argumento de archivo.


Una vieja pregunta, pero nadie sugirió este método simple, así que pensé que lo haría. Si sus nombres de archivo tienen un ETX, esto no soluciona su problema, pero sospecho que sirve para cualquier escenario del mundo real. Intentar usar null parece entrar en conflicto con las reglas de manejo IFS predeterminadas. Sazone según sus gustos con opciones de búsqueda y manejo de errores.

savedFS="$IFS" IFS=$''/x3'' filenames=(`find wherever -printf %p$''/x3''`) IFS="$savedFS"


Usted puede hacer el recuento con esto de forma segura:

find . -exec echo '';'' | wc -l

(Imprime una nueva línea para cada archivo / directorio encontrado, y luego cuenta las nuevas líneas impresas ...)