Bash를 사용하여 모든 하위 디렉토리에서 작업 수행


196

특정 폴더의 모든 하위 디렉토리에서 작업을 수행 해야하는 스크립트를 작성 중입니다.

그것을 작성하는 가장 효율적인 방법은 무엇입니까?


1
정확성에 대한 답변을 통해 다시오고 재평가 고려하시기 바랍니다 - 당신은 주요 버그 (F / E에도 불구하고 전망을 많이 가져 오는 허용 답을 가지고 누군가가 이전에 실행 어디 디렉토리에 그것을 실행 mkdir 'foo * bar'하게됩니다 foobar경우에도 이상 반복 할 수 그것들은 존재하지 않으며 모든 파일 이름 *목록 , 디렉토리가 아닌 파일 이름 으로 바뀝니다 ).
Charles Duffy

1
... 누군가가 달리면 더 나빠질 수 있습니다. 누군가가 삭제할 디렉토리를 찾기 mkdir -p '/tmp/ /etc/passwd /'위해이 방법에 따라 스크립트를 실행하면 /tmp삭제 될 수 /etc/passwd있습니다.
Charles Duffy

답변:


179
for D in `find . -type d`
do
    //Do whatever you need with D
done

81
이것은 공백에서 깨질 것입니다
ghostdog74

2
또한 find재귀 또는 비 재귀 동작을 원하는 경우 매개 변수 를 조정해야합니다 .
Chris Tonkinson

32
위의 답변은 나에게도 자체 디렉토리를 제공하므로 다음이 나를 위해 조금 나아 find . -mindepth 1 -type d
졌습니다

1
@JoshC, 두 변종 모두 생성 된 디렉토리 mkdir 'directory name with spaces'를 4 개의 개별 단어로 나눕니다.
Charles Duffy

4
나는 추가해야 -mindepth 1 -maxdepth 1하거나 너무 깊어졌습니다.
ScrappyDev

284

하위 프로세스 작성을 피하는 버전 :

for D in *; do
    if [ -d "${D}" ]; then
        echo "${D}"   # your processing here
    fi
done

또는 작업이 단일 명령 인 경우 더 간결합니다.

for D in *; do [ -d "${D}" ] && my_command; done

또는 훨씬 더 간결한 버전 ( @enzotib 덕분에 ). 이 버전에서 각 값에는 D슬래시가 있습니다.

for D in */; do my_command; done

