Comment puis-je stocker la commande “find” résultats dans un tableau en Bash
Je suis en train d'enregistrer le résultat de find
sous la 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
Je reçois 2 .txt fichiers sous le répertoire courant.
J'attends donc '2' comme résultat de ${len}
. Cependant, il imprime 1.
La raison en est qu'il prend tous les résultats de find
comme l'un des éléments.
Comment puis-je résoudre ce problème?
P. S
J'ai trouvé plusieurs solutions sur StackOverFlow sur un problème similaire. Cependant, ils sont un peu différentes, donc je ne peux pas l'appliquer dans mon cas. J'ai besoin de stocker les résultats dans une variable avant la boucle. Merci encore.
Vous devez vous connecter pour publier un commentaire.
Ici est une solution pour obtenir la sortie de
find
dans unbash
tableau:C'est difficile parce que, en général, les noms de fichier peut contenir des espaces, de nouvelles lignes, et d'autres script hostiles caractères. La seule façon d'utiliser
find
et ont les noms de fichiers en toute sécurité séparés les uns des autres est d'utiliser-print0
qui imprime les noms de fichiers séparés par un caractère null. Ce ne serait pas beaucoup d'un inconvénient si bash estreadarray
/mapfile
fonctions prises en charge null séparées par des chaînes, mais ils ne le font pas. Bash estread
fait et qui nous conduit à la boucle ci-dessus.Comment il fonctionne
La première ligne crée un tableau vide:
array=()
Chaque fois que le
read
instruction est exécutée, null, séparés de nom de fichier est lu à partir de l'entrée standard. Le-r
option indiqueread
de laisser les barres obliques inverses seul. Le-d $'\0'
ditread
que l'entrée sera null séparées. Depuis que nous avons omettre le nom deread
, le shell met l'entrée dans le nom par défaut:REPLY
.La
array+=("$REPLY")
déclaration ajoute le nouveau nom de fichier pour le tableauarray
.La dernière ligne combine la redirection et de la substitution de commande pour fournir la sortie de
find
à l'entrée standard de lawhile
boucle.Pourquoi utiliser le processus de substitution?
Si nous n'avons pas utilisé le processus de substitution, la boucle peut être écrite comme:
Au-dessus de la sortie de
find
est stockée dans un fichier temporaire et que le fichier est utilisé comme norme d'entrée à la boucle while. L'idée de processus de substitution est de rendre ces fichiers temporaires inutiles. Donc, au lieu d'avoir lewhile
boucle obtenir son stdin detmpfile
, nous pouvons lui faire son stdin de<(find . -name ${input} -print0)
.Processus de substitution est largement utile. Dans de nombreux endroits où une commande veut lire à partir d'un fichier, vous pouvez spécifier le processus de substitution,
<(...)
, au lieu d'un nom de fichier. Il y a une forme analogue,>(...)
, qui peut être utilisé à la place d'un nom de fichier dans lequel la commande veut écrire pour le fichier.Comme des tableaux, processus de substitution est une fonction de bash et autres coquillages. Il ne fait pas partie de la norme POSIX.
Notes complémentaires
La commande suivante crée une variable du shell, pas un tableau shell:
Si vous souhaitez créer un tableau, vous auriez besoin de mettre des parenthèses autour de la sortie de la trouver. Donc, naïvement, on pourrait:
Le problème est que le shell effectue de couper un mot sur les résultats de
find
de sorte que les éléments du tableau ne sont pas garantis pour être ce que vous voulez.bash
manuel pour le processus de redirection de la section.IFS=
partie avantread
est inutile. Deman read
: "...le premier mot est attribué à la première nom, le deuxième mot de la deuxième nom, et ainsi de suite, avec des restes de mots et leur intervenant séparateurs attribué le nom de famille...Les personnages dans les FI sont utilisés pour séparer la ligne en mots" et "Si aucun nom n'est fourni, la lecture est affecté à la variable RÉPONSE". Car il n'y a pas d'argument fourni, l'ensemble de la ligne est affectée à la seule variableREPLY
. Le séparateur de mots spécifié parIFS
n'est pas utile ici, parce que la ligne n'est pas divisé en mots.IFS=
pour éviter la suppression des espaces des débuts ou des fins de lignes d'entrée. Vous pouvez le tester facilement en comparant la sortie deread var <<<' abc '; echo ">$var<"
avec la sortie deIFS= read var <<<' abc '; echo ">$var<"
. Dans le premier cas, les espaces avant et aprèsabc
sont supprimés. Dans ce dernier, ils ne le sont pas. Les noms de fichiers qui commencent ou se terminent par des espaces peut-être inhabituel, mais, s'ils existent, nous les voulons traitées correctement.<'
fait < <(trouver aaa/ -pas -newermt "$last_build_timestamp_v" -type f -print0)'bash scriptname
. Si ce n'est pas à la résoudre, une bonne étape suivante consiste à exécuter votre script par shellcheck.net. (Suffit de couper et coller le script dans shellcheck de la fenêtre et attendre un moment pour une réponse.)REPLY
.-printf
comme:while IFS= read -r -d $'\0'; do array+=("$REPLY"); done < <(find . -name "${input}" -printf '%f\0')
input
réglé à une valeur d'utilité? Pour vous assurer que toutes les variables sont initialisées, essayezinput='*'; array=(); while IFS= read -r -d $'\0'; do array+=("$REPLY"); done < <(find . -name "${input}" -printf '%f\0')
''
peut être utilisé à la place de$'\0'
:n=0; while IFS= read -r -d '' line || [ "$line" ]; do echo "$((++n)):$line"; done < <(printf 'first\nstill first\0second\0third')
find /DIRECTORY -maxdepth 1 -type d | cut -f5 -d'/'
dans ledone
ligne.find 1/2/3/4 -maxdepth 1 -type d -printf '%p\0' | cut -zf5 -d'/'
.Si vous utilisez
bash
4 ou version ultérieure, vous pouvez remplacer l'utilisation defind
avecLa
**
modèle activé parglobstar
correspond à 0 ou plusieurs répertoires, permettant le modèle pour correspondre à une profondeur arbitraire dans le répertoire courant. Sans lenullglob
option, le modèle (paramètre après expansion) est traité littéralement, donc avec pas de matches vous avez un tableau avec une seule chaîne plutôt que d'un tableau vide.Ajouter le
dotglob
option à la première ligne ainsi si vous voulez parcourir les répertoires cachés (comme.ssh
) et correspondent à des fichiers cachés (comme.bashrc
) ainsi.nullglob
trop...dotglob
est défini (ce qui peut ou peut ne pas être voulu, mais il vaut la peine de mentionner aussi).vous pouvez essayer quelque chose comme
, et afin d'imprimer le tableau de valeurs , vous pouvez essayer quelque chose comme de l'écho
"${array[*]}"
Bash 4.4 introduit un
-d
option pourreadarray
/mapfile
, donc cela peut maintenant être résolu avecpour une méthode qui fonctionne avec de l'arbitraire des noms de fichiers, y compris les espaces, retours à la ligne, et d'expansion des caractères.
De la manuel (en omettant les autres options):
Et
readarray
est juste un synonyme demapfile
.En bash,
$(<any_shell_cmd>)
aide à l'exécution de la commande et de la capture de la sortie. En la passant àIFS
avec\n
comme délimiteur permet de convertir un tableau.find
dans le tableau.Vous pouviez faire comme ceci:
Pour moi, cela a bien fonctionné sur cygwin:
Cela fonctionne avec des espaces, mais pas avec des guillemets doubles (") dans les noms de répertoire (qui ne sont pas autorisés dans un environnement Windows en tout cas).
Méfiez-vous de l'espace dans le printf option.