web-dev-qa-db-fra.com

Comment arrêter la commande find après le premier match?

Existe-t-il un moyen de forcer la commande find à s'arrêter juste après avoir trouvé la première correspondance?

138
coffeMug

Avec GNU ou FreeBSD find, vous pouvez utiliser le -quit prédicat:

find . ... -print -quit

L'équivalent NetBSD find:

find . ... -print -exit

Si tout ce que vous faites est d'imprimer le nom et en supposant que les noms de fichiers ne contiennent pas de caractères de nouvelle ligne, vous pouvez faire:

find . ... -print | head -n 1

Cela n'arrêtera pas find après la première correspondance, mais peut-être, selon le timing et la mise en mémoire tampon lors de la deuxième correspondance ou (beaucoup) plus tard. Fondamentalement, find se terminera par un SIGPIPE lorsqu'il essaiera de sortir quelque chose alors que head sera déjà parti car il a déjà lu et affiché la première ligne d'entrée.

Notez que tous les shells n'attendront pas cette commande find après le retour de head. Les implémentations Bourne Shell et AT&T de ksh (lorsqu'elles ne sont pas interactives) et yash (uniquement si ce pipeline est la dernière commande d'un script) ne le feraient pas, le laissant s'exécuter en arrière-plan. Si vous préférez voir ce comportement dans n'importe quel shell, vous pouvez toujours changer ce qui précède en:

(find . ... -print &) | head -n 1

Si vous faites plus qu'imprimer les chemins des fichiers trouvés, vous pouvez essayer cette approche:

find . ... -exec sh -c 'printf "%s\n" "$1"; kill "$PPID"' sh {} \;

(remplacez printf par tout ce que vous feriez avec ce fichier).

Cela a pour effet secondaire de find de renvoyer un statut de sortie reflétant le fait qu'il a été tué cependant.

En fait, en utilisant le signal SIGPIPE au lieu de SIGTERM (kill -s PIPE au lieu de kill) fera que certains shells seront plus silencieux sur cette mort (mais retourneront toujours un état de sortie différent de zéro).

156
Stéphane Chazelas
find . -name something -print -quit

Met fin à la recherche après la première correspondance après l'avoir imprimée.

Mettre fin à la recherche après un nombre spécifique de correspondances et imprimer les résultats:

find . -name something -print | head -n 5

Étonnamment, head termine désormais la chaîne après 5 matchs, mais je ne sais pas comment ni pourquoi.

C'est très facile à tester. Laissez simplement rechercher a à la racine, ce qui entraînerait des milliers, voire plus de correspondances tout en prenant au moins une minute ou plus. Mais lorsqu'il est canalisé dans "head", "find" se terminera après la quantité spécifiée de lignes définie dans head (la tête par défaut affiche 10, utilisez "head -n" pour spécifier les lignes).

Notez que cela se terminera après que "head -n" atteindra le nombre de caractères de nouvelle ligne spécifié et donc toute correspondance contenant plusieurs caractères de nouvelle ligne comptera en conséquence.

12
TheUnseen

À des fins de divertissement, voici un générateur de recherche paresseux dans Bash. Cet exemple génère un anneau sur les fichiers du répertoire en cours. Lisez autant que vous voulez, puis kill %+ (peut-être juste 1)

#!/usr/bin/env bash
unset -v files n
trap 'kill "$x_PID"' EXIT

coproc x while :; do
    find . -type f -maxdepth 1 -exec sh -c "$(</dev/fd/3)" _ {} +
done 4<&0 <<\EOF 3<&0 <&4-
for x; do
    read -r _
    printf '%s\0' "$x"
done
EOF

while
    echo >&${x[1]}
    IFS= read -rd '' -u "$x" 'files[n++]'
do
    printf '%q ' "${files[@]}"
    echo
    sleep .2
done
2
ormaaj

grep renvoie également s'il est utilisé avec le drapeau -m, donc avec

find stuff | grep -m1 .

il reviendra après la première ligne imprimée par find.

La différence entre ceci et find stuff -print -quit | head -1 est que si la recherche est assez rapide, grep pourrait ne pas être en mesure d'arrêter le processus à temps (peu importe cependant), tandis que si la recherche est longue, il épargnera à find d'imprimer un grand nombre de lignes inutiles.

cela fonctionne à la place avec la recherche de busybox, bien que depuis grep de busybox a également -m ce n'est pas vraiment nécessaire

find /tmp/stuff -exec "sh" "-c" "eval 'echo {}; { kill \$PPID; }'" \;

cela va cracher un message sur le processus de recherche ayant reçu le signal sigterm (généralement), mais cette sortie appartient au shell en cours d'exécution, pas à la commande find, donc elle ne dérange pas avec la sortie de la commande, ce qui signifie que les tuyaux ou les redirections ne produiront que la ligne correspond à trouver.

1
untore