특정 명명 시스템을 사용하는 폴더에 많은 수의 파일이 있습니다. 다음과 같이 보입니다.
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*
awk
awk
_
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