모든 하위 디렉토리에서 파일 수를보고하는 방법은 무엇입니까?


24

모든 하위 디렉토리를 검사하고 더 많은 재귀없이 파일이 얼마나 많은지보고해야합니다.

directoryName1 numberOfFiles
directoryName2 numberOfFiles

findBash가 할 때 왜 사용하고 싶 습니까? (shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done): 모든 디렉토리에 대해 해당 디렉토리의 항목 수 (숨겨진 점 파일 포함 .및 제외 ..)
janmoesen

@janmoesen 왜 그렇게 대답하지 않았습니까? 셸 스크립팅을 처음 사용했지만 메서드에 문제가 없습니다. 나에게 그것은 가장 좋은 방법처럼 보입니다. 아무도 귀하의 의견을 찬성하지 않았지만, 왜 그 의견이 나쁜지도 언급하지 않았습니다. 공감 된 답변은 당신보다 더 많은 답을 가지고 있기 때문에 내가 뭔가를 놓치고 있는지 궁금해합니다.
toxalot

@ toxalot : 너무 짧아서 (어쩌면 약간 음질이 떨어지기 때문에) 대답으로 추가하는 것을 귀찮게하지 않았습니다. 의견을 자유롭게 올리십시오. :-) 또한이 질문은 "얼마나 많은 파일"의 의미와 관련하여 다소 모호합니다. 내 솔루션은 "일반적인"파일 디렉토리를 계산합니다 . 아마도 포스터는 "디렉토리가 아니라 파일"을 의미했을 것입니다. 명심해야 할 또 다른 사항은이 globbing은 "숨겨진"도트 파일을 고려하지 않는다는 것입니다. 그래도 두 가지 문제를 해결할 수있는 방법이 있습니다. 그러나 다시 : 원래 포스터의 정확한 요구 사항을 확신하지 못합니다.
janmoesen

답변:


30

이것은 안전하고 휴대 가능한 방식으로 수행됩니다. 이상한 파일 이름으로 혼동되지 않습니다.

for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done

먼저 파일 수를 인쇄 한 다음 디렉토리 이름을 별도의 줄에 인쇄합니다. OP 형식을 유지하려면 추가 형식이 필요합니다 (예 :

for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'

관심있는 특정 서브 디렉토리 세트가있는 경우이를 서브 디렉토리로 교체 할 수 *있습니다.

이것이 왜 안전한가요? (따라서 스크립트 가치)

파일 이름은를 제외한 모든 문자를 포함 할 수 있습니다 /. 쉘이나 명령으로 특별히 처리되는 문자가 몇 가지 있습니다. 여기에는 공백, 줄 바꿈 및 대시가 포함됩니다.

for f in *구문을 사용하면 파일 이름에 관계없이 각 파일 이름을 얻는 안전한 방법입니다.

변수에 파일 이름이 있으면 여전히 같은 것을 피해야 find $f합니다. 경우 $f파일 이름을 포함 -test, find당신은 그냥 준 옵션에 대해 불평한다. 이를 피하는 방법 ./은 이름 앞에 사용하는 것입니다 . 이런 식으로 같은 의미를 갖지만 더 이상 대시로 시작하지 않습니다.

줄 바꿈과 공백도 문제입니다. $f파일 이름으로 "hello, buddy"가 포함 된 경우 find ./$f입니다 find ./hello, buddy. 당신은 이야기하고 find보고 ./hello,하고 buddy. 해당 사항이 존재하지 않으면 불만을 제기하며을 (를) 찾지 않습니다 ./hello, buddy. 이것은 피하기 쉽습니다-변수 주위에 따옴표를 사용하십시오.

마지막으로 파일 이름에 줄 바꿈이 포함될 수 있으므로 파일 이름 목록에서 줄 바꿈을 계산하면 작동하지 않습니다. 개행 문자가있는 모든 파일 이름에 대해 추가 개수를 얻게됩니다. 이를 피하려면 파일 목록에서 줄 바꿈을 계산하지 마십시오. 대신 단일 파일을 나타내는 줄 바꿈 (또는 다른 문자)을 계산하십시오. 이것이 find명령이 단순 -exec echo \;하지 않은 이유 -exec echo {} \;입니다. 파일 크기를 조정하기 위해 새 줄 하나만 인쇄하고 싶습니다.


1
왜 파일 이름에 줄 바꿈을 사용하는 사람이 세상에 있습니까? 답변 해주셔서 감사합니다.
ShyBoy

1
파일 이름에는 / 및 null 문자를 제외한 모든 문자가 포함될 수 있습니다. dwheeler.com/essays/fixing-unix-linux-filenames.html
Flimm

2
카운트는 디렉토리 자체를 포함합니다. 카운트에서 제외하려면-mindepth 1
toxalot

-printf '\n'대신에 사용할 수도 있습니다 -exec echo.
toxalot

1
@toxalot은을 지원하는 find가 -printf있지만 FreeBSD에서 작동하도록하려는 경우가 아닙니다.
Shawn J. Goff

6

표준 Linux 솔루션을 찾고 있다고 가정하면 비교적 간단한 방법은 다음과 find같습니다.

find dir1/ dir2/ -maxdepth 1 -type f | wc -l

어디는 findA를, 지정된 두 개의 하위 디렉토리를 통과 -maxdepth추가 재귀를 방지 1 만 (파일보고 -type f) 줄 바꿈으로 구분합니다. 그런 다음 결과를 파이프하여 wc해당 행 수를 계산합니다.


2 개 이상의 디렉토리가 있습니다 ... 명령과 find . -maxdepth 1 -type d출력을 어떻게 결합 할 수 있습니까?
ShyBoy

(a) 변수에 필요한 디렉토리를 포함 find $dirs ...하거나 (b) 하나의 상위 레벨 디렉토리에만있는 경우 해당 디렉토리에서 find */ ...
가져옵니다

1
파일 이름에 개행 문자가 있으면 잘못된 결과가보고됩니다.
Shawn J. Goff

@Shawn : 감사합니다. 공백이있는 파일 이름이 있다고 생각했지만 줄 바꿈을 고려하지 않았습니다. 수정에 대한 제안?
jasonwryan

-exec echo찾기 명령에 추가 하십시오-파일 이름을 표시하지 않고 줄 바꿈 만합니다.
Shawn J. Goff

5

"재귀없이" directoryName1는 하위 디렉토리 가있는 경우 하위 디렉토리의 파일을 계산하지 않으려는 것을 의미 합니까? 그렇다면 표시된 디렉토리의 모든 일반 파일을 계산하는 방법입니다.

count=0
for d in directoryName1 directoryName2; do
  for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
    if [ -f "$f" ]; then count=$((count+1)); fi
  done
done

참고 그 -f테스트를 수행 두 함수 : 그것은 globs와 하나와 일치하는 엔트리가 상기 일반 파일인지를 테스트하고합니다 (globs와의 하나는 아무것도 is¹ 같은 패턴 나머지와 일치하지 않는 경우)이 엔트리가 일치 여부를 테스트한다. 당신은 유형과 관계없이 주어진 디렉토리에있는 모든 항목을 계산하려면, 대신 -f-e .

Ksh에는 패턴과 일치하는 파일이없는 경우 패턴을 도트 파일과 일치시키고 빈 목록을 생성하는 방법이 있습니다. ksh에서는 다음과 같은 일반 파일을 계산할 수 있습니다.

FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
  if [ -f "$x" ]; then ((++count)); fi
done

또는 모든 파일은 다음과 같습니다.

FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}

