Bash: canaliser la sortie 'find' dans 'readarray'

14

J'essaie de rechercher des fichiers en utilisant findet de placer ces fichiers dans un tableau Bash afin de pouvoir effectuer d'autres opérations sur eux (par exemple, lsou sur grepeux). Mais je ne peux pas comprendre pourquoi readarrayne lit pas la findsortie car elle y est canalisée.

Supposons que j'ai deux fichiers dans le répertoire en cours, file1.txtet file2.txt. La findsortie est donc la suivante:

$ find . -name "file*"
./file1.txt
./file2.txt

Je veux donc canaliser cela dans un tableau dont les deux éléments sont les chaînes "./file1.txt"et "./file2.txt"(sans guillemets, évidemment).

J'ai essayé cela, entre autres:

$ declare -a FILES
$ find . -name "file*" | readarray FILES
$ echo "${FILES[@]}"; echo "${#FILES[@]}"

0

Comme vous pouvez le voir sur la echosortie, mon tableau est vide.

Alors qu'est-ce que je fais mal ici? Pourquoi readarrayne pas lire findla sortie de son entrée standard et mettre ces chaînes dans le tableau?

villapx
la source

Réponses:

21

Lors de l'utilisation d'un pipeline, bash exécute les commandes en sous-coquilles. Par conséquent, le tableau est rempli, mais dans un sous-shell, de sorte que le shell parent n'y a pas accès.

Utilisez la substitution de processus:

readarray FILES < <(find)

Notez que cela ne fonctionne pas pour les fichiers avec des retours à la ligne dans leurs noms. Si tel est le cas, vous avez besoin d'une syntaxe plus élaborée:

readarray -d '' < <(find -print0)
choroba
la source
3
Pour prendre en charge les nouvelles lignes, cela suffit:readarray -d '' < <(find your_args -print0)
VasyaNovikov
6

La bonne solution est:

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

C'est similaire à ce que Greg's BashFAQ 020 explique en détail et cette réponse couvre .

N'a aucun problème avec les fichiers nommés impairs (qui ne contiennent aucun NUL dans le nom), avec des espaces ou de nouvelles lignes. Et le résultat est défini dans un tableau, ce qui le rend utile pour un traitement ultérieur.

Communauté
la source
Super, c'est une meilleure solution au problème que j'essayais de résoudre en premier lieu. +1 dès que mon représentant atteint 15 :)
villapx
3

readarray peut également lire depuis stdin

readarray FILES <<< "$(find . -name "file*")"; echo "${#FILES[@]}"
smac89
la source
Cela ne fonctionne pas find -print0pour la protection contre les noms de fichiers "inattendus".
roaima