하위 디렉터리 Bash의 파일 수 보고

하위 디렉터리 Bash의 파일 수 보고

저는 Win10 컴퓨터에서 작업하지만 일반적으로 Gitbash나 Linux 하위 시스템에서 작업합니다.

지정된 디렉터리의 모든 하위 디렉터리에 있는 파일 수를 얻으려고 합니다.

비슷한 질문이에요모든 하위 디렉터리의 파일 수를 보고하는 방법은 무엇입니까?그러나 차이점은 모든 하위 디렉터리에 걸쳐 일정한 수의 레벨을 갖는 대신 다음과 같은 것을 갖는다는 것입니다.

Dir1/sub1
Dir1/sub1/subsub1
Dir1/sub2
Dir1/sub3/subsub3/subsubsub3

나는 노력했다

 shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done

검색할 수준 수 조정(*/, */*/* 등)

하지만 실제로 원하는 것을 얻을 수는 없습니다. 예를 들면 다음과 같습니다.

Dir1/sub1: Number of files
Dir1/sub2: Number of files
Dir1/sub3: Number of files

답변1

#!/bin/bash

shopt -s dotglob nullglob

topdir='./Dir1'

for subdir in "$topdir"/*/; do
    find "$subdir" -type f -exec echo . \; |
    printf '%s: %d\n' "${subdir%/}" "$( wc -l )"
done

이 작은 bash스크립트는 하위 디렉터리의 경로 이름 목록 $topdir과 각 하위 디렉터리(어디에서든)에서 발견되는 일반 파일 수를 출력합니다.

이 스크립트는 모든 하위 디렉터리를 반복 $topdir하고 각 하위 디렉터리에 대해 find명령을 실행합니다.

find "$subdir" -type f -exec echo . \;

에서 발견된 각 일반 파일에 대해 빈 줄에 점이 출력됩니다 $subdir. 계산하기 쉽기 때문에 점을 출력합니다(파일 이름에는 개행 문자가 포함될 수 있음).

이 포인트는 다음으로 연결됩니다.

printf '%s: %d\n' "${subdir%/}" "$( wc -l )"

여기서는 printf출력 형식을 지정하는 데 사용됩니다. 하위 디렉터리 경로(마지막 슬래시 제거)와 파일 수를 사용합니다.

파일 개수는 wc -l파이프의 포인트를 계산합니다 find(엄밀히 말하면 포인트는 계산하지 않고 줄 바꿈을 계산합니다). printf표준 입력 스트림 자체를 읽지 않으므로 소비 됩니다 wc -l.

처음에 nullglobdotglob쉘 옵션을 설정하면 $topdir하위 디렉터리 없이(예: with ) 전체 루프를 건너뛸 수 있으며 nullglob아래에 숨겨진 디렉터리 이름도 포함할 수 있습니다(예: with ).$topdirdotglob

변경하여

topdir='./Dir1'

입력하다

topdir=$1

스크립트가 디렉토리 경로를 유일한 명령줄 인수로 사용하도록 할 수 있습니다.

find조금 더 복잡한 것으로 변경하면 속도를 크게 높일 수 있습니다.

find "$subdir" -type f -exec sh -c 'for pathname do echo .; done' sh {} +

(나머지 루프는 그대로 유지되어야 합니다.) 이는 echo각 파일이 아닌 발견된 파일 배치에 대해 매우 작은 인라인 쉘 스크립트를 실행합니다 . 이것은 ~이 될 것이다많은echoFaster는 쉘에 내장된 명령으로 간주됩니다 sh. ( 이를 보장하려면 sh -c로 변경해야 할 수도 있습니다 .) 를 사용하면 각 파일에 대해 느리게 실행됩니다.bash -c-exec echo . \;find/bin/echo

답변2

GNU 유틸리티 사용:

find Dir1 -mindepth 2 -type f -printf '%P\0' |
  awk -F/ -vRS='\0' '{n[$1]++}; END{for (i in n) print i ": " n[i]}'

카운트만정기적인각 하위 디렉토리의 파일 Dir1.

출력은 다음과 유사합니다.

sub1: 3
sub2: 30
sub3: 13
sub4: 3
sub5: 3

답변3

저는 Windows의 Gitbash에 익숙하지 않지만 이 스크립트를 실행하는 플랫폼이 무엇이든 다음이 설치되어 있다고 가정합니다.

  • bashv4.x 이상(macOS 사용자는 다음을 설치하여 최신 버전을 설치해야 합니다.스스로 만든또는 다른 것)
  • GNU - find실제로 모든 표준 Unix는 find가능하지만 MS-DOS/Windows 버전은 그렇지 않습니다 grep.

위의 내용을 가정하면 이 스크립트가 트릭을 수행해야 합니다.

#!/bin/bash
# USAGE: count_files <dir> ...

declare -A filecount

# Tell bash to execute the last pipeline element in this shell, not a subshell
shopt -s lastpipe

# Run through all the user-supplied directories at one go
for d in "$@"; do
  find "$d" -type f | while read f; do
    [[ $f =~ ^(${d%%/}/[^/]+)/ ]] && (( filecount["${BASH_REMATCH[1]}"]++ ))
  done
done

# REPORT!
for k in "${!filecount[@]}"; do
  echo "$k: ${filecount[$k]}"
done

답변4

버전이 4.0 이상이라고 가정하면 bash거의 거기에 있습니다.

셸 옵션을 사용하면 코드에서 파일 수를 재귀적으로 계산할 수 있습니다 globstar. 에서 man bash(1):

**설정된 경우 경로 이름 확장 컨텍스트에 사용된 패턴은 모든 파일과 0개 이상의 디렉터리 및 하위 디렉터리와 일치합니다. 패턴 뒤에 가 오면 /디렉터리와 하위 디렉터리만 일치합니다.

최상위 디렉터리(하위 디렉터리 포함)에 있는 모든 파일을 재귀적으로 계산하려면 다음을 수행하세요.

shopt -s dotglob globstar
for dir in */; do
    all=( "$dir"/** )
    printf '%s\n' "$dir: ${#all[@]}"
done

시도 중인 코드에서와 같이 각 최상위 디렉토리에 대해 경로 이름 확장 결과로 배열을 채운 다음 해당 요소 수를 표시합니다. 이름이 (숨겨진 파일)로 시작하는
dotglob파일을 포함하는 데 사용됩니다 ..

하위 디렉터리 개체를 제외한 모든 파일을 재귀적으로 계산하려면 모든 파일 수에서 하위 디렉터리 수를 빼면 됩니다.

shopt -s dotglob globstar
for dir in */; do
    all=( "$dir"/** )
    alldir=( "$dir"/**/ )
    printf '%s\n' "$dir: $(( ${#all[@]} - ${#alldir[@]} ))"
done

그러나 여기서는 "문서"에 대한 광범위한 정의를 가정합니다.POSIX에서는는 일반 파일, 문자, 블록 또는 FIFO 특수 파일, 기호 링크, 소켓, 디렉토리 또는 표준 외부에 있을 수 있는 특정 구현을 참조할 수 있습니다.
특정 유형의 파일(예: 일반 파일)만 계산하려면 find.
또는 위의 코드를 확장하여 루프에서 파일 형식을 테스트할 수 있습니다.

shopt -s dotglob globstar
for dir in */; do
    all=( "$dir"/** )
    count=0
    for file in "${all[@]}"; do
        test -f "$file" && count="$(( "$count" + 1 ))"
    done
    printf '%s\n' "$dir: $count"
done

그러나 이 덜 편리한 솔루션은 find기반 대안보다 훨씬 느립니다(예: 더 빠른 솔루션보다 두 배 이상 느림).코살로난다의 답변bash, Linux 5.0 및 4.6에서 테스트됨 find).

또한 find기본 동작과 달리 globstar이 옵션을 사용하는 경로 이름 확장은 파일을 확인하는 기호 링크를 따르므로 위의 모든 조각에도 해당 파일이 포함됩니다.
(원래는 디렉토리로 확인되는 기호 링크도 사용했지만 이 동작은 bash4.3에서 변경되었습니다.)

마지막으로, 셸 옵션에 의존하지 않는 솔루션을 제공하기 위해 globstar재귀 함수를 사용하여 디렉터리의 최상위 하위 디렉터리에 있는 모든 일반 파일을 재귀적으로 계산할 수 있습니다 $1.

#!/bin/bash

# nullglob is needed to avoid the function being
# invoked on 'dir/*' when * matches nothing
shopt -s nullglob dotglob

function count_files () {
    for file in "$1"/*; do
        # Only count regular files
        [ -f "$file" ] && count="$(( "$count" + 1 ))"
        # Only recurse on directories
        [ -d "$file" ] && count_files "$file"
    done
}

for dir in "$1"/*/; do
    count="0"
    count_files "$dir"
    printf '%s: %s\n' "$dir" "$count"
done

관련 정보