Bash는 이것을 더 간단하게 만드는 다른 방법을 가지고 있습니다. 일반 파일을 세려면

shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
  if [ -f "$x" ]; then ((++count)); fi
done

모든 파일을 세려면 :

shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}

평소와 같이 zsh에서는 훨씬 간단합니다. 일반 파일을 세려면

files=({directoryName1,directoryName2}/*(DN.))
count=$#files

변경 (DN.)(DN)모든 파일 려면로 하십시오.

¹ 각 패턴이 일치한다는 점에 유의하십시오. 그렇지 않으면 결과가 꺼질 수 있습니다 (예 : 숫자로 시작하는 파일을 세는 경우 for x in [0-9]*; do if [ -f "$x" ]; then …이라는 파일이있을 수 있으므로 수행 할 수 없음 [0-9]foo).


2

을 바탕으로 카운트 스크립트 , 숀의 대답 과 배쉬 트릭 확실히 심지어 파일 이름 줄 바꿈이 한 줄에 사용할 수있는 형태로 인쇄로 만들려면 :

for f in *
do
    if [ -d "./$f" ]
    then
        printf %q "$f"
        printf %s ' '
        find "$f" -maxdepth 1 -printf x | wc -c
    fi
done

printf %q인용 된 버전의 문자열, 즉 한 줄짜리 문자열을 인쇄하여 Bash 스크립트에 넣어 줄 바꿈 및 기타 특수 문자를 포함한 리터럴 문자열로 해석 할 수 있습니다. 예를 들어 echo -n $'\tfoo\nbar'vs를 참조하십시오 printf %q $'\tfoo\nbar'.

find명령은 각 파일에 대해 단일 문자를 인쇄 한 다음 줄을 세지 않고 문자를 세는 방식으로 작동합니다.


1

여기에 사용하여 결과를 얻을 수있는 "무차별"-ish 방법 find, echo, ls, wc, xargsawk.

find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'

이 일. 그러나``공백이있는 디렉토리가 있으면 출력이 엉망이됩니다.
ShyBoy

파일 이름에 개행 문자가 있으면 잘못된 결과가보고됩니다.
Shawn J. Goff

-1
for i in *; do echo $i; ls $i | wc -l; done

4
U & L에 오신 것을 환영합니다. 답변은 간단한 코드 드롭이 아니라 설명이 포함 된 긴 형식이어야합니다. 이것을 확장하고 무슨 일이 일어나고 있는지 설명하십시오. 또한 이것은 매우 비효율적 인 방법이며 예를 들어 공백이있는 파일을 처리하지 않습니다.
slm

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.