¿Cómo detener el comando de búsqueda después del primer partido?

131

¿Hay alguna forma de obligar al findcomando a detenerse justo después de encontrar el primer partido?

taza de cafe
fuente
44
TheUnseen: la razón por la que se cierra después de cinco resultados cuando se canaliza a head -n 5 es porque head sale después de cinco resultados. Cuando la cabeza sale, la tubería se cierra y envía una señal al programa para que la tubería termine también. Perdón por no responderte directamente, aparentemente necesitas 50 reputación para responder.
Ruste

Respuestas:

148

Con GNU o FreeBSD find, puede usar el -quitpredicado:

find . ... -print -quit

El findequivalente de NetBSD :

find . ... -print -exit

Si todo lo que hace es imprimir el nombre, y suponiendo que los nombres de los archivos no contienen caracteres de nueva línea, puede hacer lo siguiente:

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

Eso no se detendrá finddespués del primer partido, pero posiblemente, dependiendo del momento y el almacenamiento en el segundo partido o (mucho) más tarde. Básicamente, findterminará con un SIGPIPE cuando intente generar algo mientras headya no está, porque ya ha leído y mostrado la primera línea de entrada.

Tenga en cuenta que no todos los shells esperarán ese findcomando después de que headhaya regresado. Las implementaciones de shell Bourne y AT&T de ksh(cuando no son interactivas) y yash(solo si esa canalización es el último comando en un script) no lo harían, dejándolo ejecutándose en segundo plano. Si prefiere ver ese comportamiento en cualquier shell, siempre puede cambiar lo anterior a:

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

Si está haciendo más que imprimir las rutas de los archivos encontrados, puede probar este enfoque:

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

(reemplace printfcon lo que sea que esté haciendo con ese archivo).

Eso tiene el efecto secundario de finddevolver un estado de salida que refleja el hecho de que fue asesinado

En realidad, el uso de la señal SIGPIPE en lugar de SIGTERM (en kill -s PIPElugar de kill) hará que algunos proyectiles sean más silenciosos sobre esa muerte (pero aún devolvería un estado de salida distinto de cero).

Stéphane Chazelas
fuente
3
En caso de que alguien necesite probar si algún archivo coincide con los predicados, parando tan pronto como se encuentre uno, en Bash y GNU Find puedes hacer: if [[ $(find ... -print -quit) ]]; then ...Solo prueba si find imprimió algo.
Tobia
@Tobia Es mejor poner la $(…)parte entre comillas en caso de que esté usando solo los corchetes ( [ … ]).
phk
@phk Excepto que no estoy usando los corchetes simples (porque son horribles), así que no necesito usar comillas.
Tobia
2
@Tobia, [es un comando estándar. No es tanto ese comando lo que es horrible, sino la forma en que los shells tipo Bourne analizan las líneas de comando. [[...]]es una construcción ksh que tiene problemas propios en varios shells. Por ejemplo, hasta hace poco [[ $(...) ]]no funcionaría zsh(lo necesitabas [[ -n $(...) ]]). Excepto en zsh, necesita comillas [[ $a = $b ]], [[ =~ ]]tiene diferencias incompatibles entre implementaciones e incluso entre versiones para bash y varios errores en algunos. Personalmente, lo prefiero [.
Stéphane Chazelas
lo que es ...? .
kyb
11
find . -name something -print -quit

Termina la búsqueda después de la primera coincidencia después de imprimirla.

Termine la búsqueda después de una cantidad específica de coincidencias y resultados de impresión:

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

Sorprendentemente, la cabeza ahora termina la secuencia después de 5 partidos, aunque no sé cómo ni por qué.

Es muy fácil de probar. Solo deje que encuentre una búsqueda en la raíz que generaría miles, tal vez incluso más coincidencias mientras toma al menos un minuto o más. Pero cuando se canaliza a "head", "find" terminará después de la cantidad especificada de líneas definidas en head (el encabezado predeterminado muestra 10, use "head -n" para especificar líneas).

Tenga en cuenta que esto terminará después de que "head -n" alcance el recuento de caracteres de nueva línea especificado y, por lo tanto, cualquier coincidencia que contenga varios caracteres de nueva línea contará en consecuencia.

Lo nunca visto
fuente
También he observado que este 'programa finaliza después de que el cabezal termina con su salida', pero no de manera consistente en todos los shells. Creo que esto merece su propia pregunta: afortunadamente para bash, la respuesta ya está en el comportamiento Bash: Head & Tail de StackOverflow con script bash . Eso me da suficiente información para concluir que si el programa finaliza o continúa ejecutándose en segundo plano depende de su respuesta a SIGPIPE, el asesinato es el valor predeterminado.
sabio
Quise decir 'a través de * programas * / shells', pero aparentemente unix.stackexchange.com preferiría registrar esto como un segundo comentario en lugar de permitirme editar mi primer comentario (esta es una decisión de política de intercambio de pila, específica del sitio). Además, ahora veo que @Ruste comentó al respecto en la parte superior, lo que no me ayudó inicialmente ya que fui directamente a las respuestas ...
sabio
2

Para fines de entretenimiento, aquí hay un generador de búsqueda perezosa en Bash. Este ejemplo genera un anillo sobre los archivos en el directorio actual. Lee cuantos quieras entonces kill %+(tal vez 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
ormaaj
fuente
1

grep también regresa si se usa con la bandera -m, entonces con

find stuff | grep -m1 .

regresará después de la primera línea impresa por find.

La diferencia entre esto y esto find stuff -print -quit | head -1es que si la búsqueda es lo suficientemente rápida, grep podría no ser capaz de detener el proceso a tiempo (sin embargo, en realidad no importa), mientras que si la búsqueda es larga, no tendrá que imprimir mucho. líneas.

en cambio, esto funciona con busybox find, aunque como busybox grep también lo tiene, -mno es realmente necesario

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

esto emitirá un mensaje sobre el proceso de búsqueda que ha recibido la señal (generalmente) sigterm, pero esta salida pertenece al shell en ejecución, no al comando de búsqueda, por lo que no se mete con la salida del comando, lo que significa que las tuberías o redireccionamientos generarán solo la línea emparejado por encontrar.

deshacer
fuente