많은 텍스트 파일을 필터링하고 싶습니다. 각 파일에는 긴 숫자 목록이 포함되어 있습니다. 파일은 각 숫자의 연속 숫자 수를 기준으로 필터링되어야 합니다.
예시 목록은 다음 파일 중 하나를 구성합니다.
입력 파일 data.log
:
12365
91738
349874
128152639
1234
7654
08767
1234567
제 생각에는:
1- 두 개의 연속 숫자를 포함하는 숫자로 이 목록을 필터링합니다. 예상되는 출력은 다음과 같습니다.
12365
349874
128152639
1234
7654
08767
1234567
2- 3개의 연속 숫자를 포함하는 숫자로 이 목록을 필터링합니다. 예상되는 출력은 다음과 같습니다.
12365
349874
1234
7654
08767
1234567
3- 4개의 연속 숫자를 포함하는 숫자로 이 목록을 필터링합니다. 예상되는 출력은 다음과 같습니다.
1234
7654
1234567
4- 5개의 연속 숫자를 포함하는 숫자로 이 목록을 필터링하면 예상되는 출력은 다음과 같아야 합니다.
1234567
숫자에서 연속된 숫자의 순서가 가장 작은 것에서 가장 큰 것(예: 1234...등) 또는 큰 것에서 작은 것(예: 54321)인 경우 출력에 포함되어야 합니다.
답변1
grep
, tee
및 를 사용하여 -isms로 가득 찬 까다로운 작은 함수를 rev
만듭니다 .bash
dqs() { a=${2:-123456789} ; [ "$1" -ge 2 ] &&
grep -iF "$(eval eval printf '%s\\\\n' \\$\\{a:\{0..$((${#a}-$1))\}:$1\\} |
tee >(rev) )"
}
테스트를 받아보세요:
dqs 5 < data.log
1234567
dqs 4 < data.log
1234
7654
1234567
dqs 3 < data.log
12365
349874
1234
7654
08767
1234567
작동 방식:
printf
원하는 길이의 시퀀스 목록을 인쇄합니다(예:123,234,등.), tee
거울을 부착하십시오 (즉오른쪽에서 왼쪽으로 또는 뒤로) rev
그런 다음 grep -f <(...)
해당 목록에 있는 항목에 대해 표준 입력을 검색합니다.
이 시퀀스 목록을 만들려면 일반적으로 루프 또는 seq
둘 다 필요하지만 여기서는 다음을 사용하여 수행합니다.bash
서열 표현, 와 결합하위 문자열 확장, 좀 남았어산수. 그러나 이는 bash
인터프리터가 필요한 순서대로 이러한 작업을 수행할 수 없기 때문에 불가능합니다 . 그래서 우리는 올바른 순서로 일을 수행하도록 하기 위해 eval eval
몇 가지 전략을 사용합니다 .\\\
bash
여기서는 [ "$@" -gt 0 ] &&
기능적으로 필요하지 않지만 가지고 있는 것이 더 안전합니다. 그것은 거기에 dqs
있음 을 보장합니다단 하나뿐인숫자 인수입니다. 그렇지 않으면 grep
실행되지 않습니다. 이렇게 하면 eval eval
어떤 작업도 수행되지 않습니다 .사악한.
보너스: 두 번째 인수를 추가하면 다른 시퀀스 123456789
가 변경되어도 코드는 계속 작동합니다. 예를 들어, dqs 4 123456789ABCDEF
4자리 16진수 시퀀스(및 역순)가 검색되고 dqs 3 $(printf %s {a..z})
3자리 알파벳 시퀀스가 검색됩니다.
# search `man bash` for the three most popular words
# that have 3 three char alphabetic runs
man bash | tr ' ' '\n' | sort | uniq -c | sort -gr |
dqs 3 $(printf '%s' {a..z}) | head -3
산출:
92 first
76 default
38 environment
답변2
매우 큰 파일이 많으면 awk의 정규식 일치가 느려질 수 있습니다. 한 가지 접근 방식은 grep을 사용하여 어려운 작업을 수행하고 awk를 사용하여 검색할 문자열 목록을 작성하는 것입니다(이를 하드코딩하고 싶지 않기 때문입니다). 즉
$grep -E '12|98|23|87|34|76|45|65|56|54|67|43|78|32|89|21' data.log
2명의 캐릭터에 대해 이 작업을 수행하는 것이 가능하지만 최대 9명의 캐릭터에 대해 수행할 수 있기를 원합니다. 여러 패턴 검색을 지원하려면 grep을 확장하려면 -E가 필요합니다(12|98은 두 가지 패턴입니다). 일반 ol' grep에서는 이를 허용하지 않습니다.
awk는 문자열 123456789를 반복하여 연속 세그먼트를 꺼낼 수 있지만 앞으로 및 뒤로 이동하려고 합니다.
$awk 'BEGIN {f=123456789 ; b=987654321 ; for(i=1;i<9;i++) print substr(f,i,2),substr(b,i,2)}'
12 98
23 87
34 76
45 65
56 54
67 43
78 32
89 21
길이가 2로 하드코딩되지 않도록 뭔가를 추가해 보겠습니다(-vn=3은 awk 스크립트 내에서 변수 n=3을 설정합니다).
$awk -vn=3 'BEGIN {f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}'
123 987
234 876
345 765
456 654
567 543
678 432
789 321
그리고 (거의 다!) 출력 레코드 구분 기호(ORS)와 출력 필드 구분 기호(OFS)를 |
$awk -vn=3 'BEGIN {ORS="|" ; OFS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}'
123|987|234|876|345|765|456|654|567|543|678|432|789|321|
321 이후의 마지막 파이프를 제거해야 합니다. 그렇지 않으면 grep이 모든 항목과 일치하므로 sed '.$//'를 추가하여 문자열 끝($) 앞의 마지막 문자를 아무것도 없는 것으로 바꿉니다.
$awk -vn=3 'BEGIN {ORS="|" ; OFS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}' | sed 's/.$//'
이제 이것을 쉘 스크립트에 넣어서 일반적인 검색을 할 수 있습니다:
$cat t.sh
#!/bin/bash
grep -E `awk --assign n=$1 'BEGIN {OFS="|" ; ORS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}' | sed 's/.$//'` $2
$chmod 775 t.sh
$./t.sh 4 data.log
1234
7654
1234567
답변3
많은 대용량 파일은 이 작업을 신속하게 수행해야 함을 나타냅니다. 이것은 다음을 의미합니다while read
루팅이 불가능해요. 여기서 깨달아야 할 한 가지는 각 운동이 (적어도) 작은 패턴 세트 중 하나와 일치하는 것으로 축소될 수 있다는 것입니다.진짜또는 와 같은 빠른 사용 grep
또는 유사한 도구 . 예를 들어, 5자리 시퀀스의 경우:rg
ack
grep -e 12345 -e 23456 […] -e 65432 -e 54321
man grep
자세한 내용과 사용법은 참조하세요그렉의 위키Bash를 빠르게 배우세요.