Wie stoppe ich den Suchbefehl nach dem ersten Treffer?

131

Gibt es eine Möglichkeit, den findBefehl sofort nach dem Finden der ersten Übereinstimmung zum Stoppen zu zwingen ?

Kaffeebecher
quelle
4
TheUnseen: Der Grund dafür, dass es nach fünf Ergebnissen beendet wird, wenn es an head -n 5 weitergeleitet wird, ist, dass head nach fünf Ergebnissen beendet wird. Beim Verlassen des Kopfes wird die Pipe geschlossen und sendet ein Signal an das Programm, das die Pipe zum Beenden einleitet. Es tut uns leid, dass Sie nicht direkt geantwortet haben. Anscheinend benötigen Sie 50 Ruf, um zu antworten.
Ruste

Antworten:

148

Mit GNU oder FreeBSD findkönnen Sie das -quitPrädikat verwenden:

find . ... -print -quit

Das NetBSD- findÄquivalent:

find . ... -print -exit

Wenn Sie nur den Namen drucken und davon ausgehen, dass die Dateinamen keine Zeilenumbrüche enthalten, können Sie Folgendes tun:

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

Das wird nicht findnach dem ersten Match aufhören , sondern möglicherweise, je nach Timing und Pufferung nach dem zweiten Match oder (viel) später. Grundsätzlich findwird mit einem SIGPIPE beendet werden , wenn es um Ausgang etwas versucht , während headschon weg, da sie bereits gelesen und angezeigt , um die erste Zeile der Eingabe.

Beachten Sie, dass nicht alle Shells findnach headder Rückkehr auf diesen Befehl warten . Die Bourne-Shell- und AT & T-Implementierungen von ksh(wenn nicht interaktiv) und yash(nur wenn diese Pipeline der letzte Befehl in einem Skript ist) würden dies nicht tun, sodass sie im Hintergrund ausgeführt würden. Wenn Sie dieses Verhalten lieber in einer Shell sehen möchten, können Sie das oben stehende jederzeit ändern in:

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

Wenn Sie mehr tun als nur die Pfade der gefundenen Dateien zu drucken, können Sie diesen Ansatz ausprobieren:

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

(Ersetzen printfSie sie durch das, was Sie mit dieser Datei tun würden.)

Dies hat den Nebeneffekt find, dass ein Exit-Status zurückgegeben wird, der die Tatsache widerspiegelt, dass er getötet wurde.

Tatsächlich führt die Verwendung des Signals SIGPIPE anstelle von SIGTERM ( kill -s PIPEanstelle von kill) dazu, dass einige Shells über diesen Tod leiser sind (aber dennoch einen Ausgangsstatus ungleich Null zurückgeben würden).

