programing tip

파일을 세는 bash 명령이 있습니까?

itbloger 2020. 6. 27. 11:43
반응형

파일을 세는 bash 명령이 있습니까?


패턴과 일치하는 파일 수를 계산하는 bash 명령이 있습니까?

예를 들어,이 패턴과 일치하는 디렉토리의 모든 파일 수를 가져오고 싶습니다. log*


이 간단한 단일 라이너는 bash뿐만 아니라 모든 쉘에서 작동해야합니다.

ls -1q log* | wc -l

ls -1q는 공백이나 개행과 같은 특수 문자가 포함되어 있어도 파일 당 한 줄을 제공합니다.

출력은 행 수를 계산하는 wc -l로 파이프됩니다.


\nbash를 사용 하여 안전하게 할 수 있습니다 (즉, 공백이 있거나 파일 이름 이 버그로 표시되지 않음 ).

$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}

일치하는 파일이없는 경우 배열nullglob 에서 리터럴 *.log얻지 않도록 활성화해야합니다 . 안전하게 재설정하는 방법에 대한 예 는 'set -x'를 "실행 취소"하는 방법을 참조하십시오 .$logfiles


여기에 많은 답변이 있지만 일부는 고려하지 않습니다.

  • 공백, 개행 또는 제어 문자가 포함 된 파일 이름
  • 하이픈으로 시작하는 파일 이름 (이라는 파일을 상상해보십시오 -l)
  • 글로브가 있다면 (점으로 시작하는 숨겨진 파일, *.log대신log*
  • 글로브와 일치하는 디렉토리 (예 : 디렉토리라는 logs일치 log*)
  • 빈 디렉토리 (예 : 결과는 0)
  • 매우 큰 디렉토리 (모두 나열하면 메모리가 소모 될 수 있음)

다음은 이들 모두를 처리하는 솔루션입니다.

ls 2>/dev/null -Ubad1 -- log* | wc -l

설명:

  • -U원인 ls을 의미하지 정렬 항목으로는 메모리에 목록 전체 디렉토리를로드 할 필요가 없습니다
  • -b그래픽이 아닌 문자에 대해 C 스타일 이스케이프를 인쇄하여 줄 바꿈을로 인쇄하는 것이 중요 \n합니다.
  • -a숨겨진 파일을 포함하여 모든 파일을 인쇄합니다 (glob log*가 숨겨진 파일을 암시하지 않을 때 반드시 필요하지는 않음 )
  • -d목록에 시도하지 않고 디렉토리 밖으로 인쇄 내용이 무엇 디렉토리의 ls일반적으로 할 것을
  • -1 하나의 열에 있는지 확인하십시오 (ls는 파이프에 쓸 때 자동으로 수행하므로 반드시 필요한 것은 아닙니다)
  • 2>/dev/null로그 파일이 0 개이면 오류 메시지를 무시하도록 stderr를 리디렉션합니다. (주 shopt -s nullglob원인이 ls아니라 전체 작업 디렉토리를 나열합니다.)
  • wc -l디렉토리 목록이 생성 될 때 디렉토리 목록을 사용하므로 출력은 ls어느 시점에서나 메모리에 저장되지 않습니다.
  • --파일 이름은 --인수로 이해되지 않도록 사용하여 명령과 분리 됩니다 ls( log*제거 된 경우 ).

log* 전체 파일 목록으로 확장 되어 많은 파일 인 경우 메모리를 소모 할 수 있으므로 grep을 통해 실행하는 것이 좋습니다.

ls -Uba1 | grep ^log | wc -l

이 마지막 것은 많은 메모리를 사용하지 않고 (서브 쉘을 사용하더라도) 매우 큰 파일 디렉토리를 처리합니다. -d단지 현재 디렉토리의 내용을 나열 있기 때문에, 더 이상 필요하지 않습니다.


재귀 검색의 경우 :

find . -type f -name '*.log' | wc -l

wc -w출력의 단어 수를 계산합니다 (bash는 *.log해당 패턴과 일치하는 공백으로 구분 된 파일 목록으로 확장 ). 줄 wc -l수는 계산합니다 ( find한 줄에 하나의 결과를 인쇄 함).

비재 귀적 검색의 경우 다음을 수행하십시오.

find . -maxdepth 1 -type f -name '*.log' | wc -l 

이 질문에 대한 답변이 잘못되었지만 담당자가 적으므로 의견을 추가 할 수 없습니다.

이 질문에 대한 정답은 Mat가 제공합니다.

shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}

