
단일 레벨 하위 디렉터리가 많은 특정 디렉터리에서 실행되는 스크립트를 작성하려고 합니다. 스크립트는 cd
각 하위 디렉터리로 이동하여 디렉터리의 파일에 대해 명령을 실행한 다음 cd
종료하고 다음 디렉터리로 계속 진행합니다.
출력은 원래 디렉터리와 동일한 구조와 이름을 가진 새 디렉터리로 반환되어야 합니다. 이를 수행하는 가장 좋은 방법은 무엇입니까?
답변1
가장 좋은 방법은 서브쉘을 사용하는 것입니다.
for dir in */; do
(cd -- "$dir" && some command there)
done
다음 상황을 피하십시오.
for dir in */; do
cd -- "$dir" || continue
some command there
cd ..
done
또는:
here=$PWD
for dir in */; do
cd -- "$dir" || continue
some command here
cd "$here"
done
특히 심볼릭 링크가 포함되거나 스크립트를 실행할 때 현재 디렉터리의 경로가 변경될 수 있는 경우 신뢰성이 떨어지기 때문입니다.
하위 쉘을 포함하지 않는 "올바른" 방법은 close-on-exec 플래그를 사용하여 파일 설명자에서 현재 디렉터리를 열고 chdir()
하위 디렉터리로 이동한 다음 fchdir(fd)
. 쉘은 이를 지원합니다.
하지만 당신은 이렇게 할 수 있습니다 perl
:
#! /usr/bin/perl
opendir DIR, "." or die "opendir: $!";
while (readdir DIR) {
if (chdir($_)) {
do-something-there;
chdir DIR || die "fchdir: $!";
}
}
closedir DIR
답변2
for 루프 사용
디렉토리의 하위 디렉토리를 반복하려면 for
루프를 사용할 수 있습니다.
for dir in */; do
echo "Doing something in $dir"
done
와일드카드는 */
현재 디렉토리 내의 하위 디렉토리뿐만 아니라 디렉토리에 대한 기호 링크까지 확장됩니다. 이름이 시작하는 디렉터리 .
(즉, 도트 파일의 디렉터리)는 생략됩니다. 심볼릭 링크를 건너뛰려면 명시적으로 이를 수행할 수 있습니다.
for dir in */; do
if [ -L "${dir%/}" ]; then continue; fi
echo "Doing something in $dir"
done
디렉터리의 모든 파일에 대해 명령을 실행하려면 *
와일드카드 문자를 사용하세요. 다시 말하지만 여기에는 도트 파일이 포함되지 않습니다. 디렉터리가 비어 있으면 와일드카드 문자가 확장되지 않습니다.
for dir in */; do
if [ -L "${dir%/}" ]; then continue; fi
if (set -- "$dir"*; [ "$#" -ne 0 ]); then
somecommand -- "$dir"*
fi
done
각 파일에 대해 개별적으로 명령을 호출해야 하는 경우 중첩 루프를 사용하십시오. 아래 코드 조각에서는 [ -e "$file" ] || [ -L "$file" ]
파일이 기존 파일(깨진 심볼릭 링크 제외)인지 심볼릭 링크(깨진 여부)인지 테스트하는 데 사용합니다. "$file"
이는 빈 디렉터리 조건으로 인해 와일드카드 패턴이 확장되지 않은 경우에만 작동합니다.
for dir in */; do
if [ -L "${dir%/}" ]; then continue; fi
for file in "$dir"*; do
if [ -e "$file" ] || [ -L "$file" ]; then
somecommand -- "$file"
fi
done
done
명령에 현재 디렉터리로 하위 디렉터리가 필요한 경우 이를 수행하는 방법에는 두 가지가 있습니다. cd
전후에 전화하실 수 있습니다.
for dir in */; do
if [ -L "${dir%/}" ]; then continue; fi
if cd -- "$dir"; then
for file in *; do
if [ -e "$file" ] || [ -L "$file" ]; then
somecommand -- "$file"
fi
done
cd ..
fi
done
단점은 명령 실행 중에 하위 디렉터리가 이동되거나 프로세스에 상위 디렉터리로 변경할 권한이 없는 경우와 같은 극단적인 경우에는 실패할 수 있다는 것입니다. 이러한 극단적인 경우를 방지하기 위한 또 다른 접근 방식 cd ..
은 하위 쉘에서 명령을 실행하는 것입니다. 서브쉘은 현재 디렉토리를 포함하여 원래 쉘의 상태를 복사하며 서브쉘에서 발생하는 일은 원래 쉘 프로세스에 영향을 주지 않습니다.
for dir in */; do
if [ -L "${dir%/}" ]; then continue; fi
( cd -- "$dir" &&
for file in *; do
if [ -e "$file" ] || [ -L "$file" ]; then
somecommand -- "$file"
fi
done
)
fi
done
그리고 검색
보시다시피, 명목이 아닌 경우를 처리하는 것은 그리 쉬운 일이 아닙니다. 이들 중 일부는 걱정할 필요가 없는 극단적인 경우이지만 빈 디렉터리를 처리할 수 있어야 합니다. ksh, bash 또는 zsh를 사용하는 경우 이러한 상황을 처리하는 더 쉬운 방법이 있습니다. 위에서는 일반 sh에서 작동하는(그리고 도트 파일을 처리하지 않는) 코드를 보여주었습니다.
파일과 디렉터리를 열거하는 또 다른 방법은 find
명령입니다. 디렉터리 트리를 재귀적으로 순회하도록 설계되었지만 하위 디렉터리가 없어도 사용할 수 있습니다. GNU 또는 FreeBSD 찾기(즉, 내장되지 않은 Linux, Cygwin, FreeBSD 또는 OSX)를 사용하면 디렉토리 트리의 모든 파일에 대해 명령을 실행하는 쉬운 방법이 있습니다.
find . ! -type d -execdir somecommand {} \;
디렉터리가 아닌 모든 파일에 대해 명령이 실행됩니다. 일반 파일에서만 실행하십시오(심볼릭 링크 및 기타 특수 유형의 파일 제외).
find . -type f -execdir somecommand {} \;
최상위 디렉토리에 직접 위치한 파일을 제외하려면 다음을 수행하십시오.
find . -mindepth 2 -type f -execdir somecommand {} \;
하위 하위 디렉토리가 존재하고 그 디렉토리로 재귀적으로 들어가고 싶지 않은 경우:
find . -mindepth 2 -maxdepth 3 -type f -execdir somecommand {} \;