폴더의 모든 하위 디렉터리를 재귀적으로 탐색해야 합니다. 하위 디렉터리에 확장명이 ".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" 파일이 있는 디렉토리 이름( )을 -printf
NUL 바이트( )로 구분하여 인쇄합니다. 중복을 제거한 다음 각 디렉토리로 이동하여 실행합니다.%h
\000
sort
xargs
cd
yourcommandhere
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를 사용하는 것보다 훨씬 빠르게 실행되고 특정 디렉터리에서 명령을 여러 번 실행할 위험(다소 작은)을 제거한다는 것입니다.sort
xargs
정렬이나 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
) 이 포함된 디렉토리 이름 \
은 스크립트를 깨뜨릴 수도 있습니다(그리고 덜 일반적인 문자일 수도 있지만 아직 더 이상 테스트하지 않았습니다). 이 문제를 해결하는 것은 어렵고 어떻게 해야 할지 모르기 때문에 그러한 디렉토리를 사용하지 말라고 조언할 수밖에 없습니다.