허용되는 대답의 문제점은 wc -l이 줄 바꾸기 문자 수를 계산하고 터미널에 '?'로 인쇄하더라도 문자 수를 계산한다는 것입니다. 'ls -l'의 출력에서. 이는 파일 이름에 줄 바꿈 문자가 포함 된 경우 허용 된 답변이 실패 함을 의미합니다. 제안 된 명령을 테스트했습니다.

ls -l log* | wc -l

이름에 개행 문자가 포함 된 패턴과 일치하는 파일이 하나 뿐인 경우에도 2의 값을 잘못보고합니다. 예를 들면 다음과 같습니다.

touch log$'\n'def
ls log* -l | wc -l

If you have a lot of files and you don't want to use the elegant shopt -s nullglob and bash array solution, you can use find and so on as long as you don't print out the file name (which might contain newlines).

find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l

This will find all files that match log* and that don't start with .* — The "not name .*" is redunant, but it's important to note that the default for "ls" is to not show dot-files, but the default for find is to include them.

This is a correct answer, and handles any type of file name you can throw at it, because the file name is never passed around between commands.

But, the shopt nullglob answer is the best answer!


Here is my one liner for this.

 file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)

You can use the -R option to find the files along with those inside the recursive directories

ls -R | wc -l // to find all the files

ls -R | grep php | wc -l // to find the files which contains the word php

you can use patterns on the grep


You can define such a command easily, using a shell function. This method does not require any external program and does not spawn any child process. It does not attempt hazardous ls parsing and handles “special” characters (whitespaces, newlines, backslashes and so on) just fine. It only relies on the file name expansion mechanism provided by the shell. It is compatible with at least sh, bash and zsh.

The line below defines a function called count which prints the number of arguments with which it has been called.

count() { echo $#; }

Simply call it with the desired pattern:

count log*

For the result to be correct when the globbing pattern has no match, the shell option nullglob (or failglob — which is the default behavior on zsh) must be set at the time expansion happens. It can be set like this:

shopt -s nullglob    # for sh / bash
setopt nullglob      # for zsh

Depending on what you want to count, you might also be interested in the shell option dotglob.

Unfortunately, with bash at least, it is not easy to set these options locally. If you don’t want to set them globally, the most straightforward solution is to use the function in this more convoluted manner:

( shopt -s nullglob ; shopt -u failglob ; count log* )

If you want to recover the lightweight syntax count log*, or if you really want to avoid spawning a subshell, you may hack something along the lines of:

# sh / bash:
# the alias is expanded before the globbing pattern, so we
# can set required options before the globbing gets expanded,
# and restore them afterwards.
count() {
    eval "$_count_saved_shopts"
    unset _count_saved_shopts
    echo $#
}
alias count='
    _count_saved_shopts="$(shopt -p nullglob failglob)"
    shopt -s nullglob
    shopt -u failglob
    count'

As a bonus, this function is of a more general use. For instance:

count a* b*          # count files which match either a* or b*
count $(jobs -ps)    # count stopped jobs (sh / bash)

By turning the function into a script file (or an equivalent C program), callable from the PATH, it can also be composed with programs such as find and xargs:

find "$FIND_OPTIONS" -exec count {} \+    # count results of a search

ls -1 log* | wc -l

Which means list one file per line and then pipe it to word count command with parameter switching to count lines.


Here's what I always do:

ls log* | awk 'END{print NR}'

참고URL : https://stackoverflow.com/questions/11307257/is-there-a-bash-command-which-counts-files

반응형