web-dev-qa-db-fra.com

Comment puis-je stocker les résultats de la commande find sous forme de tableau dans Bash

J'essaie de sauvegarder le résultat de la recherche sous forme de tableaux . Voici mon code:

#!/bin/bash

echo "input : "
read input

echo "searching file with this pattern '${input}' under present directory"
array=`find . -name ${input}`

len=${#array[*]}
echo "found : ${len}"

i=0

while [ $i -lt $len ]
do
echo ${array[$i]}
let i++
done

J'obtiens 2 fichiers .txt dans le répertoire en cours . Donc j'attends '2' comme résultat de ${len}. Cependant, il affiche 1 . La raison pour laquelle il faut tout le résultat de find en tant qu’éléments . Comment puis-je résoudre ce problème? Merci.

P.S J'ai trouvé plusieurs solutions dans Stack OverFlow à propos d'un problème similaire. Cependant, c'est un peu différent pour que je puisse appliquer à la mienne. J'ai besoin de stocker le résultat dans la variable avant la boucle. Merci encore.

46
Juneyoung Oh

Voici une solution pour obtenir la sortie de find dans un tableau bash:

array=()
while IFS=  read -r -d $'\0'; do
    array+=("$REPLY")
done < <(find . -name "${input}" -print0)

Ceci est délicat car, en général, les noms de fichiers peuvent avoir des espaces, de nouvelles lignes et d’autres caractères hostiles aux scripts. La seule façon d'utiliser find et de séparer les noms de fichiers en toute sécurité consiste à utiliser -print0, qui affiche les noms de fichiers séparés par un caractère null. Cela ne serait pas très gênant si les fonctions readarraymapfile de bash prenaient en charge les chaînes séparées par null, mais ce n'est pas le cas. read de Bash fait et cela nous amène à la boucle ci-dessus.

Comment ça marche

  1. La première ligne crée un tableau vide: array=()

  2. Chaque fois que l'instruction read est exécutée, un nom de fichier séparé de null est lu à partir de l'entrée standard. L'option -r indique à read de ne pas modifier les barres obliques inverses. Le -d $'\0' indique à read que l'entrée sera séparée de zéro. Puisque nous omettons le nom de read, le shell place l'entrée dans le nom par défaut: REPLY.

  3. L'instruction array+=("$REPLY") ajoute le nouveau nom de fichier au tableau array.

  4. La dernière ligne combine la redirection et la substitution de commandes pour fournir la sortie de find à l’entrée standard de la boucle while

Pourquoi utiliser la substitution de processus?

Si nous n'utilisions pas la substitution de processus, la boucle pourrait être écrite ainsi:

array=()
find . -name "${input}" -print0 >tmpfile
while IFS=  read -r -d $'\0'; do
    array+=("$REPLY")
done <tmpfile
rm -f tmpfile

Dans ce qui précède, la sortie de find est stockée dans un fichier temporaire et ce fichier est utilisé comme entrée standard dans la boucle while. L'idée de la substitution de processus est de rendre ces fichiers temporaires inutiles. Ainsi, au lieu que la boucle while obtienne son stdin de tmpfile, nous pouvons lui demander d'obtenir son stdin de <(find . -name ${input} -print0).

La substitution de processus est très utile. Dans de nombreux endroits où une commande veut _/lire à partir d'un fichier, vous pouvez spécifier la substitution de processus, <(...), au lieu d'un nom de fichier. Il existe une forme analogue, >(...), qui peut être utilisée à la place d'un nom de fichier lorsque la commande veut write dans le fichier.

Comme les tableaux, la substitution de processus est une caractéristique de bash et d'autres shells avancés. Cela ne fait pas partie de la norme POSIX.

Notes complémentaires

La commande suivante crée une variable Shell, pas un tableau Shell:

array=`find . -name "${input}"`

Si vous voulez créer un tableau, vous devez mettre des parens autour de la sortie de find. Alors, naïvement, on pourrait:

array=(`find . -name "${input}"`)  # don't do this

Le problème est que le shell effectue le fractionnement de Word sur les résultats de find, de sorte que les éléments du tableau ne sont pas nécessairement ce que vous voulez.

69
John1024

Si vous utilisez bash 4 ou une version ultérieure, vous pouvez remplacer votre utilisation de find par

shopt -s globstar nullglob
array=( **/*"$input"* )

Le modèle ** activé par globstar correspond à 0 ou plusieurs répertoires, ce qui permet au modèle de correspondre à une profondeur arbitraire dans le répertoire actuel. Sans l'option nullglob, le modèle (après le développement du paramètre) est traité de manière littérale. Par conséquent, sans correspondance, vous disposeriez d'un tableau avec une seule chaîne plutôt que d'un tableau vide.

Ajoutez également l'option dotglob à la première ligne si vous souhaitez parcourir les répertoires cachés (tels que .ssh) et faire correspondre les fichiers cachés (tels que .bashrc). 

11
chepner

vous pouvez essayer quelque chose comme

array=(`find . -type f | sort -r | head -2`)
, et pour imprimer les valeurs du tableau, vous pouvez essayer quelque chose comme echo "${array[*]}"

7
Ahmed Al-Haffar

Bash 4.4 a introduit une option -d dans readarraymapfile, DE SORTE QUE CECI PEUT MAINTENANT &ECIRC;TRE R&EACUTE;SOLU AVEC

readarray -d '' array < <(find . -name "$input" -print0)

POUR UNE M&EACUTE;THODE QUI FONCTIONNE AVEC DES NOMS DE FICHIERS ARBITRAIRES, Y COMPRIS DES ESPACES, DES NOUVELLES LIGNES ET DES CARACT&EGRAVE;RES GLOBANTS.

&AGRAVE; PARTIR DU MANUEL (SANS AUTRES OPTIONS):

mapfile [-d delim] [array]

-d
LE PREMIER CARACT&EGRAVE;RE DE _/delim
est utilisé pour terminer chaque ligne d'entrée plutôt que de nouvelle ligne. Si delim est la chaîne vide, mapfile met fin à une ligne lorsqu'il lit un caractère NUL.

Et readarray est juste un synonyme de mapfile.

1
Benjamin W.

Dans bash, $(<any_Shell_cmd>) permet d’exécuter une commande et de capturer la sortie. Transmettre ceci à IFS avec \n en tant que délimiteur permet de convertir cela en tableau.

IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")
0
rashok