find를 사용하여 처음 몇 개의 일치하는 파일만 찾으세요.

find를 사용하여 처음 몇 개의 일치하는 파일만 찾으세요.

*.txt한 디렉토리에 수백 개의 파일이 있다고 가정합니다. 처음 세 개의 파일을 찾은 *.txt다음 검색 프로세스를 종료하고 싶습니다.

이 유틸리티를 사용하여 어떻게 이를 달성할 수 있습니까 find? 매뉴얼 페이지를 잠깐 살펴보았지만 옵션이 표시되지 않았습니다.

답변1

find출력을 파이프할 수 있습니다 head.

find . -name '*.txt' | head -n 3

답변2

이 다른 답변몇 가지 결함이 있습니다. 명령은

find . -name '*.txt' | head -n 3

그럼 설명이 나와요댓글 중 하나에[강조 내]:

head파이프의 왼쪽에서 시작하고 입력을 기다립니다. 그런 다음 find지정된 기준과 일치하는 파일을 시작하고 검색하여 파이프를 통해 출력을 보냅니다. head요청된 라인 수만큼 수신되어 인쇄 되면 파이프를 닫고 종료됩니다.find닫힌 파이프를 확인하고 파이프도 종료됩니다.심플하고 우아한,효율적인.

이것은거의진짜.

문제는 find닫힌 파이프가 쓰기를 시도할 때만 인식된다는 것입니다. 이 경우에는 네 번째 일치 항목이 발견될 때입니다. 하지만 4차전이 없으면 find진행됩니다. 당신의 껍질이 기다리고 있을 거예요!스크립트에서 이런 일이 발생하면 파이프 출력이 최종이고 아무것도 추가할 수 없다는 것을 이미 알고 있더라도 스크립트는 기다릴 것입니다. 효율성이 낮습니다.

find이 특정 작업 자체가 빠르게 완료되지만 큰 파일 트리에서 복잡한 검색을 수행하는 경우 다음에 수행할 작업을 불필요하게 지연시켜 이 명령의 효과가 무시될 수 있습니다.

덜 완벽한 솔루션은 다음을 실행하는 것입니다.

( find … & ) | head -n 3

이 방법으로 종료하면 head쉘이 즉시 계속됩니다. 백그라운드 find프로세스는 무시되거나(조만간 종료됨) pkill다른 방식으로 대상이 지정될 수 있습니다.


개념을 증명하기 위해 검색할 수 있습니다 /. 우리는 단 한 번의 경기만 예상하지만 find여기저기 알아보고 있기 때문에 시간이 많이 걸릴 수 있습니다.

find / -name / 2>/dev/null | head -n 1

문제가 발견되면 즉시 Ctrl+를 사용하여 종료하세요. C이제 비교해보세요:

pidof find ; ( find / -name / 2>/dev/null & ) | head -n 1 ; pidof find

더 나은 해결책은 다음과 같습니다.

yes | head -n 2 \
| find … -print -exec sh -c '
   read dummy || kill -s PIPE "$PPID"
' find-sh \;

