특정 패턴을 검색하는 데 필요한 약 1M 파일이 포함된 디렉토리가 있습니다. 나는 모든 파일에 대해 이 작업을 수행하는 방법을 알고 있습니다.
find /path/ -exec grep -H -m 1 'pattern' \{\} \;
전체 출력이 필요하지 않습니다(너무 느림). 처음 몇 번 클릭하면 효과가 있었으므로 행 수를 제한해 보았습니다.
find /path/ -exec grep -H -m 1 'pattern' \{\} \; | head -n 5
이렇게 하면 5개의 라인이 생성되고 그 다음에는
find: `grep' terminated by signal 13
그리고 find
계속 일하세요. 이건 설명하기 쉽죠여기. 나는 quit
행동을 시도했다:
find /path/ -exec grep -H -m 1 'pattern' \{\} \; -quit
이는 첫 번째 일치 항목만 출력합니다.
찾기 출력을 특정 수의 결과로 제한할 수 있습니까(예: quit
simple to 와 유사한 매개변수 제공 head -n
)?
답변1
-quit
이미 GNU 확장( , -H
, ) 을 사용하고 있으므로 GNU 옵션을 사용 하여 일치 항목을 찾는 즉시 출력하도록 -m1
할 수도 있습니다 . 따라서 일치 항목이 발견되면 SIGPIPE에 의해 종료될 가능성이 더 높습니다. 6번째 줄은 다음과 같습니다.grep
-r
--line-buffered
grep -rHm1 --line-buffered pattern /path | head -n 5
의 경우 find
다음을 수행해야 할 수도 있습니다.
find /path -type f -exec sh -c '
grep -Hm1 --line-buffered pattern "$@"
[ "$(kill -l "$?")" = PIPE ] && kill -s PIPE "$PPID"
' sh {} + | head -n 5
즉, 래핑하고 grep
( 여전히 가능한 적은 호출을 sh
실행하고 싶으 므로 그렇습니다), SIGPIPE로 인해 종료되면 해당 부모( )를 종료합니다 .grep
{} +
sh
find
grep
또 다른 접근 방식은 생성된 명령이 신호로 인해 종료될 때 즉시 xargs
as.exit를 사용하는 것입니다 -exec {} +
.xargs
find . -type f -print0 |
xargs -r0 grep -Hm1 --line-buffered pattern |
head -n 5
( -r
그리고 -0
GNU 확장입니다). grep
손상된 파이프에 쓰기가 수행 되면 둘 다 종료 grep
되고 다음에 무언가가 인쇄될 때도 자체적으로 종료됩니다. 달리면 더 빨리 일어날 수 있습니다.xargs
find
find
stdbuf -oL
POSIX 버전은 다음과 같습니다.
trap - PIPE # restore default SIGPIPE handler in case it was disabled
RE=pattern find /path -type f -exec sh -c '
for file do
awk '\''
$0 ~ ENVIRON["RE"] {
print FILENAME ": " $0
exit
}'\'' < "$file"
if [ "$(kill -l "$?")" = PIPE ]; then
kill -s PIPE "$PPID"
exit
fi
done' sh {} + | head -n 5
파일당 여러 명령을 실행하므로 매우 비효율적입니다.
답변2
오류를 방지하는 솔루션은 다음과 같습니다.
find / -type f -print0 \
| xargs -0 -L 1 grep -H -m 1 --line-buffered 2>/dev/null \
| head -10
이 예에서는 명령이 실패하면 xargs가 중지되므로 파이프 오류만 발생하며 이는 stderr 리디렉션에 의해 필터링됩니다.
답변3
grep
한 번에 하나의 파일을 작업합니다 . 귀하의 것을 사용하면 -quit
첫 번째 성공적인 grep에서 검색을 중지할 수 있습니다.
[업데이트] 내 첫 번째 솔루션은 한 번에 여러 파일을 grep하는 것이었습니다.
find /path/ -type f -exec grep -H -m 1 'pattern' \{\} + -quit | head -n 5
(마법은 하위 명령 끝에 . 을 추가하는 것입니다 +
. /path/에 여러 파일이 포함되어 있다고 확신하는 경우 이 옵션을 제거할 수 있습니다)-exec
-type f
-H
grep
@StéphaneChazelas가 보고한 대로 여기서 문제는 -exec
명령이 비동기식으로 실행되고 항상 첫 번째 파일에서 true
=> 종료를 반환한다는 것입니다.find
완료 시 중지 하려면 find
수신 중인 SIGPIPE도 수신해야 합니다(신호 13). 이는 파이프를 통해 무언가를 전송해야 함을 의미합니다.head
find
grep
find
다음은 Stéphane의 제안을 기반으로 향상된 빠르고 더러운 트릭입니다.
find /path/ -type f -exec grep -H -m 1 --line-buffered 'pattern' {} + -printf '\r' | head -n 5
출력을 무해한 문자로 -printf '\r'
강제하면 출력 이 변경되지 않기를 바랍니다 . 중지 되면 SIGPIPE가 수신되어 중지됩니다.find
grep
head
find
[업데이트 2] 나는 이것이 더러운 해킹이라고 경고했습니다. 더 나은 해결책은 다음과 같습니다.
find /path/ -type f -exec grep --quiet 'pattern' {} ";" -print | head -n 5
여기서는 더 이상 grep
파일 이름을 인쇄 하지 않지만 find
=> 더 이상 "grep은 신호 13에 의해 종료되었습니다" find
로 끝나지 않습니다 head
. 문제는 일치하는 행이 더 이상 인쇄되지 않는다는 것입니다 grep
.
[update3] 마지막으로 @Andrey가 제안한 것처럼 아래의 뻔뻔스럽고 추악한 명령은 마지막 문제를 해결할 것입니다.
find /path/ -type f \
-exec grep --quiet 'pattern' {} \; \
-printf '%p:' \
-exec grep -h -m 1 'pattern' {} \; \
| head -n 5`
답변4
더 간단한 경우 대체 경로는 파이프 대신 문자열일 수 있습니다. 예를 들어-
find . -exec stat -c %y {} \; | head -n1
위와 같은 문제를 보게 될 것입니다. 고려하는 간단한 방법 -
head -n1 <<<$(find . -exec stat -c %y {} \;)