Come fermare il comando find dopo la prima partita?


131

C'è un modo per forzare l' findarresto del comando subito dopo aver trovato la prima partita?


4
TheUnseen: il motivo per cui si chiude dopo cinque risultati quando viene reindirizzato a head -n 5 è perché head esce dopo cinque risultati. Quando la testa esce, il tubo si chiude e invia un segnale anche al tubo del programma in esso per terminare. Ci dispiace per non aver risposto direttamente a te, a quanto pare hai bisogno di 50 reputazione per rispondere.
Ruste,

Risposte:


148

Con GNU o FreeBSD find, puoi usare il -quitpredicato:

find . ... -print -quit

L' findequivalente NetBSD :

find . ... -print -exit

Se tutto ciò che fai è stampare il nome e supponendo che i nomi dei file non contengano caratteri di nuova riga, potresti fare:

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

Ciò non si interromperà finddopo la prima partita, ma probabilmente, a seconda dei tempi e del buffering della seconda partita o (molto) in seguito. Fondamentalmente, findverrà terminato con un SIGPIPE quando tenta di emettere qualcosa mentre headè già sparito perché ha già letto e visualizzato la prima riga di input.

Nota che non tutte le shell aspetteranno quel findcomando dopo che headè tornato. Le implementazioni della shell Bourne e AT&T di ksh(quando non interattive) e yash(solo se quella pipeline è l'ultimo comando in uno script) non lo farebbero, lasciandola in esecuzione in background. Se preferisci vedere quel comportamento in qualsiasi shell, puoi sempre cambiare quanto sopra in:

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

Se stai facendo qualcosa di più della stampa dei percorsi dei file trovati, potresti provare questo approccio:

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

(sostituiscilo printfcon qualunque cosa faresti con quel file).

Ciò ha l'effetto collaterale di findrestituire uno stato di uscita che riflette il fatto che è stato ucciso.

In realtà, l'uso del segnale SIGPIPE invece di SIGTERM ( kill -s PIPEanziché kill) farà sì che alcune shell rimangano più silenziose su quella morte (ma restituirebbero comunque uno stato di uscita diverso da zero).


3
Nel caso in cui qualcuno abbia bisogno di verificare se un file corrisponde ai predicati, fermandosi non appena ne viene trovato uno, in Bash e GNU Find puoi fare: if [[ $(find ... -print -quit) ]]; then ...Verifica semplicemente se trova qualcosa stampato.
Tobia,

@Tobia Meglio mettere la $(…)parte tra virgolette nel caso in cui si utilizzino solo le parentesi singole ( [ … ]).
phk,

@phk Tranne che non sto usando le parentesi singole (perché sono orribili) quindi non ho bisogno di usare le virgolette.
Tobia,

2
@Tobia, [è un comando standard. Non è tanto orribile quel comando, ma il modo in cui le conchiglie tipo Bourne analizzano le linee di comando. [[...]]è un costrutto ksh che ha problemi propri in varie shell. Ad esempio, fino a poco tempo fa [[ $(...) ]]non funzionava zsh(necessario [[ -n $(...) ]]). Tranne in zsh, hai bisogno di virgolette [[ $a = $b ]], le [[ =~ ]]differenze sono incompatibili tra le implementazioni e persino tra le versioni per bash e alcuni bug in alcuni. Personalmente, preferisco [.
Stéphane Chazelas,

che cos'è ...? .
Kyb,

11
find . -name something -print -quit

Termina la ricerca dopo la prima corrispondenza dopo averla stampata.

Termina la ricerca dopo un determinato numero di corrispondenze e stampa i risultati:

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

Abbastanza sorprendentemente, la testa ora termina la stringa dopo 5 partite, anche se non so come o perché.

È molto facile da testare. Basta trovare la ricerca a su root che porterebbe a migliaia, forse anche più corrispondenze impiegando almeno un minuto o più. Ma quando convogliato in "head" "find" termina dopo la quantità specificata di righe definite in head (default head mostra 10, usa "head -n" per specificare le righe).

Si noti che ciò terminerà dopo che "head -n" avrà raggiunto il conteggio dei caratteri di nuova riga specificato e pertanto qualsiasi corrispondenza che contiene più caratteri di nuova riga verrà conteggiata di conseguenza.


Ho anche osservato che questo "programma termina dopo che la testa ha finito con il suo output" fenomeno, ma non coerentemente tra le shell. Penso che questo meriti una sua domanda: fortunatamente per bash la risposta è già al comportamento Bash: Head & Tail di StackOverflow con script bash . Questo mi dà abbastanza informazioni per concludere che se il programma termina o continua a essere eseguito in background dipende dalla sua risposta a SIGPIPE - uccidere è l'impostazione predefinita.
saggio

Intendevo "attraverso * programmi * / shells", ma apparentemente unix.stackexchange.com preferirei che lo registrassi come secondo commento piuttosto che permettermi di modificare il mio primo commento (si tratta di uno scambio di stack, una decisione politica specifica del sito). Inoltre, ora vedo che @Ruste ha commentato questo effetto in alto, il che non mi ha aiutato inizialmente da quando sono andato direttamente alle risposte ...
saggio

2

A scopo di intrattenimento, ecco un generatore di ricerca pigro in Bash. Questo esempio genera un anello sui file nella directory corrente. Leggi quanti ne vuoi allora kill %+(forse solo 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

1

grep ritorna anche se usato con la bandiera -m, quindi con

find stuff | grep -m1 .

tornerà dopo la prima riga stampata da find.

La differenza tra questo ed find stuff -print -quit | head -1è che se la ricerca è abbastanza veloce grep potrebbe non essere in grado di arrestare il processo in tempo (non importa davvero però), mentre se la ricerca è lunga risparmierà di stampare molto non necessario Linee.

questo invece funziona con la ricerca di busybox, anche se dal momento che busybox grep ha anche -mnon è davvero necessario

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

questo sputerà un messaggio sul processo find che ha ricevuto il segnale (di solito) sigterm, ma questo output appartiene alla shell in esecuzione, non al comando find, quindi non confonde con l'output del comando, il che significa che pipe o reindirizzamenti produrranno solo la linea abbinato da trova.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.