Stéphane Chazelas
quelle
3
Für den Fall, dass jemand testen muss, ob eine Datei mit den Prädikaten übereinstimmt, können if [[ $(find ... -print -quit) ]]; then ...Sie in Bash und GNU Find Folgendes tun: Es wird nur getestet, ob der Ausdruck überhaupt gefunden wird.
Tobia
@Tobia Besser, das $(…)Teil in Anführungszeichen zu setzen, wenn Sie nur die einzelnen Klammern verwenden ( [ … ]).
Phk
@phk Außer ich benutze nicht die einzelnen Klammern (weil sie schrecklich sind), deshalb brauche ich keine Anführungszeichen.
Tobia
2
@Tobia [ist ein Standardbefehl. Es ist nicht so sehr dieser Befehl, der schrecklich ist, sondern die Art und Weise, wie Bourne-ähnliche Shells Befehlszeilen analysieren. [[...]]ist ein ksh-Konstrukt, das in verschiedenen Shells eigene Probleme hat. Zum Beispiel, bis vor kurzem [[ $(...) ]]würde nicht funktionieren zsh(Sie brauchten [[ -n $(...) ]]). Abgesehen davon zsh, dass Sie Anführungszeichen in benötigen [[ $a = $b ]], [[ =~ ]]weist das inkompatible Unterschiede zwischen Implementierungen und sogar zwischen Versionen für Bash und einigen Bugs auf. Persönlich bevorzuge ich [.
Stéphane Chazelas
was ist ...? .
kyb
11
find . -name something -print -quit

Beendet die Suche nach dem ersten Treffer nach dem Drucken.

Suche nach einer bestimmten Anzahl von Übereinstimmungen beenden und Ergebnisse ausdrucken:

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

Überraschenderweise beendet head die Zeichenfolge nach 5 Übereinstimmungen, obwohl ich nicht weiß, wie oder warum.

Es ist sehr einfach zu testen. Lassen Sie finden suchen ein auf Wurzel , die Tausende, vielleicht sogar mehr Spiele während der Einnahme von mindestens einer Minute oder mehr zur Folge hätte. Wenn "find" in "head" umgeleitet wird, wird "find" nach der angegebenen Anzahl von Zeilen, die in head definiert sind, beendet (Standardeinstellung head zeigt 10, verwenden Sie "head -n", um Zeilen anzugeben).

Beachten Sie, dass dies beendet wird, nachdem "head -n" die angegebene Anzahl von Zeilenvorschubzeichen erreicht hat. Daher zählt jede Übereinstimmung, die mehrere Zeilenvorschubzeichen enthält, entsprechend.

Das ungesehene
quelle
Ich habe auch beobachtet, dass dieses "Programm endet, nachdem der Kopf mit seiner Ausgabe fertig ist" -Phänomen, aber nicht konsistent über die Shells hinweg. Ich denke, dass dies eine eigene Frage verdient - zum Glück für Bash ist die Antwort bereits bei StackOverflows Bash: Head & Tail-Verhalten mit Bash-Skript . Das gibt mir genügend Informationen, um zu dem Schluss zu kommen, dass es von der Reaktion auf SIGPIPE abhängt, ob das Programm im Hintergrund beendet wird oder weiter ausgeführt wird - das Töten ist die Standardeinstellung.
Salbei
Ich meinte "programmübergreifend" / "shells", aber anscheinend würde unix.stackexchange.com es vorziehen, dass ich dies als zweiten Kommentar protokolliere, anstatt meinen ersten Kommentar bearbeiten zu lassen (dies ist eine stapelaustauschspezifische Richtlinienentscheidung). Außerdem sehe ich jetzt, dass @Ruste diesen Effekt oben kommentiert hat, was mir anfangs nicht weitergeholfen hat, da ich direkt zu den Antworten gegangen bin ...
Salbei,
2

Zu Unterhaltungszwecken gibt es hier einen Lazy-Find-Generator in Bash. In diesem Beispiel wird ein Ring über die Dateien im aktuellen Verzeichnis generiert. Lies so viele du willst kill %+(vielleicht nur 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
ormaaj
quelle
1

grep gibt auch zurück, wenn es mit dem Flag verwendet wird -m, also mit

find stuff | grep -m1 .

Es wird nach der ersten von find ausgegebenen Zeile zurückgegeben.

Der Unterschied zwischen diesem und diesem find stuff -print -quit | head -1ist, dass grep, wenn die Suche schnell genug ist, möglicherweise nicht in der Lage ist, den Prozess rechtzeitig anzuhalten (obwohl dies nicht wirklich wichtig ist), während es bei einer langen Suche Zeit spart, eine Menge nicht benötigter Dokumente zu drucken Linien.

dies funktioniert stattdessen mit busybox find, obwohl da busybox grep auch -mnicht wirklich benötigt wird

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

Dies gibt eine Nachricht darüber aus, dass der Suchvorgang das (normalerweise) sigterm-Signal empfangen hat, aber diese Ausgabe gehört zur laufenden Shell, nicht zum find-Befehl, sodass sie nicht mit der Befehlsausgabe in Konflikt gerät, was bedeutet, dass Pipes oder Umleitungen nur die Zeile ausgeben von find abgestimmt.

untore
quelle