패턴별로 파일 이름 그룹화 및 개수 계산

패턴별로 파일 이름 그룹화 및 개수 계산

특정 명명 시스템을 사용하는 폴더에 많은 수의 파일이 있습니다. 다음과 같이 보입니다.

my_file_A_a.txt
my_file_A_d.txt
my_file_A_f.txt
my_file_A_t.txt
my_file_B_r.txt
my_file_B_x.txt
my_file_C_f.txt
my_file_D_f.txt
my_file_D_g.txt
my_file_E_r.txt

다음과 같은 결과를 반환하는 명령줄 또는 일련의 명령(임시 파일을 사용할 수 있고 쓰기 권한이 있음)을 원합니다.

A: 4
B: 2
C: 1
D: 2
E: 1

많은 명령으로 수행할 수 있지만 ls -1 *A* | wc -l계산할 "그룹"이 수백 개이므로 시간이 오래 걸립니다.

또한 각 그룹의 이름은 고유합니다. 그룹 이 있고 그룹 A이 있지만 그룹 B은 없습니다 AB.

답변1

파일 이름이 "잘 작동"한다고 가정합니다. 즉, 개행 문자가 포함되어 있지 않으며 ls다음 조합이 awk작동합니다.

ls -d my_file* | awk -F'_' 'NF==4{count[$3]++} END{for (i in count) printf "%s: %d\n", i, count[i]}'

ls그러면 시작된 프로그램의 모든 파일을 나열하는 명령의 출력이 리디렉션됩니다 . 프로그램은 as 필드 구분 기호를 사용하고 그룹 번호를 "배열 인덱스"로 사용하는 세 번째 필드를 확인하여 배열에서 항목을 추적합니다.my_file*awkawk_count

마지막으로 각 그룹이 얼마나 자주 발생하는지에 대한 개요를 인쇄합니다.

알아채다

  • 정확히 4개의 필드를 요구함으로써 완전히 잘못된 파일 이름에 대한 "최소" 보호가 제공됩니다. 이 가정은 _예제에서 파일 이름 의 a,,,, d... 부분 에 포함될 수 없습니다.f
  • 출력이 반드시 범주 이름별로 정렬되는 것은 아닙니다. 정렬 순서는 루프 awk에서 배열 인덱스를 탐색하는 방법 에 따라 달라집니다 for (i in count). 정렬이 필요한 경우 다른 파이프를 추가할 수 있습니다 sort. 또는 GNU Awk를 사용하는 경우 다음을 통해 구성 설정을 추가할 수 있습니다.
    BEGIN{PROCINFO["sorted_in"]="@ind_str_asc"}
    
    규칙 이전에 NF==4{...}. 이렇게 하면 배열 인덱스를 기준으로 배열이 탐색되고 사전(ASCII) 순서로 정렬됩니다.
  • 이는 처음에 설명한 제한 사항에 적용되며 파일 이름 구조가 매우 간단하기 때문입니다. 일반적으로 말하자면 그렇다.구문 분석 출력은 권장되지 않습니다.ls.

답변2

for f in my_file_*_*.txt
do
    f="${f#my_file_}"
    printf "%s\n" "${f%%_*.txt}"
done |
sort |
uniq -c

루프 for는 각 파일 이름의 형식을 다시 지정하여 f선행 my_file_및 후행을 제거한 _whatever.txt다음 출력을 정렬하여 uniq각 고유 값의 발생 횟수를 계산하는 데 사용합니다.

답변3

와일드카드를 반복한 다음 bash의 정규식 기능을 사용하여 파일 이름에서 필드를 추출하여 처리합니다.[[ 조건식 구성.

unset collect
declare -A collect
for f in ./*_*_*_*.txt
do 
  [[ $f =~ [^_]+_+[^_]+_+([^_]+)_+[^_]+.txt ]] &&
  ((collect["${BASH_REMATCH[1]}"]++))
done

for group in "${!collect[@]}"
do
  printf '%s: %d\n' "$group" "${collect["$group"]}"
done

대괄호로 묶인 유일한 필드는 밑줄로 구분된 세 번째 필드입니다. 일단 캡처되면 collect연관 배열( )의 값이 증가합니다.

답변4

밑줄로 구분된 4개의 필드를 포함하고 문자열로 끝나는 파일 이름은 .txt확장된 와일드카드 패턴과 일치합니다 +([!_])_+([!_])_+([!_])_+([!_]).txt. 각각은 확장 정규 표현식 +([!_])처럼 밑줄이 아닌 하나 이상의 문자와 일치합니다 .[^_]+

접미사 string 과 함께 처음 두 필드와 마지막 필드를 제거하여 세 번째 필드를 추출할 수 있습니다 .txt.

#!/bin/bash

shopt -s extglob nullglob

names=( +([!_])_+([!_])_+([!_])_+([!_]).txt )
names=( "${names[@]#+([!_])_+([!_])_}" )
names=( "${names[@]%_+([!_]).txt}" )

printf '%s\n' "${names[@]}" | sort | uniq -c

스크립트는 파일 이름의 세 번째 필드에 새 줄이 포함되어 있지 않다고 가정합니다.

질문의 예제 파일 이름을 테스트합니다.

$ ls
list              my_file_A_f.txt   my_file_B_x.txt   my_file_D_g.txt
my_file_A_a.txt   my_file_A_t.txt   my_file_C_f.txt   my_file_E_r.txt
my_file_A_d.txt   my_file_B_r.txt   my_file_D_f.txt   script
$ ./script
   4 A
   2 B
   1 C
   2 D
   1 E

간단한 스크립트로 awk필터링 하고 원하는 형식으로 변환할 수 있습니다.

$ ./script | awk '{ printf "%s: %d\n", $2, $1 }'
A: 4
B: 2
C: 1
D: 2
E: 1

이름이 제대로 동작한다면, 즉 이름에 개행 문자가 포함되어 있지 않다면 스크립트를 약간 단순화하고 이를 사용할 수 있습니다 cut.

#!/bin/bash

shopt -s extglob nullglob

printf '%s\n' +([!_])_+([!_])_+([!_])_+([!_]).txt |
cut -d _ -f 3 | sort | uniq -c

관련 정보