각 디렉토리로 이동하여 명령을 실행하는 방법은 무엇입니까?


143

어떻게 parent_directory 내에서 각 디렉토리를 통과하고 bash는 스크립트 작성합니까 실행 명령각 디렉토리를 .

디렉토리 구조는 다음과 같습니다.

parent_directory (이름은 무엇이든 될 수 있습니다-패턴을 따르지 않습니다)

  • 001 (디렉토리 이름은이 패턴을 따릅니다)
    • 0001.txt (파일 이름은이 패턴을 따릅니다)
    • 0002.txt
    • 0003.txt
  • 002
    • 0001.txt
    • 0002.txt
    • 0003.txt
    • 0004.txt
  • 003
    • 0001.txt

디렉토리 수를 알 수 없습니다.

답변:


108

현재 디렉토리가 다음과 같은 경우 다음을 수행 할 수 있습니다 parent_directory.

for d in [0-9][0-9][0-9]
do
    ( cd "$d" && your-command-here )
done

(하고 )현재 디렉토리가 기본 스크립트에서 변경되지 않도록, 서브 쉘을 만들 수 있습니다.


5
와일드 카드는 숫자 이외의 문자와 일치하지 않기 때문에 여기서는 중요하지 않지만 일반적으로 루프 이름 안에는 항상 디렉토리 이름을 큰 따옴표로 묶기를 원합니다. cd "$d"와일드 카드가 이름에 공백 및 / 또는 쉘 메타 문자가 포함 된 파일과 일치하는 상황으로 전송한다는 점에서 더 좋습니다.
tripleee

1
(여기의 모든 답변에는 동일한 결함이있는 것으로 보이지만 가장 투표가 많은 답변에서 가장 중요합니다.)
tripleee

1
또한 대부분의 명령은 실제로 어떤 디렉토리를 실행하는지 신경 쓰지 않습니다. for f in foo bar; do cat "$f"/*.txt; done >output기능적으로 cat foo/*.txt bar/*.txt >output. 그러나 ls인수를 전달하는 방법에 따라 약간 다른 출력을 생성하는 하나의 명령입니다. 마찬가지로, 하위 디렉토리에서 파일을 실행할 경우 grep또는 wc상대 파일 이름을 출력하는 또는는 다른 파일 이름이 달라집니다 (그러나 종종 그런 이유로 하위 디렉토리로 정확하게 들어 가지 않기를 원합니다).
tripleee

158

Todd가 게시 한이 답변 이 도움이되었습니다.

find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;

\( ! -name . \) 현재 디렉토리에서 명령을 실행 피한다.


4
특정 폴더에서만 명령을 실행하려면 다음을 수행하십시오.find FOLDER* -maxdepth 0 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;
Martin K

3
-exec bash -c 'cd "$0" && pwd' {} \;일부 디렉토리에는 작은 따옴표와 큰 따옴표가 포함되어 있기 때문에 해야했습니다 .
Charlie Gorichanaz

12
다음을 추가하여 현재 디렉토리를 생략 할 수도 있습니다.-mindepth 1
mdziob

이것을 함수로 바꾸는 방법은 "git remote get-url origin`과 같은 명령을 pwd직접 받는 것이 아니라 ?
roachsinai

disd(){find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c 'cd "{}" && "$@"' \;}작동하지.
roachsinai 2019

48

GNU를 사용하는 경우 다음 과 같은 매개 변수 find를 사용해 볼 수 있습니다 -execdir.

find . -type d -execdir realpath "{}" ';'

또는 ( @gniourf_gniourf 주석에 따라 ) :

find . -type d -execdir sh -c 'printf "%s/%s\n" "$PWD" "$0"' {} \;

참고 : 전면 을 수정 하는 ${0#./}대신 사용할 수 있습니다 .$0./

또는 더 실용적인 예 :

find . -name .git -type d -execdir git pull -v ';'

현재 디렉토리를 포함하려면 다음을 사용하여 훨씬 간단합니다 -exec.

find . -type d -exec sh -c 'cd -P -- "{}" && pwd -P' \;

또는 사용 xargs:

find . -type d -print0 | xargs -0 -L1 sh -c 'cd "$0" && pwd && echo Do stuff'

또는 @gniourf_gniourf가 제안한 비슷한 예 :

find . -type d -print0 | while IFS= read -r -d '' file; do
# ...
done

위의 예제는 이름에 공백이있는 디렉토리를 지원합니다.


또는 bash 배열에 할당하여 :

dirs=($(find . -type d))
for dir in "${dirs[@]}"; do
  cd "$dir"
  echo $PWD
done

.특정 폴더 이름으로 변경하십시오 . 재귀 적으로 실행할 필요가 없으면 dirs=(*)대신 다음을 사용할 수 있습니다 . 위의 예는 이름에 공백이있는 디렉토리를 지원하지 않습니다.

@gniourf_gniourf가 제안한 것처럼 명시 적 루프를 사용하지 않고 배열에 find 출력을 넣는 유일한 방법은 Bash 4.4에서 다음과 같이 사용할 수 있습니다.

mapfile -t -d '' dirs < <(find . -type d -print0)

또는 권장되지 않는 방법 ( 파싱ls 포함 ) :

ls -d */ | awk '{print $NF}' | xargs -n1 sh -c 'cd $0 && pwd && echo Do stuff'

위의 예는 현재 dir을 무시하지만 (OP에서 요청한대로) 공백이있는 이름을 구분합니다.

또한보십시오:


1
find명시 적 루프를 사용하지 않고 배열에 출력을 넣는 유일한 올바른 방법 은 Bash 4.4에서 사용할 수 있습니다 mapfile -t -d '' dirs < <(find . -type d -print0). 그때까지 유일한 옵션은 루프를 사용하는 것입니다 (또는 작업을으로 연기 find).
gniourf_gniourf 14시 03 분

