연속된 숫자가 포함된 숫자 목록 필터링

연속된 숫자가 포함된 숫자 목록 필터링

많은 텍스트 파일을 필터링하고 싶습니다. 각 파일에는 긴 숫자 목록이 포함되어 있습니다. 파일은 각 숫자의 연속 숫자 수를 기준으로 필터링되어야 합니다.

예시 목록은 다음 파일 중 하나를 구성합니다.

입력 파일 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 123456789ABCDEF4자리 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자리 시퀀스의 경우:rgack

grep -e 12345 -e 23456 […] -e 65432 -e 54321

man grep자세한 내용과 사용법은 참조하세요그렉의 위키Bash를 빠르게 배우세요.

관련 정보