센트OS 5.9
요전에 디렉토리에 많은 파일이 있는 문제에 부딪혔습니다. 계산하기 위해 나는 달렸다ls -l /foo/foo2/ | wc -l
단일 디렉터리에 100만 개 이상의 파일이 있는 것으로 나타났습니다(긴 이야기 - 근본 원인이 해결되고 있음).
내 질문은: 계산을 수행하는 더 빠른 방법이 있습니까? 카운트를 얻는 가장 효율적인 방법은 무엇입니까?
답변1
짧은 답변:
\ls -afq | wc -l
(여기에는 .
합계가 포함되어 있으므로 ..
2를 뺍니다.)
디렉터리의 파일을 나열하면 세 가지 일반적인 상황이 발생할 수 있습니다.
- 디렉터리의 파일 이름을 열거합니다. 이는 피할 수 없는 일입니다. 파일을 열거하지 않고는 디렉터리에 있는 파일의 수를 셀 수 없습니다.
- 파일 이름을 정렬합니다. 쉘 와일드카드와
ls
명령을 사용하여 이를 수행할 수 있습니다. - 부르다
stat
디렉터리인지 여부 등 각 디렉터리 항목에 대한 메타데이터를 검색합니다.
#3은 각 파일에 대해 inode를 로드해야 하므로 비용이 가장 많이 듭니다. 대조적으로, #1에 필요한 모든 파일 이름은 몇 개의 블록에 콤팩트하게 저장됩니다. #2는 약간의 CPU 시간을 낭비하지만 일반적으로 거래 중단 요인은 아닙니다.
파일 이름에 개행 문자가 없으면 단순히 ls -A | wc -l
디렉터리에 파일이 몇 개 있는지 알려줍니다. 별칭이 있는 경우 ls
호출이 트리거될 수 있으므로 stat
(예: 호출이 필요한 ls --color
파일 ls -F
형식을 알아야 함 stat
) 명령줄에서 호출하거나 command ls -A | wc -l
별칭 \ls -A | wc -l
을 피하세요.
파일 이름에 줄바꿈이 있는 경우 줄바꿈 나열 여부는 Unix 변형에 따라 다릅니다. GNU coreutils 및 BusyBox는 ?
기본적으로 개행을 표시하므로 안전합니다.
ls -f
항목을 정렬하지 않고 나열하기 위해 호출됩니다 (#2). 이는 자동으로 켜집니다 -a
(적어도 최신 시스템에서는). 이 -f
옵션은 POSIX에 있지만 선택적 상태입니다. 대부분의 구현에서는 이를 지원하지만 BusyBox는 지원하지 않습니다. 이 옵션은 -q
인쇄할 수 없는 문자(줄 바꿈 포함)를 로 대체합니다 ?
. 이는 POSIX이지만 BusyBox에서는 지원되지 않습니다. 따라서 BusyBox 지원이 필요한 경우 이름에 줄 바꿈이 포함된 파일 수를 희생하여 무시하십시오.
디렉토리에 하위 디렉토리가 없으면 대부분의 버전은 해당 항목을 find
호출하지 않습니다 stat
(리프 디렉토리 최적화: 링크 수가 2인 디렉토리는 하위 디렉토리를 가질 수 없으므로 find
조건에서 요구하지 않는 한 항목의 메타데이터를 조회할 필요가 없습니다 -type
). 이는 find . | wc -l
디렉터리에 하위 디렉터리가 없고 파일 이름에 개행 문자가 포함되지 않은 경우 디렉터리의 파일 수를 계산하는 이식 가능하고 빠른 방법입니다.
디렉터리에 하위 디렉터리가 없지만 파일 이름에 개행 문자가 포함될 수 있는 경우 다음 중 하나를 시도해 보십시오. 지원되는 경우 두 번째 디렉터리가 더 빠르지만 눈에 띄게 그렇지 않을 수도 있습니다.
find -print0 | tr -dc \\0 | wc -c
find -printf a | wc -c
반면, find
디렉토리에 하위 디렉토리가 있는 경우 :을 사용하지 마십시오. 또는 각 항목을 find . -maxdepth 1
호출할 수도 있습니다 stat
(적어도 GNU 찾기 및 BusyBox 찾기 사용). 정렬(#2)을 피할 수 있지만 inode 조회(#3)가 발생하여 성능이 저하됩니다.
외부 도구가 없는 셸에서는 .run을 사용하여 현재 디렉터리의 파일 수를 계산할 수 있습니다 set -- *; echo $#
. 이는 도트 파일(이름이 로 시작하는 파일 .
)을 놓치고 빈 디렉토리에 0 대신 1을 보고합니다. 이는 외부 프로그램을 시작할 필요가 없기 때문에 작은 디렉터리의 파일 수를 계산하는 가장 빠른 방법이지만(zsh 제외) 정렬 단계(#2)로 인해 더 큰 디렉터리에 시간을 낭비합니다.
Bash에서 이는 현재 디렉터리의 파일 수를 계산하는 안정적인 방법입니다.
shopt -s dotglob nullglob a=(*) echo ${#a[@]}
ksh93에서 이는 현재 디렉터리의 파일 수를 계산하는 안정적인 방법입니다.
FIGNORE='@(.|..)' a=(~(N)*) echo ${#a[@]}
zsh에서 이는 현재 디렉터리의 파일 수를 계산하는 안정적인 방법입니다.
a=(*(DNoN)) echo $#a
이 옵션을 설정 한 경우
mark_dirs
해당 옵션을 꺼야 합니다.a=(*(DNoN^M))
.모든 POSIX 셸에서 이는 현재 디렉터리의 파일 수를 계산하는 안정적인 방법입니다.
total=0 set -- * if [ $# -ne 1 ] || [ -e "$1" ] || [ -L "$1" ]; then total=$((total+$#)); fi set -- .[!.]* if [ $# -ne 1 ] || [ -e "$1" ] || [ -L "$1" ]; then total=$((total+$#)); fi set -- ..?* if [ $# -ne 1 ] || [ -e "$1" ] || [ -L "$1" ]; then total=$((total+$#)); fi echo "$total"
zsh를 제외하고 이러한 모든 방법은 파일 이름을 정렬합니다.
답변2
find /foo/foo2/ -maxdepth 1 | wc -l
내 컴퓨터에서는 훨씬 빠르지만 로컬 .
디렉터리가 개수에 추가됩니다.
답변3
ls -1U
파이프라인은 파일 항목을 정렬하려고 시도하지 않고 디스크의 폴더에 정렬된 대로 읽기만 하므로 리소스 비용이 적게 듭니다. 또한 출력이 적습니다. 즉 wc
, .
ls -f
which is more 또는 less 를 사용할 수도 있습니다 ls -1aU
.
파이핑 없이 명령을 통해 이를 수행하는 자원 효율적인 방법이 있는지 모르겠습니다.
답변4
당신은 시도 할 수 있습니다perl -e 'opendir($dh,".");$i=0;while(readdir $dh){$i++};print "$i\n";'
쉘 파이프와 시간을 비교하는 것은 흥미로울 것입니다.