Question

L'utilisation find . -print0 semble être le seul moyen sûr d'obtenir une liste de fichiers en bash en raison de la possibilité de noms de fichiers contenant des espaces, des sauts de ligne, entre guillemets, etc.

Cependant, je vais avoir du mal à faire réellement la production de trouver utile dans bash ou avec d'autres utilitaires de ligne de commande. La seule façon que j'ai réussi à utiliser la sortie est par la tuyauterie à Perl, et changer les IFS de Perl null:

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

Cet exemple imprime le nombre de fichiers trouvés, évitant le danger de nouvelles lignes dans les noms de fichiers corrompant le compte, comme cela se produirait avec:

find . | wc -l

Comme la plupart des programmes de ligne de commande ne prennent pas en charge l'entrée délimité nul, je me dis que la meilleure chose serait de capturer la sortie de find . -print0 dans un tableau bash, comme je l'ai fait dans le Perl extrait ci-dessus, puis passez à la tâche , quel qu'il soit.

Comment puis-je faire?

Cela ne fonctionne pas:

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

beaucoup plus question générale pourrait être: Comment puis-je faire des choses utiles avec des listes de fichiers dans bash

?
Était-ce utile?

La solution

Shamelessly volé BashFAQ Greg :

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)

Notez que la construction de redirection utilisé ici (cmd1 < <(cmd2)) est similaire, mais pas tout à fait la même chose que plus pipeline habituel (cmd2 | cmd1) - si les commandes sont builtins shell (par exemple while), la version de pipeline les exécute en sous-couches et toutes les variables qu'ils fixent (par exemple, le a tableau) sont perdus lors de leur sortie. cmd1 < <(cmd2) ne fonctionne que cmd2 dans un sous-shell, de sorte que le tableau vit passé sa construction. Attention: cette forme de redirection est uniquement disponible en bash, même pas bash en mode sh émulation; vous devez démarrer votre script avec #!/bin/bash.

En outre, parce que l'étape de traitement de fichiers (dans ce cas, juste a[i++]="$file", mais vous voudrez peut-être faire quelque chose colombophile directement dans la boucle) a son entrée redirigé, il ne peut pas utiliser toutes les commandes qui pourraient lire de stdin. Pour éviter cette limitation, je tends à utiliser:

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)

... qui passe la liste des fichiers via l'unité 3, plutôt que stdin.

Autres conseils

Peut-être que vous recherchez xargs:

find . -print0 | xargs -r0 do_something_useful

L'option -L 1 pourrait être utile pour vous aussi, ce qui rend xargs exec do_something_useful avec seulement 1 argument de fichier.

Le principal problème est que le séparateur NUL (\ 0) est inutile ici, car il est impossible de IFS attribuer une valeur NUL. Alors que de bons programmeurs nous prenons en charge, que l'entrée de notre programme est quelque chose qu'il est capable de gérer.

Tout d'abord, nous créons un petit programme qui fait cette partie pour nous:

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

... et l'appeler base64str (ne pas oublier chmod + x)

Deuxièmement, nous pouvons maintenant utiliser un simple et direct pour la boucle:

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

Le truc est donc, qu'une chaîne base64 a aucun signe qui cause des problèmes pour bash -. Bien sûr, un xxd ou quelque chose de similaire peut également faire le travail

Une autre façon de fichiers de comptage:

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

Depuis Bash 4.4, le mapfile builtin a le commutateur -d (pour spécifier un séparateur, similaire au commutateur -d de l'instruction read) et le séparateur peut être l'octet nul. Par conséquent, une bonne réponse à la question dans le titre

  

Capture de sortie de find . -print0 dans un tableau bash

est:

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

Vous pouvez faire en toute sécurité le comte avec ceci:

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

(Il imprime une nouvelle ligne pour chaque fichier / dir trouvé, puis compter les nouvelles lignes imprimées ...)

Je pense que des solutions plus élégantes existe, mais je jette celui-ci dans ce travaillera également pour les noms de fichiers avec des espaces et / ou des sauts de ligne.

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

Vous pouvez alors par exemple la liste des fichiers un par un (dans ce cas, dans l'ordre inverse):

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

Cette page donne un bel exemple, et plus voir Chapitre 26 dans Guide avancé d'écriture des scripts Bash .

Évitez xargs si vous le pouvez:

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' 

Je suis nouveau, mais je crois que cette réponse; espère que cela aide quelqu'un:

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

Ceci est similaire à la version Stephan202, mais les fichiers (et répertoires) sont placés dans un tableau à la fois. La boucle de for ici est juste pour « faire des choses utiles »:

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

Pour obtenir un compte:

echo ${#files[@]}

question ancienne, mais personne ne suggère cette méthode simple, donc je pensais. Certes, si vos noms de fichiers ont un ETX, cela ne résout pas votre problème, mais je soupçonne qu'il sert à tout scénario monde réel. Essayer d'utiliser null semble contraire aux règles de défaut de traitement IFS. Saison à vos goûts avec des options de FIND et la gestion des erreurs.

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

La réponse de Gordon Davisson est idéal pour bash. Toutefois, un raccourci existe utile pour les utilisateurs zsh:

D'abord, placez-vous dans une variable chaîne:

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

Ensuite, diviser cette variable et la stocker dans un tableau:

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

Il y a une astuce: ^@ est le caractère NUL. Pour ce faire, vous devez taper Ctrl + V suivi par Ctrl + @.

Vous pouvez vérifier chaque entrée de $ B contient juste valeur:

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

Les lecteurs attentifs peuvent remarquer que l'appel à la commande find peut être évité dans la plupart des cas en utilisant la syntaxe **. Par exemple:

B=( /tmp/** )

Bash n'a jamais été aussi bon à la manipulation des noms de fichiers (ou tout autre texte vraiment) parce qu'il utilise des espaces comme séparateur de liste.

Je vous recommande d'utiliser python avec la bibliothèque sh place.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top