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


182

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

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

답변:


243

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

ls -1q log* | wc -l

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

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


10
내가 사용하지 것이다 -l즉 필요로하기 때문에, stat(2)각 파일 및 계산의 목적을 위해 아무것도 추가하지 않습니다.
camh

12
ls자식 프로세스를 생성하기 때문에을 사용하지 않습니다 . log*는 쉘이 아닌 확장되어 ls있으므로 간단 echo합니다.
cdarke

2
공백이나 특수 문자가 포함 된 파일 이름이 있으면 에코가 작동하지 않습니다.
Daniel

4
@WalterTross 사실입니다 (효율성은 원래 질문의 요구 사항이 아님). 또한 출력이 터미널이 아닌 경우에도 -q가 줄 바꿈으로 파일을 처리한다는 것을 알았습니다. 그리고이 플래그는 내가 테스트 한 모든 플랫폼과 셸에서 지원됩니다. 당신과 입력에 대한 camh 덕분에 답변을 업데이트!
Daniel

3
해당 디렉토리에 호출 logs된 디렉토리가 있으면 해당 로그 디렉토리 의 내용 도 계산됩니다. 이것은 의도적 인 것이 아닙니다.
mogsie

54

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

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

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


2
아마도 명시 적으로는 Bash-이라고 지적 특히 전적으로 속도를 아직하지 않은 새로운 방문자, 대답 sh와 bash는 차이
tripleee

또한 설정이 해제되지 않은 shopt -u nullglob경우 nullglob시작을 건너 뛰어야합니다 .
tripleee

참고 : 대체 하면 디렉토리 *.log*계산됩니다. 열거하려는 파일의 전통적인 명명 규칙이있는 경우을 name.extension사용하십시오 *.*.
AlainD