노트:

  • 여기서는 일치하는 3개의 파일을 원하지만 head -n 2(가 아님 head -n 3)을 사용합니다. 세 번째 일치 파일 이후에는 read표준 입력에서 입력을 찾을 수 없으며 kill종료됩니다 find. 를 사용하면 네 번째 파일 이후에 실행됩니다 head -n 3.kill

  • 신호는 입니다 SIGPIPE. kill -s INT …또한 작동해야합니다. 가장 간단한 해법()으로 SIGPIPE끝나는 신호이기 때문에 특별히 선택했습니다.findfind … | head -n 3

  • 3개의 파일이 필요한 경우 일치하는 각 파일에 대해 하나씩 실행하는 것은 sh무시할 수 있습니다. 기억하세요, 목표는 find백그라운드에서 헛되이 실행되는 상황(제가 "완벽하지 않은 솔루션"이라고 부르는 것)을 피하는 것입니다. 운영 체제의 전반적인 성능에 있어서 수명이 짧은 몇 개의 쉘보다 더 중요한 것은 없습니다. " 더 이상 사용되지 않습니다. " find파일 시스템을 탐색하는 것이 더 좋습니다. 그러나 (최대) 1000개의 파일을 원하고 find더 일찍 파일이 부족할 가능성이 있는 경우(그래서 우리는 아마도 어떤 문제도 피하고 싶지 않을 것입니다) 이러한 쉘은 책임이 있습니다.

    다음 코드는 프로세스 수를 줄 sh였지만 결함이 있다고 생각합니다.

    # flawed, DO NOT USE
    yes | head -n 999 \
    | find … -exec sh -c '
       for pathname do
          printf "%s\\n" "$pathname"
          read dummy || { kill -s PIPE "$PPID"; exit 0; }
       done
    ' find-sh {} +
    

    -print(쉘 코드 외부에서)를 (쉘 코드 내부에서)로 바꿔야 합니다 printf …. 그 이유는 너무 많은 경로명이 -print앞에 -exec sh … {} +인쇄될 수 있기 때문입니다.

    잠재적인 문제가 발생합니다. 모든 사람이 printf별도의 프로세스를 생성하면 이 "최적화"가 의미가 없게 됩니다. 다행스럽게도 거의(?) 모두 sh printf내장되어 있습니다.

    그러나 진짜 결점은 exec sh … {} +경로명을 넘겨주기 전에 가능한 한 많은 경로명을 기다린다는 것입니다 sh. 한편으로 이것이 바로 sh프로세스 수를 줄이는 것입니다. 반면에 1000번째 일치 항목이 대기열에 추가되면 find1001번째 항목에 대한 검색이 계속되고 1001번째 일치 항목이 발견되면 더 많은 항목이 검색될 수 있다는 것이 거의 확실합니다. 이 경우 1001번째 일치가 종료되므로 find … | head -n 1000이 결함이 있는 솔루션은 가장 간단한 솔루션보다 더 나쁘므로 사용하지 마십시오.

  • find … | head -n 3가장 간단한 해결책()은 인쇄된 경로 이름 중 하나에 개행 문자가 있으면 잘못 계산됩니다. null로 끝나는 문자열을 원할 경우 가장 간단한 해결책은 다음과 같습니다 . 즉, 이 이식 불가능한 옵션을 지원 find … -print0 | head -z -n 3해야 합니다 . 우리의 최적화된 솔루션에서는 쉘 코드 에서는 둘 중 하나 가 필요하지 않습니다.head-zhead -zfind -print0printf "%s\\0" "$pathname"

  • 계산은 shstdin에 상속된 행을 사용하여 내부적으로 수행 됩니다 find. 일반적으로 에 아무것도 파이프하지 않지만 find일반적으로 계산 이외의 목적으로 파이프할 수 있습니다. 그러면 다른 목적은 우리의 계산 방법과 호환되지 않습니다.

  • yes휴대가 쉽지 않습니다. 우리의 목적 while :; do echo; done은 휴대용 대안입니다.

  • find-sh설명은 다음과 같습니다.의 두 번째 sh는 무엇입니까 sh -c 'some shell code' sh?


사용자가 이 솔루션을 구현하는 쉘 기능을 요청했습니다. 여기있어:

findn () (
  n="$1"
  shift
  case "$n" in
    '' | *[!0123456789]*) echo >&2 not a valid number; 
  exit 1;;
  esac
  [ "$n" -eq 0 ] && exit 0
  n="$((n-1))"
  while :; do echo; done | head -n "$n" \
  | find "$@" -exec sh -c '
     read dummy || kill -s PIPE "$PPID"
  ' find-sh \;
)

첫 번째 매개변수는 원하는 최대 일치 수이며 나머지는 처리됩니다 find. 노트:

사용 예:

findn 2 / -name bin -print 2>/dev/null

답변3

find많은 사람들에게 효과가 있을 수 있는 이것이 없는 해결책은 fdRust로 작성된 찾기 같은 도구를 사용하는 것입니다. (fd는 간단하고 빠르며 사용자 친화적인 대안입니다.)

fd --glob '*.txt' /path/to/search --max-results $n

답변4

4.4+ 및 GNU 도구를 사용하여 bash세 번째 파일을 찾은 후 일찍 종료하려면 다음을 수행할 수 있습니다.

n=3
readarray -td '' first_3_files < <(
  (
    echo "$BASHPID"
    LC_ALL=C exec stdbuf -o0 find . -name '*.txt' -type f -print0
  ) | {
    IFS= read -r pid
    head -zn "$n"
    kill -s PIPE "$pid"
  }
)

echo "The first $n files are:"
printf ' - %s\n' "${first_3_files[@]}"

stdbuf -o0find검색을 계속하고 네 번째 파일 경로를 찾아 인쇄할 때만 SIGPIPE를 수신하는 대신 출력 버퍼링을 중지하고 반환하자마자 SIGPIPE 신호를 보냅니다 .findhead -zn 3find

또는 GNU 조건자를 사용하는 또 다른 GNU 특정 find방법 -quit:

n=3
readarray -td '' first_3_files < <(
  seq "$((n - 1))" | LC_ALL=C find . -name '*.txt' -type f -print0 \
   ! -exec read iteration ';' -quit)

( 시스템에 독립 실행형 유틸리티가 없는 경우 read이 유틸리티를 사용하십시오 -exec sh -c 'read iteration' ';'. read있는 시스템에는 내장 프로그램 주변의 쉘 스크립트 래퍼로 구현되었을 수 있습니다 read.)

를 사용하면 zsh다음을 수행할 수 있습니다.

first_3_files=( **/*.txt(ND.Y3) )

관련 정보