모든 하위 디렉터리를 재귀적으로 탐색하고 특정 확장자를 가진 파일이 있는 경우 해당 폴더에서 명령을 한 번 실행합니다.

모든 하위 디렉터리를 재귀적으로 탐색하고 특정 확장자를 가진 파일이 있는 경우 해당 폴더에서 명령을 한 번 실행합니다.

폴더의 모든 하위 디렉터리를 재귀적으로 탐색해야 합니다. 하위 디렉터리에 확장명이 ".xyz"인 파일이 있으면 해당 폴더에서 특정 명령을 한 번 실행해야 합니다.

이것이 내가 지금까지 가지고 있는 것입니다

recursive() {
  for d in *; do
    if [ -d "$d" ]; then
      (cd -- "$d" && recursive)
    fi
  dir=`pwd`   
  pattern="*.xyz"
file_count=$(find $dir -name $pattern | wc -l)
if [[ $file_count -gt 0 ]]; then
    echo "Match found. Going to execute a command"
    #execute command
fi
  done
}

(cd /target; recursive)

그런데 문제는 일치하는 항목이 있을 때 "일치 항목을 찾았습니다.."라는 메시지가 각 폴더에 여러 번 표시된다는 것입니다. 이 문제를 해결하는 동안 더 쉬운 방법이 있습니까?

답변1

당신은 재창조하고 있습니다 find.

다음과 같이 시도해 보십시오(GNU findutils및 GNU 사용 sort).

find /target -iname '*.xyz' -printf '%h\000' | sort -z -u | 
  xargs -0 -r -I {} sh -c "cd {} ; yourcommandhere"

"*.xyz" 파일이 있는 디렉토리 이름( )을 -printfNUL 바이트( )로 구분하여 인쇄합니다. 중복을 제거한 다음 각 디렉토리로 이동하여 실행합니다.%h\000sortxargscdyourcommandhere

xargs를 사용하여 실행할 스크립트를 작성할 수도 있습니다. 예를 들어

find /target -iname '*.xyz' -printf '%h\000' | sort -z -u | 
  xargs -0 -r /path/to/myscript.sh

간단한 myscript.sh 예:

#!/bin/sh

for d in "$@" ; do
  cd "$d"
  echo "Match found in $d. Going to execute command"
  # execute command
done

일치하는 디렉터리가 많으면 두 번째 버전이 훨씬 더 빠릅니다. 즉, 각 디렉터리에 대해 한 번씩 쉘을 포크하는 대신 쉘을 한 번만 포크한 다음 각 인수에 대해 반복합니다.


그건 그렇고, 여기서는 둘 다 실제로 printf필요 하지 않습니다. 하지만 무슨 일이 일어나고 있는지 읽고 이해하기가 더 쉬워집니다. 마찬가지로 중요한 점은 (printf 및 sort를 사용하여) 초기에 중복 항목을 제거함으로써 bash를 사용하는 것보다 훨씬 빠르게 실행되고 특정 디렉터리에서 명령을 여러 번 실행할 위험(다소 작은)을 제거한다는 것입니다.sortxargs

정렬이나 xargs 없이 동일한 작업을 수행하는 또 다른 방법은 다음과 같습니다.

find /target -iname '*.xyz' -exec bash -c \
    'typeset -A seen
     for f in "$@"; do
       d="$(dirname "$f")";
       if [[ ! -v $seen[$d] ]]; then
         echo "Match found in $d. Going to execute command"
         # Execute command
         seen["$d"]=1
       fi
     done' {} +

이는 bash()의 연관 배열을 사용하여 $seen[]어떤 디렉토리가 조회되고 처리되었는지 추적합니다. 수천 개의 일치하는 파일이 있는 경우 *.xml(bash 스크립트가 여러 번 포크할 수 있도록 최대 명령줄 길이를 초과하기에 충분함) 다음 명령을 실행하세요.가능한특정 디렉터리에서 여러 번 실행합니다.

find 옵션으로 실행되는 스크립트는 -exec위의 xargs 버전과 같은 독립 실행형 스크립트일 수 있습니다.

그런데 여기의 모든 변형은 awk나 perl 또는 sh나 bash 스크립트가 아닌 모든 스크립트를 쉽게 실행할 수 있습니다.

답변2

find문자열을 인쇄하는 내장 플래그가 있는데, 이는 여기서 매우 유용합니다:

find -iname "*.xyz" -printf "%h\n"패턴과 일치하는 파일이 포함된 모든 디렉터리의 이름을 인쇄합니다( 의 마법 구문은 %h물론 개행 문자를 사용하여 find파일 디렉터리로 확장됩니다 \n).

그래서 이것이 당신이 원하는 것입니다:

COMMAND='echo'
find `pwd` -iname "*.pdf" -printf "%h\n" | sort -u | while read i; do                                              
    cd "$i" && pwd && $COMMAND
done

여기서 무슨 일이 일어나고 있습니다. 명령을 한 번만 실행하려면 중복된 항목을 제거하는 sort플래그 를 사용하여 파이프하면 됩니다. -u그런 다음 모든 것을 반복합니다 while. 또한 상대 경로가 아닌 절대 경로를 출력하기 find `pwd`위해 좋은 트릭인 를 사용했다는 점에 유의하세요. 이를 통해 상대 경로에 대해 걱정할 필요 없이 find사용할 수 있습니다 .cd

편집: 이 스크립트를 실행할 때 디렉토리 이름에 주의하세요. 줄 바꿈( \n) 이 포함된 디렉토리 이름 \은 스크립트를 깨뜨릴 수도 있습니다(그리고 덜 일반적인 문자일 수도 있지만 아직 더 이상 테스트하지 않았습니다). 이 문제를 해결하는 것은 어렵고 어떻게 해야 할지 모르기 때문에 그러한 디렉토리를 사용하지 말라고 조언할 수밖에 없습니다.

관련 정보