49
당신은 피할 수 if또는 [:와for D in */; do
enzotib

2
+1 디렉토리 이름이 ./로 시작하지 않기 때문에 +1 대답
Hayri Uğur Koltuk 1

3
이것은 디렉토리 이름 +1의 공백까지 정확합니다
Alex Reinking

5
마지막 명령에는 한 가지 문제점이 있습니다. 하위 디렉토리가없는 디렉토리에있는 경우; "* /"를 반환합니다. 따라서 두 번째 명령 for D in *; do [ -d "${D}" ] && my_command; done또는 두 가지 최신 명령 의 조합을 사용하는 것이 좋습니다 .for D in */; do [ -d $D ] && my_command; done
Chris Maes

5
이 답변은 숨겨진 디렉토리를 무시합니다. 숨겨진 디렉토리를 포함하려면 for D in .* *; do대신 사용하십시오 for D in *; do.
patryk.beza

107

가장 간단한 비 재귀 방법은 다음과 같습니다.

for d in */; do
    echo "$d"
done

/끝에 만 사용 디렉토리 알려줍니다.

필요가 없습니다

  • 찾기
  • 어 wk
  • ...

11
참고 : 여기에는 도트 디어가 포함되지 않습니다 (좋은 일이지만 알 수 있어야 함).
wisbucky

shopt -s dotglob와일드 카드를 확장 할 때 도트 파일 / dotdir을 포함 하는 데 사용할 수 있습니다 . 또한보십시오 : gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
Steen Schütt

나는 당신이 의미 생각 /*대신 *//사용할 경로를 나타내는.
Brōtsyorfuzthrāx

2
@Shule /*은 절대 경로를위한 */것이며 현재 위치의 하위 디렉토리를 포함합니다
Dan G

3
유용한 힌트 : $ d에서 후행 '/'를 잘라내려면${d%/*}
Hafthor

18

find명령을 사용하십시오 .

GNU에서는 매개 변수 find를 사용할 수 있습니다 -execdir.

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

또는 -exec매개 변수 를 사용하여 :

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

또는 다음 xargs명령으로

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

또는 for 루프를 사용하십시오.

for d in */; { echo "$d"; }

재귀의 경우 확장 글러브 ( **/)를 대신 사용하십시오 (:로 활성화 shopt -s extglob).


자세한 예 는 각 디렉토리로 이동하여 명령을 실행하는 방법을 참조하십시오. 그래서


1
-exec {} +POSIX 지정이고, -exec sh -c 'owd=$PWD; for arg; do cd -- "$arg" && pwd -P; cd -- "$owd"; done' _ {} +다른 유효한 옵션이며,보다 적은 수의 쉘을 호출합니다 -exec sh -c '...' {} \;.
찰스 더피

13

편리한 원 라이너

for D in *; do echo "$D"; done
for D in *; do find "$D" -type d; done ### Option A

find * -type d ### Option B

옵션 A는 사이에 공백이있는 폴더에 적합합니다. 또한 폴더 이름의 각 단어를 별도의 엔티티로 인쇄하지 않기 때문에 일반적으로 더 빠릅니다.

# Option A
$ time for D in ./big_dir/*; do find "$D" -type d > /dev/null; done
real    0m0.327s
user    0m0.084s
sys     0m0.236s

# Option B
$ time for D in `find ./big_dir/* -type d`; do echo "$D" > /dev/null; done
real    0m0.787s
user    0m0.484s
sys     0m0.308s

10

find . -type d -print0 | xargs -0 -n 1 my_command


해당 루프의 반환 값을 for 루프에 공급할 수 있습니까? 이것은 더 큰 스크립트의 일부입니다.
mikewilliamson

@ 마찬가지로. $? 아마도 find 대신 xargs 명령의 상태를 얻을 것 my_command입니다.
Paul Tomblin

7

그러면 서브 쉘이 생성됩니다 (즉, while루프가 종료 될 때 변수 값이 손실 됨 ).

find . -type d | while read -r dir
do
    something
done

이것은하지 않습니다 :

while read -r dir
do
    something
done < <(find . -type d)

디렉토리 이름에 공백이 있으면 둘 중 하나가 작동합니다.


더 나은 이상한 파일 이름의 처리를 위해 (이름을 포함하는 공백을 종료 및 / 또는 줄 바꿈을 포함), 사용 find ... -print0while IFS="" read -r -d $'\000' dir
고든 데이비슨

@GordonDavisson는 ... 사실, 난 그 주장 것 -d ''때문에, bash는 구문과 기능에 대해 오해의 소지가 적은 -d $'\000'그 (거짓) 의미 $'\000'에서 몇 가지 방법으로 다른 인 ''추론에서 (거짓, 다시) 하나가 쉽게 할 수 실제로 - bash는 C 문자열 (NUL 구분, NUL을 포함 할 수 없음) 대신 Pascal 스타일 문자열 (길이 지정, NUL 리터럴 포함 가능)을 지원합니다.
Charles Duffy

5

시도해 볼 수 있습니다 :

#!/bin/bash
### $1 == the first args to this script
### usage: script.sh /path/to/dir/

for f in `find . -maxdepth 1 -mindepth 1 -type d`; do
  cd "$f"
  <your job here>
done

또는 유사한 ...

설명:

find . -maxdepth 1 -mindepth 1 -type d: 만 1의 1의 최대 재귀 깊이 ($ 1의 하위 디렉토리) 및 최소 깊이 (제외 현재 폴더와 디렉터리를 찾을 수 .)


이것은 버그입니다-공백이있는 디렉토리 이름으로 시도하십시오. BashPitfalls # 1DontReadLinesWithFor를 참조하십시오 .
찰스 더피

공백이있는 디렉토리 이름은 따옴표로 묶어 작동하므로 OP는 파일에서 줄을 읽으려고하지 않습니다.
Henry Dobson

그것은에서 작동합니다 cd "$f". 의 출력 find이 문자열 분할 인 경우에는 작동하지 않으므로 에서 이름의 개별 부분을 별도의 값으로 사용하여 확장 확장을 $f인용하거나 인용하지 않는 정도를 $f만듭니다.
찰스 더피

나는 그들이 말하지 않았다 파일에서 줄을 읽으려고. find의 출력은 기본 -print동작 으로 라인 지향적입니다 (이론상 하나의 이름은 한 줄에 있지만 아래 참조) .
Charles Duffy

from-line과 같은 행 지향 출력 find -print은 임의의 파일 이름을 전달 하는 안전한 방법 이 아닙니다. 파일을 실행 하고 이름에 별도의 행 mkdir -p $'foo\n/etc/passwd\nbar'이있는 디렉토리를 얻을 수 있기 때문 /etc/passwd입니다. /upload또는의 파일에서 이름 처리/tmp주의하지 않고 디렉토리의 하는 것은 권한 에스컬레이션 공격을 얻는 좋은 방법입니다.
찰스 더피

4

디렉토리 이름에 공백이 있으면 허용되는 대답은 공백으로 구분되며 선호하는 구문은 $()bash / ksh입니다. 예 GNU find -exec+;들어 옵션 사용

find .... -exec mycommand +; #this is same as passing to xargs

또는 while 루프를 사용하십시오

find .... |  while read -r D
do
  ...
done 

어떤 param이 디렉토리 이름을 보유합니까? 예를 들어, chmod를 + X $ DIR_NAME (예, 난 단지 디렉토리에 대한 chmod를 옵션이 알고)
마이크 그라프

사이에 하나 개 미묘한 차이가 find -exec로 전달하는과 xargs: find동안 실행되는 명령의 종료 값을 무시 xargs제로가 아닌 출구에 실패 할 것입니다. 필요에 따라 정확할 수 있습니다.
Jason Wodicka

find ... -print0 | while IFS= read -r d더 안전합니다-공백으로 시작하거나 끝나는 이름과 줄 바꿈 문자가 포함 된 이름을 지원합니다.
Charles Duffy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.