CentOS 5.9
다른 날 디렉토리에 많은 파일이있는 문제가 발생했습니다. 그것을 계산하기 위해, 나는 달렸다ls -l /foo/foo2/ | wc -l
단일 디렉토리에 백만 개가 넘는 파일이 있다는 것이 밝혀졌습니다 (긴 이야기-근본 원인이 수정되었습니다).
내 질문은 : 더 빠른 계산 방법이 있습니까? 카운트를 얻는 가장 효율적인 방법은 무엇입니까?
답변
짧은 답변:
\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 find 및 BusyBox find 사용). 정렬 (# 2)을 피하지만 성능을 저하시키는 inode 조회 (# 3)의 가격을 지불합니다.
외부 도구가없는 쉘에서을 사용하여 현재 디렉토리의 파일 수를 실행할 수 있습니다 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를 제외한 파일 이름을 정렬합니다.
답변
find /foo/foo2/ -maxdepth 1 | wc -l
내 컴퓨터에서 상당히 빠르지 만 로컬 .
디렉토리가 카운트에 추가됩니다.
답변
ls -1U
파이프는 파일 항목을 정렬하지 않고 디스크의 폴더에서 정렬 될 때 파일을 읽기만하기 때문에 약간의 리소스를 소비해야합니다. 또한 출력이 적어 약간의 작업이 줄어 듭니다 wc
.
당신은 또한 ls -f
어느 단축키를 사용할 수 있습니다 ls -1aU
.
파이프없이 명령을 통해 리소스를 효율적으로 수행 할 수있는 방법이 있는지 모르겠습니다.
답변
또 다른 비교 포인트. 쉘 oneliner가 아니지만이 C 프로그램은 수퍼 플로어를 수행하지 않습니다. 숨겨진 파일은 출력과 일치하도록 무시됩니다 ls|wc -l
( ls -l|wc -l
첫 번째 출력 행의 총 블록으로 인해 하나가 꺼져 있음).
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <error.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int file_count = 0;
DIR * dirp;
struct dirent * entry;
if (argc < 2)
error(EXIT_FAILURE, 0, "missing argument");
if(!(dirp = opendir(argv[1])))
error(EXIT_FAILURE, errno, "could not open '%s'", argv[1]);
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_name[0] == '.') { /* ignore hidden files */
continue;
}
file_count++;
}
closedir(dirp);
printf("%d\n", file_count);
}
답변
당신은 시도 할 수 있습니다 perl -e 'opendir($dh,".");$i=0;while(readdir $dh){$i++};print "$i\n";'
쉘 파이프와 타이밍을 비교하는 것이 흥미로울 것입니다.
답변
에서 이 답변 , 나는 가능한 솔루션으로이 일을 생각할 수 있습니다.
/*
* List directories using getdents() because ls, find and Python libraries
* use readdir() which is slower (but uses getdents() underneath.
*
* Compile with
* ]$ gcc getdents.c -o getdents
*/
#define _GNU_SOURCE
#include <dirent.h> /* Defines DT_* constants */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
struct linux_dirent {
long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[];
};
#define BUF_SIZE 1024*1024*5
int
main(int argc, char *argv[])
{
int fd, nread;
char buf[BUF_SIZE];
struct linux_dirent *d;
int bpos;
char d_type;
fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
if (fd == -1)
handle_error("open");
for ( ; ; ) {
nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
if (nread == -1)
handle_error("getdents");
if (nread == 0)
break;
for (bpos = 0; bpos < nread;) {
d = (struct linux_dirent *) (buf + bpos);
d_type = *(buf + bpos + d->d_reclen - 1);
if( d->d_ino != 0 && d_type == DT_REG ) {
printf("%s\n", (char *)d->d_name );
}
bpos += d->d_reclen;
}
}
exit(EXIT_SUCCESS);
}
위의 C 프로그램을 파일을 나열해야하는 디렉토리에 복사하십시오. 그런 다음 다음 명령을 실행하십시오.
gcc getdents.c -o getdents
./getdents | wc -l
답변
외부 프로그램이 필요하지 않지만 얼마나 효율적인지 모르는 bash 전용 솔루션 :
list=(*)
echo "${#list[@]}"