Как остановить команду поиска после первого матча?


131

Есть ли способ заставить findкоманду остановиться сразу после нахождения первого совпадения?


4
TheUnseen: Причина, по которой он выходит после пяти результатов при передаче по каналу -n 5, заключается в том, что голова выходит после пяти результатов. Когда головка выходит, труба закрывается и посылает сигнал программе, которая также завершает работу. Извините, что не отвечаете непосредственно вам, очевидно, вам нужно 50 репутации, чтобы ответить.
Руст

Ответы:


148

В GNU или FreeBSD findвы можете использовать -quitпредикат:

find . ... -print -quit

NetBSD findэквивалент:

find . ... -print -exit

Если все, что вы делаете, это печатаете имя и предполагаете, что имена файлов не содержат символов новой строки, вы можете сделать:

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

Это не остановится findпосле первого матча, но, возможно, в зависимости от времени и буферизации после второго матча или (намного) позже. По сути, findбудет завершено с SIGPIPE, когда он попытается вывести что-то, когда headуже нет, потому что он уже прочитал и отобразил первую строку ввода.

Обратите внимание, что не все оболочки будут ожидать этой findкоманды после headее возвращения. Реализация оболочки Bourne и AT & T ksh(если она не является интерактивной) и yash(только если этот конвейер является последней командой в сценарии) не будут выполняться, оставляя ее работающей в фоновом режиме. Если вы предпочитаете видеть такое поведение в любой оболочке, вы всегда можете изменить приведенное выше:

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

Если вы делаете больше, чем просто распечатываете пути найденных файлов, вы можете попробовать этот подход:

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

(замените тем, printfчто вы будете делать с этим файлом).

У этого есть побочный эффект findвозвращения статуса выхода, отражающего факт, что это было убито все же.

На самом деле, использование сигнала SIGPIPE вместо SIGTERM ( kill -s PIPEвместо kill) приведет к тому, что некоторые оболочки будут более молчаливы об этой смерти (но все равно вернут ненулевой статус выхода).


3
В случае, если кому-то нужно проверить, соответствует ли какой-либо файл предикатам, и остановится, как только он будет найден, в Bash и GNU Find вы можете сделать следующее: if [[ $(find ... -print -quit) ]]; then ...он просто проверяет, найдено ли что-либо напечатанное вообще.
Tobia

@Tobia Лучше поместить $(…)деталь в кавычки, если вы используете только одинарные скобки ( [ … ]).
phk

@phk За исключением того, что я не использую одиночные скобки (потому что они ужасны), поэтому мне не нужно использовать кавычки.
Tobia

2
@Tobia, [это стандартная команда. Это не столько ужасная команда, сколько способ, которым борновые оболочки разбирают командные строки. [[...]]это конструкция ksh, которая имеет свои проблемы в различных оболочках. Например, до недавнего времени [[ $(...) ]]не работал в zsh(вам нужно [[ -n $(...) ]]). За исключением того zsh, что вам нужны кавычки [[ $a = $b ]], у них [[ =~ ]]есть несовместимые различия между реализациями и даже между версиями для bash, и некоторые ошибки в некоторых. Лично я предпочитаю [.
Стефан

что такое ...? ,
kyb

11
find . -name something -print -quit

Завершает поиск после первого совпадения после его печати.

Завершить поиск после определенного количества совпадений и распечатать результаты:

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

Как ни странно - голова теперь завершает строку после 5 совпадений, хотя я не знаю, как и почему.

Это очень легко проверить. Просто дайте как найти на корню , который приведет тысячи, может быть , даже больше матчей, принимая по крайней мере минуту или больше. Но когда по трубопроводу в «голову» «поиск» прекратится после указанного количества строк, определенных в заголовке (заголовок по умолчанию показывает 10, используйте «заголовок -n» для указания строк).

Обратите внимание, что это прекратится после того, как "head -n" достигнет указанного количества символов новой строки, и, следовательно, любое совпадение, содержащее несколько символов новой строки, будет учитываться соответствующим образом.


Я также наблюдал, что это «программа завершается после того, как голова покончила с выходным сигналом», но не всегда в разных оболочках. Я думаю, что это заслуживает отдельного вопроса - к счастью, для bash ответ уже находится в поведении StackOverflow Bash: Head & Tail со сценарием bash . Это дает мне достаточно информации, чтобы прийти к выводу, что от того, будет ли программа завершена или продолжает выполняться в фоновом режиме, зависит ее ответ на SIGPIPE - по умолчанию уничтожение.
Мудрец

Я имел в виду «между * программами * / оболочками», но, очевидно, unix.stackexchange.com предпочел бы, чтобы я записывал это как второй комментарий, а не разрешал мне редактировать свой первый комментарий (это стек-обмен, решение для конкретной политики сайта). Кроме того, теперь я вижу, что @Ruste прокомментировал этот эффект сверху, что изначально не помогло мне, так как я пошел прямо к ответам ...
мудрец

2

Для развлекательных целей, вот ленивый генератор поиска в Bash. В этом примере создается кольцо для файлов в текущем каталоге. Прочитайте сколько хотите тогда kill %+(возможно только 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 также возвращает, если используется с флагом -m, поэтому с

find stuff | grep -m1 .

он вернется после первой строки, напечатанной функцией find.

Разница между этим и тем find stuff -print -quit | head -1, что если поиск выполняется достаточно быстро, grep может не успеть вовремя остановить процесс (хотя это и не имеет значения), в то время как если поиск будет продолжительным, он найдет для печати много ненужного. линий.

вместо этого он работает с сервисом busybox find, хотя, так как grep busybox также -mне нужен

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

это выдаст сообщение о том, что процесс поиска получил (обычно) сигнал sigterm, но этот вывод принадлежит запущенной оболочке, а не команде find, поэтому он не связывается с выводом команды, то есть каналы или перенаправления будут выводить только строку соответствует найти.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.