52

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

  • 공백, 개행 또는 제어 문자가 포함 된 파일 이름
  • 하이픈으로 시작하는 파일 이름 (이라는 파일을 상상해보십시오 -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단지 현재 디렉토리의 내용을 나열 있기 때문에, 더 이상 필요하지 않습니다.


48

재귀 검색의 경우 :

find . -type f -name '*.log' -printf x | wc -c

wc -c의 출력에서 ​​문자 수를 계산하는 find동안 각 결과에 대해 단일 문자 를 인쇄하도록 -printf x지시 find합니다 x.

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

find . -maxdepth 1 -type f -name '*.log' -printf x | wc -c

6
하더라도 당신은 공백으로 파일이없는, 스크립트의 다른 사용자는 스크립트가 실패하는 원인이 악의적으로 명명 된 파일을 발생할 수 있습니다. 또한 StackOverflow 에서이 문제를 겪는 다른 사람들에게는 줄 바꿈이있는 파일이있을 수 있으며 함정을 알아야합니다.
mogsie

참고로 단순히 나가면 -name '*.log'모든 파일을 계산하여 사용 사례에 필요한 것입니다. 또한 -maxdepth 플래그는 매우 유용합니다.
starmandeluxe 5

2
줄 바꿈 파일 이름이 있으면 여전히 잘못된 결과를 생성합니다. 해결 방법은 다음과 같습니다 find. 그대로 파일 이름 이외의 다른 것을 인쇄하십시오.
tripleee

8

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

이 질문에 대한 정답은 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

6

파일이 많고 우아 shopt -s nullglob하고 bash 배열 솔루션 을 사용하지 않으려면 파일 이름을 인쇄하지 않는 한 찾기 등을 사용할 수 있습니다 (줄 바꿈 포함).

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

log *와 일치하고 다음으로 시작하지 않는 모든 파일을 찾습니다 .*. "not name. *"는 중복되지만 "ls"의 기본값은 도트 파일을 표시하지 않지만 기본값은 점입니다. 찾기 위해 그들을 포함하는 것입니다.

이것은 정답이며 파일 이름이 명령 사이에 전달되지 않기 때문에 던질 수있는 모든 유형의 파일 이름을 처리합니다.

그러나 shopt nullglob답이 가장 좋습니다!


아마 다시 대답하는 대신 원래 답변을 업데이트해야합니다.
qodeninja

사용 find대 사용 ls은 문제를 해결하는 두 가지 다른 방법 이라고 생각 합니다. find항상 기계에 존재하는 것은 아니지만 ls일반적으로
mogsie

2
그러나 라드 상자에는 find아마도 멋진 옵션이 없을 것입니다 ls.
tripleee

1
-maxdepth 1
tripleee

1
이 솔루션은 숨겨진 디렉토리 내의 파일 수를 계산합니다. find기본적으로이 작업을 수행합니다. 숨겨진 하위 폴더가 있다는 것을 모르면 혼동을 일으킬 수 ls있으며 일부 상황에서는 기본적으로 숨겨진 파일을보고하지 않는 것이 유리합니다 .
MrPotatoHead

6

여기에 하나의 라이너가 있습니다.

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

이해하기 위해 인터넷 검색이 필요했지만 이것이 좋습니다! 그래서 set -- 위해 우리가 준비를 제외하고 아무것도하지 않는 $#것을, 쉘 프로그램에 전달 된 명령 행 인수의 수를 저장
xverges

@xverges 예, "shopt -s nullglob"은 숨겨진 파일 (.files)을 세지 않습니다. set-위치 매개 변수의 수 (이 경우 파일 수)를 저장 / 설정하기위한 것입니다. 위치 매개 변수의 수 (파일 수)를 표시하기위한 # $.
zee

3

-R 옵션을 사용하여 재귀 디렉토리 내의 파일과 함께 파일을 찾을 수 있습니다.

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

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

당신은 grep에 패턴을 사용할 수 있습니다


3

중요한 의견

(댓글을 작성하기에 평판이 충분하지 않음)

이것은 버기입니다 :

ls -1q some_pattern | wc -l

설정된 shopt -s nullglob경우 패턴이있는 파일 (CentOS-8 및 Cygwin에서 테스트 된 파일)뿐만 아니라 모든 일반 파일 의 수를 인쇄합니다 . 다른 의미없는 버그가 무엇인지 누가 알 ls수 있습니까?

이것은 정확 하고 훨씬 빠릅니다.

shopt -s nullglob; files=(some_pattern); echo ${#files[@]};

예상되는 작업을 수행합니다.


그리고 실행 시간이 다릅니다.
첫 번째 : 0.006CentOS 및 0.083Cygwin (주의해서 사용하는 경우).
두 번째 : 0.000CentOS 및 0.003Cygwin에서.


2

쉘 함수를 사용하여 이러한 명령을 쉽게 정의 할 수 있습니다. 이 방법은 외부 프로그램이 필요하지 않으며 하위 프로세스를 생성하지 않습니다. 위험한 ls구문 분석을 시도하지 않고 "특수"문자 (공백, 줄 바꿈, 백 슬래시 등) 만 처리합니다. 쉘에서 제공하는 파일 이름 확장 메커니즘에만 의존합니다. 적어도 sh, bash 및 zsh와 호환됩니다.

아래 줄은 호출 count된 인수 수를 인쇄하는 호출 된 함수를 정의합니다 .

count() { echo $#; }

원하는 패턴으로 간단히 호출하십시오.

count log*

글 로빙 패턴이 일치하지 않을 때 결과가 정확하려면 확장시 쉘 옵션 nullglob(또는 failglob-zsh의 기본 동작)을 설정해야합니다. 다음과 같이 설정할 수 있습니다.

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

계산할 대상에 따라 쉘 옵션에 관심이있을 수 있습니다 dotglob.

불행히도 bash를 사용하면 이러한 옵션을 로컬로 설정하기가 쉽지 않습니다. 전역 적으로 설정하지 않으려면 가장 간단한 해결책은 이보다 복잡한 방식으로 함수를 사용하는 것입니다.

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

간단한 구문을 복구 count log*하거나 서브 쉘을 생성하지 않으려면 다음 행을 따라 무언가를 해킹 할 수 있습니다.

# 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'

보너스로이 기능은보다 일반적으로 사용됩니다. 예를 들어 :

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

는 PATH에서 호출하는 스크립트 파일에 기능 (또는 동등한 프로그램 C)을 선회함으로써,도 같은 프로그램으로 구성 될 수 findxargs:

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

2

나는이 답변에 많은 생각을했으며 특히 don't-parse-ls stuff가 있습니다. 처음에는 시도했습니다

<경고! 작동하지 않았습니다>
du --inodes --files0-from=<(find . -maxdepth 1 -type f -print0) | awk '{sum+=int($1)}END{print sum}'
</ 경고! 작동하지 않았습니다>

같은 파일 이름 만 있으면 작동했습니다.

touch $'w\nlf.aa'

하지만 이와 같은 파일 이름을 만들면 실패

touch $'firstline\n3 and some other\n1\n2\texciting\n86stuff.jpg'

나는 마침내 내가 아래에 넣는 것을 생각해 냈습니다. 참고 디렉토리에있는 모든 파일 (하위 디렉토리는 포함하지 않음)을 얻으려고했습니다. @Mat 및 @Dan_Yard의 답변과 함께 @mogsie가 설정 한 요구 사항을 거의 대부분 가지고 있다고 생각합니다 (메모리는 확실하지 않습니다). @mogsie의 대답은 정확하다고 생각합니다. 그러나 ls매우 구체적인 상황이 아닌 한 항상 구문 분석을 피하려고 합니다.

awk -F"\0" '{print NF-1}' < <(find . -maxdepth 1 -type f -print0) | awk '{sum+=$1}END{print sum}'

더 읽기 쉽게 :

awk -F"\0" '{print NF-1}' < \
  <(find . -maxdepth 1 -type f -print0) | \
    awk '{sum+=$1}END{print sum}'

이것은 파일에 대해 특별히 찾기를 수행하여 공백과 줄 바꿈 문제를 피하기 위해 널 문자로 출력을 구분 한 다음 널 문자 수를 계산합니다. 끝에 널 문자가 있으므로 파일 수는 널 문자 수보다 1이 적습니다.

OP의 질문에 대답하기 위해 고려해야 할 두 가지 경우가 있습니다.

1) 비재 귀적 검색 :

awk -F"\0" '{print NF-1}' < \
  <(find . -maxdepth 1 -type f -name "log*" -print0) | \
    awk '{sum+=$1}END{print sum}'

2) 재귀 검색. -name매개 변수 내부의 내용 은 약간 다른 동작 (숨겨진 파일 등)으로 변경해야 할 수도 있습니다.

awk -F"\0" '{print NF-1}' < \
  <(find . -type f -name "log*" -print0) | \
    awk '{sum+=$1}END{print sum}'

이 답변이 내가이 답변에서 언급 한 답변과 어떻게 다른지에 대해 의견을 남기고 싶다면,하십시오.


이 답변 을 얻는 동안이 사고 과정에 도달했습니다 .


1

내가 항상하는 일은 다음과 같습니다.

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


awk 'END{print NR}'와 동일해야합니다 wc -l.
musiphil

0
ls -1 log* | wc -l

즉, 한 줄에 하나의 파일을 나열한 다음 매개 변수를 카운트 라인으로 전환하여 워드 카운트 명령으로 파이프합니다.


ls 출력을 파이핑 할 때는 "-1"옵션이 필요하지 않습니다. 그러나 패턴과 일치하는 파일이 없으면 ls 오류 메시지를 숨길 수 있습니다. "ls log * 2> / dev / null | wc -l"을 제안합니다.
JohnMudd

다니엘의 대답에 따른 토론 은 여기에서도 관련이 있습니다. 이것은 줄 바꿈과 일치하는 디렉토리 또는 파일 이름이 없을 때 잘 작동하지만 좋은 대답은 적어도 이러한 경계 조건을 지적해야하며 큰 대답은 없어야합니다. 많은 버그는 누군가 이해하지 못한 코드를 복사 / 붙여 넣기하기 때문입니다. 따라서 결함을 지적하면 적어도주의해야 할 사항을 이해하는 데 도움이됩니다. (주의 사항을 무시하고 더 많은 버그가 발생하여 코드가 목적에 충분하다고 생각한 후에 변경되었습니다.)
tripleee

-1

모든 것을 계산하려면 ls를 단어 수 줄로 파이프하십시오.

ls | wc -l

패턴으로 계산하려면 먼저 파이프를 grep하십시오.

ls | grep log | wc -l
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.