1
실제로, 실제로 dirs배열 이 필요하지 않습니다 . 다음 find과 같이 (안전하게) 출력을 반복 할 수 find . -type d -print0 | while IFS= read -r -d '' file; do ...; done있습니다.
gniourf_gniourf 14:04의

@gniourf_gniourf 저는 시각적이고 항상 단순 해 보이는 것들을 찾거나 만들려고합니다. 예 를 들어이 스크립트 를 사용했으며 bash 배열을 사용하면 파이프를 사용하는 대신 논리를 작은 조각으로 분리하여 쉽고 읽을 수 있습니다. 추가 파이프를 도입, IFS, read, 추가적인 복잡성 인상을 준다. 나는 그것에 대해 생각해야 할 것입니다. 어쩌면 더 쉬운 방법이있을 수 있습니다.
kenorb

더 쉽게 당신이 깨 졌다는 것을 의미 한다면, 쉬운 방법이 있습니다. 이제 걱정하지 마십시오.이 모든 것들 IFSread(당신이 말한대로) 익숙해지면 두 번째 본질이됩니다 ... 이는 스크립트를 작성하는 정식적이고 관용적 인 방법의 일부입니다.
gniourf_gniourf

그건 그렇고, 당신의 첫 번째 명령은 find . -type d -execdir echo $(pwd)/{} ';'당신이합니다 (원하는 것을하지 않는 $(pwd)이전 확장 find... 심지어 실행)
gniourf_gniourf

29

파이핑 한 다음을 사용하여이 작업을 수행 할 수 있습니다 xargs. catch는 -Ibash 명령의 하위 문자열을 각각이 전달한 하위 문자열로 대체 하는 플래그 를 사용해야 합니다 xargs.

ls -d */ | xargs -I {} bash -c "cd '{}' && pwd"

pwd각 디렉토리에서 실행하려는 명령 으로 바꿀 수 있습니다 .


2
왜 이것이 공표되지 않았는지, 지금까지 찾은 가장 간단한 스크립트는 모르겠습니다. 감사합니다
Linvi

20

최상위 폴더가 알려진 경우 다음과 같이 작성할 수 있습니다.

for dir in `ls $YOUR_TOP_LEVEL_FOLDER`;
do
    for subdir in `ls $YOUR_TOP_LEVEL_FOLDER/$dir`;
    do
      $(PLAY AS MUCH AS YOU WANT);
    done
done

$ (원하는대로 플레이); 원하는만큼 코드를 넣을 수 있습니다.

나는 어떤 디렉토리에서도 "cd"를하지 않았다.

건배,


3
여기에 "Useless Use of ls" 의 몇 가지 예가 있습니다 for dir in $YOUR_TOP_LEVEL_FOLDER/*. 대신 할 수 있습니다 .
Mark Longair

1
글쎄, 아마도 일반적인 의미로는 쓸모가 없지만 ls에서 직접 필터링 할 수 있습니다 (즉, 모든 디렉토리는 .tmp로 끝남). 그래서 ls $dir
제가이

13
for dir in PARENT/*
do
  test -d "$dir" || continue
  # Do something with $dir...
done

그리고 이것은 이해하기 매우 쉽습니다!
Rbjz

6

하나의 라이너는 빠르고 더러운 사용법에 좋지만 스크립트 작성에 더 자세한 버전을 선호합니다. 이것은 내가 사용하는 템플릿으로 많은 경우를 처리하며 폴더에서 실행할 더 복잡한 코드를 작성할 수 있습니다. dir_command 함수에서 bash 코드를 작성할 수 있습니다. 아래에서 dir_coomand는 예제로 git의 각 저장소에 태그를 지정합니다. 나머지 스크립트는 디렉토리의 각 폴더에 대해 dir_command를 호출합니다. 주어진 폴더 세트 만 반복하는 예제도 포함됩니다.

#!/bin/bash

#Use set -x if you want to echo each command while getting executed
#set -x

#Save current directory so we can restore it later
cur=$PWD
#Save command line arguments so functions can access it
args=("$@")

#Put your code in this function
#To access command line arguments use syntax ${args[1]} etc
function dir_command {
    #This example command implements doing git status for folder
    cd $1
    echo "$(tput setaf 2)$1$(tput sgr 0)"
    git tag -a ${args[0]} -m "${args[1]}"
    git push --tags
    cd ..
}

#This loop will go to each immediate child and execute dir_command
find . -maxdepth 1 -type d \( ! -name . \) | while read dir; do
   dir_command "$dir/"
done

#This example loop only loops through give set of folders    
declare -a dirs=("dir1" "dir2" "dir3")
for dir in "${dirs[@]}"; do
    dir_command "$dir/"
done

#Restore the folder
cd "$cur"

5

폴더를 반복하고 싶기 때문에 파일 형식을 지정하지 않아도됩니다 ...이 같은 것을 찾고 있습니까?

cd parent
find . -type d | while read d; do
   ls $d/
done

4

당신이 사용할 수있는

find .

현재 디렉토리 재귀에서 모든 파일 / 디렉토리를 검색하려면

출력을 xargs 명령으로 파이프 할 수있는 것보다

find . | xargs 'command here'

3
이것은 요청 된 것이 아닙니다.
kenorb 2016 년

0
for p in [0-9][0-9][0-9];do
    (
        cd $p
        for f in [0-9][0-9][0-9][0-9]*.txt;do
            ls $f; # Your operands
        done
    )
done

디렉토리를 다시 백업하거나 cd서브 쉘에서 수행해야합니다 .
Mark Longair

Downvote : cd이 코드는 실제로 유용한 작업을 수행하지 않으므로 편집 내용이 손실되었습니다 .
tripleee
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.