Como parar o comando find após a primeira partida?

131

Existe uma maneira de forçar o findcomando a parar logo após encontrar a primeira correspondência?

coffeMug
fonte
4
The Unseen: A razão pela qual ele sai após cinco resultados quando canalizada para a cabeça -n 5 é porque a cabeça sai após cinco resultados. Quando a cabeça sai, o tubo se fecha e envia um sinal para o programa que o canaliza para terminar também. Desculpe por não responder diretamente a você, aparentemente você precisa de 50 reputação para responder.
Ruste

Respostas:

148

Com o GNU ou FreeBSD find, você pode usar o -quitpredicado:

find . ... -print -quit

O findequivalente ao NetBSD :

find . ... -print -exit

Se tudo o que você fizer é imprimir o nome e supondo que os nomes dos arquivos não contenham caracteres de nova linha, você poderá:

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

Isso não será interrompido findapós a primeira partida, mas possivelmente, dependendo do tempo e do buffer da segunda partida ou (muito) depois. Basicamente, findserá finalizado com um SIGPIPE quando ele tentar produzir algo enquanto headjá estiver ausente, porque já leu e exibiu a primeira linha de entrada.

Observe que nem todos os shells aguardam esse findcomando após o headretorno. As implementações de shell Bourne e AT&T de ksh(quando não interativas) e yash(somente se esse pipeline for o último comando em um script) não o deixariam em execução em segundo plano. Se você preferir ver esse comportamento em qualquer shell, sempre pode alterar o acima para:

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

Se você estiver fazendo mais do que imprimir os caminhos dos arquivos encontrados, tente esta abordagem:

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

(substitua printfpelo que você faria com esse arquivo).

Isso tem o efeito colateral de findretornar um status de saída, refletindo o fato de que ele foi morto.

Na verdade, o uso do sinal SIGPIPE em vez de SIGTERM (em kill -s PIPEvez de kill) fará com que alguns shells fiquem mais silenciosos sobre a morte (mas ainda retornariam um status de saída diferente de zero).

Stéphane Chazelas
fonte
3
Caso alguém precise testar se algum arquivo corresponde aos predicados, parando assim que um é encontrado, no Bash e no GNU Find você pode fazer: if [[ $(find ... -print -quit) ]]; then ...Ele apenas testa se há alguma coisa impressa.
Tobia
@Tobia É melhor colocar a $(…)parte entre aspas, caso você esteja usando apenas colchetes ( [ … ]).
Phd #
@phk Exceto que eu não estou usando os colchetes simples (porque são horríveis), então não preciso usar aspas.
213 Tobia
2
@Tobia, [é um comando padrão. Não é tanto esse comando que é horrível, mas a maneira como os projéteis tipo Bourne analisam as linhas de comando. [[...]]é uma construção do ksh que possui problemas próprios em vários shells. Por exemplo, até recentemente [[ $(...) ]], não funcionava zsh(você precisava [[ -n $(...) ]]). Exceto em zsh, você precisa de aspas [[ $a = $b ]], [[ =~ ]]há diferenças incompatíveis entre implementações e até mesmo entre versões para o bash e vários bugs em alguns. Pessoalmente, eu prefiro [.
Stéphane Chazelas
o que é ...? .
Kyb
11
find . -name something -print -quit

Encerra a busca após a primeira correspondência após a impressão.

Termine a localização após uma quantidade específica de correspondências e imprima os resultados:

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

Surpreendentemente - a cabeça agora termina a sequência após 5 partidas, embora eu não saiba como ou por quê.

É muito fácil testar. Basta encontrar uma pesquisa na raiz, o que resultaria em milhares, talvez até mais correspondências, levando pelo menos um minuto ou mais. Porém, quando canalizado para "head", "find" terminará após a quantidade especificada de linhas definida no head (o cabeçote padrão mostra 10, use "head -n" para especificar as linhas).

Observe que isso terminará depois que "head -n" atingir a contagem de caracteres de nova linha especificada e, portanto, qualquer correspondência que contenha vários caracteres de nova linha contará de acordo.

O invisível
fonte
Também observei que esse 'programa termina após o término do processo de saída', mas não de forma consistente entre os shells. Acho que isso merece sua própria pergunta - felizmente para bash, a resposta já está no comportamento Bash: Head & Tail do StackOverflow com script bash . Isso fornece informações suficientes para concluir que se o programa termina ou continua a ser executado em segundo plano depende de sua resposta ao SIGPIPE - matar é o padrão.
sage
Eu quis dizer 'across * programs * / shells', mas aparentemente o unix.stackexchange.com prefere que eu registre isso como um segundo comentário, em vez de me permitir editar meu primeiro comentário (essa é uma decisão de política específica do site de stackexchange). Além disso, agora vejo que @Ruste comentou para este efeito, na parte superior, o que não me ajudou inicialmente desde que eu fui direto para respostas ...
sage
2

Para fins de entretenimento, aqui está um gerador de busca preguiçoso no Bash. Este exemplo gera um anel sobre os arquivos no diretório atual. Leia o quanto você quiser kill %+(talvez apenas 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
fonte
1

grep também retorna se usado com o sinalizador -m, portanto, com

find stuff | grep -m1 .

ele retornará após a primeira linha impressa por localização.

A diferença entre isso e find stuff -print -quit | head -1é que, se a pesquisa for rápida o suficiente, o grep poderá não ser capaz de interromper o processo a tempo (embora isso não importe muito), enquanto que, se a pesquisa for longa, será possível encontrar muitas impressões desnecessárias. linhas.

isso funciona com o busybox find, embora, como o busybox grep também o tenha -m, não seja realmente necessário

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

isso emitirá uma mensagem sobre o processo de localização que recebeu o sinal (geralmente) sigterm, mas essa saída pertence ao shell em execução, não ao comando find, para que não mexa na saída do comando, o que significa que pipes ou redirecionamentos produzirão apenas a linha correspondido por encontrar.

desenterrar
fonte