디렉터리를 변경하고 명령을 자동화한 다음 디렉터리를 다시 변경합니다.

디렉터리를 변경하고 명령을 자동화한 다음 디렉터리를 다시 변경합니다.

단일 레벨 하위 디렉터리가 많은 특정 디렉터리에서 실행되는 스크립트를 작성하려고 합니다. 스크립트는 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 {} \;

관련 정보