저는 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
.
처음에 nullglob
및 dotglob
쉘 옵션을 설정하면 $topdir
하위 디렉터리 없이(예: with ) 전체 루프를 건너뛸 수 있으며 nullglob
아래에 숨겨진 디렉터리 이름도 포함할 수 있습니다(예: with ).$topdir
dotglob
변경하여
topdir='./Dir1'
입력하다
topdir=$1
스크립트가 디렉토리 경로를 유일한 명령줄 인수로 사용하도록 할 수 있습니다.
find
조금 더 복잡한 것으로 변경하면 속도를 크게 높일 수 있습니다.
find "$subdir" -type f -exec sh -c 'for pathname do echo .; done' sh {} +
(나머지 루프는 그대로 유지되어야 합니다.) 이는 echo
각 파일이 아닌 발견된 파일 배치에 대해 매우 작은 인라인 쉘 스크립트를 실행합니다 . 이것은 ~이 될 것이다많은echo
Faster는 쉘에 내장된 명령으로 간주됩니다 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에 익숙하지 않지만 이 스크립트를 실행하는 플랫폼이 무엇이든 다음이 설치되어 있다고 가정합니다.
bash
v4.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
이 옵션을 사용하는 경로 이름 확장은 파일을 확인하는 기호 링크를 따르므로 위의 모든 조각에도 해당 파일이 포함됩니다.
(원래는 디렉토리로 확인되는 기호 링크도 사용했지만 이 동작은 bash
4.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