Mise à jour 2020 pour les utilisateurs Linux:
Si vous avez une version de bash (4,4-alpha ou mieux) la mise à jour, comme vous le faites probablement si vous êtes sous Linux, alors vous devriez utiliser la réponse de Benjamin W. .
Si vous êtes sur Mac OS, qui - j'ai vérifié la dernière fois - utilisait encore bash 3.2, ou si vous utilisez un autre bash, passez à la section suivante.
Réponse pour bash 4.3 ou version antérieure
Voici une solution pour obtenir la sortie de find
dans un bash
tableau:
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 au script. La seule façon d'utiliser find
et de séparer en toute sécurité les noms de fichiers les uns des autres consiste à utiliser -print0
qui imprime les noms de fichiers séparés par un caractère nul. Ce ne serait pas très gênant si les fonctions readarray
/ mapfile
fonctions de bash supportaient les chaînes séparées par des valeurs nulles, mais ce n'est pas le cas. Bash le read
fait et cela nous conduit à la boucle ci-dessus.
[Cette réponse a été rédigée à l'origine en 2014. Si vous disposez d'une version récente de bash, veuillez consulter la mise à jour ci-dessous.]
Comment ça fonctionne
La première ligne crée un tableau vide: array=()
Chaque fois que l' read
instruction est exécutée, un nom de fichier séparé par des valeurs nulles est lu à partir de l'entrée standard. L' -r
option indique read
de laisser les caractères antislash seuls. Le -d $'\0'
indique read
que l'entrée sera séparée par des valeurs nulles. Puisque nous omettons le nom read
, le shell met l'entrée dans le nom par défaut: REPLY
.
L' array+=("$REPLY")
instruction ajoute le nouveau nom de fichier au tableau array
.
La dernière ligne combine la redirection et la substitution de commande pour fournir la sortie de find
à l'entrée standard de la while
boucle.
Pourquoi utiliser la substitution de processus?
Si nous n'utilisons pas de substitution de processus, la boucle pourrait être écrite comme suit:
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. Donc, au lieu de while
demander à la boucle de récupérer son stdin tmpfile
, nous pouvons lui demander de récupérer son stdin <(find . -name ${input} -print0)
.
La substitution de processus est très utile. Dans de nombreux endroits où une commande souhaite 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 où la commande veut écrire dans le fichier.
Comme les tableaux, la substitution de processus est une fonctionnalité de bash et d'autres shells avancés. Il ne fait pas partie du standard POSIX.
Alternative: lastpipe
Si vous le souhaitez, lastpipe
peut être utilisé à la place de la substitution de processus (chapeau: César ):
set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS= read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array
shopt -s lastpipe
dit à bash d'exécuter la dernière commande du pipeline dans le shell actuel (pas en arrière-plan). De cette façon, le array
reste en existence une fois le pipeline terminé. Parce lastpipe
que ne prend effet que si le contrôle des tâches est désactivé, nous exécutons set +m
. (Dans un script, contrairement à la ligne de commande, le contrôle des travaux est désactivé par défaut.)
Notes complémentaires
La commande suivante crée une variable shell, pas un tableau shell:
array=`find . -name "${input}"`
Si vous souhaitez créer un tableau, vous devez placer des parens autour de la sortie de find. Donc, naïvement, on pourrait:
array=(`find . -name "${input}"`)
Le problème est que le shell effectue la division des mots sur les résultats de find
afin que les éléments du tableau ne soient pas garantis comme vous le souhaitez.
Mise à jour 2019
À partir de la version 4.4-alpha, bash prend désormais en charge une -d
option pour que la boucle ci-dessus ne soit plus nécessaire. Au lieu de cela, on peut utiliser:
mapfile -d $'\0' array < <(find . -name "${input}" -print0)
Pour plus d' informations à ce sujet , s'il vous plaît voir (et upvote) la réponse de Benjamin W. .
IFS=
quand même éviter de supprimer les espaces au début ou à la fin des lignes d'entrée. Vous pouvez tester cela 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 peuvent être inhabituels, mais s'ils existent, nous voulons qu'ils soient traités correctement.<'
terminé <<(find aaa / -not -newermt "$ last_build_timestamp_v" -type f -print0) '''
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')
BLAH=$(find . -name '*.php')
. Comme discuté dans la réponse, cette approche fonctionnera dans des cas limités, mais elle ne fonctionnera pas en général avec tous les noms de fichiers et ne produira pas, comme l'OP s'y attendait, un tableau .Bash 4.4 a introduit une
-d
option pourreadarray
/mapfile
, donc cela peut maintenant être résolu avecreadarray -d '' array < <(find . -name "$input" -print0)
pour une méthode qui fonctionne avec des noms de fichiers arbitraires, y compris des espaces, des sauts de ligne et des caractères globuleux. Cela nécessite que vos
find
supports-print0
, comme par exemple GNU find le fait.À partir du manuel (en omettant les autres options):
Et
readarray
c'est juste un synonyme demapfile
.la source
Si vous utilisez
bash
4 ou une version ultérieure, vous pouvez remplacer votre utilisation defind
parshopt -s globstar nullglob array=( **/*"$input"* )
Le
**
modèle activé parglobstar
correspond à 0 ou plusieurs répertoires, permettant au modèle de correspondre à une profondeur arbitraire dans le répertoire courant. Sans l'nullglob
option, le modèle (après le développement des paramètres) est traité littéralement, donc sans correspondance, vous auriez un tableau avec une seule chaîne plutôt qu'un tableau vide.Ajoutez également l'
dotglob
option à la première ligne si vous souhaitez parcourir les répertoires cachés (comme.ssh
) et faire correspondre les fichiers cachés (comme.bashrc
).la source
nullglob
aussi…dotglob
soit défini (cela peut être souhaité ou non, mais cela vaut également la peine d'être mentionné).tu peux essayer quelque chose comme
, et pour imprimer les valeurs du tableau, vous pouvez essayer quelque chose comme echoarray=(`find . -type f | sort -r | head -2`)
"${array[*]}"
la source
Ce qui suit semble fonctionner à la fois pour Bash et Z Shell sur macOS.
#! /bin/sh IFS=$'\n' paths=($(find . -name "foo")) unset IFS printf "%s\n" "${paths[@]}"
la source
printf "%b" "file name with spaces, a star * ...\012and a second line\0" | xargs -0 touch
Dans bash,
$(<any_shell_cmd>)
permet d'exécuter une commande et de capturer la sortie. Passer ceci àIFS
avec\n
comme délimiteur permet de le convertir en tableau.IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")
la source
find
dans le tableau.Vous pourriez faire comme ceci:
#!/bin/bash echo "input : " read input echo "searching file with this pattern '${input}' under present directory" array=(`find . -name '*'${input}'*'`) for i in "${array[@]}" do : echo $i done
la source
Pour moi, cela a bien fonctionné sur cygwin:
declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")") for nm in "${names[@]}" do echo "$nm" done
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 de toute façon).
Faites attention à l'espace dans l'option -printf.
la source