2개의 숫자를 비교하고 비슷한 부분만 복사 sed/grep/awk

2개의 숫자를 비교하고 비슷한 부분만 복사 sed/grep/awk

라는 배열이 있다고 가정합니다 a. 배열에는 2개의 항목이 있습니다 a[1]. a[2]따라서 각 요소에는 숫자 값이 포함됩니다. 두 값 모두 시작 번호는 비슷하지만 끝은 다릅니다. 비슷한 부분만 복사하고 나머지는 무시하겠습니다.

그래서

$ echo ${a[1]}
.1.3.6.1.4.1.232.13600256

$ echo ${a[2]}
.1.3.6.1.4.1.232.13600276

이러한 요소를 비교한 다음 비슷한 부분만 복사하려면 몇 가지 명령이 필요합니다.일치하지 않는 첫 번째 필드까지. 즉, 이 예에서는

산출

similar part is .1.3.6.1.4.1.232

다른 예시

$ echo ${a[1]}
.1.3.6.1.4.1.759.2344.454545

$ echo ${a[2]}
.1.3.6.1.4.1.759.3234.454545

이 예의 출력

similar part is .1.3.6.1.4.1.759

답변1

에서스택 오버플로:

sed에서 문자열에 개행 문자가 포함되어 있지 않다고 가정합니다.

string1="test toast"
string2="test test"
printf "%s\n%s\n" "$string1" "$string2" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1/'

이는 문자열 자체에 개행 문자가 포함되어 있지 않다고 가정합니다.

따라서 다음을 수행할 수 있습니다.

printf "%s\n" "${a[1]}" "${a[2]}" | sed -r 'N;s/^(.*)(\..*)?\n\1.*$/\1/'

그만큼(\..*) ~해야 한다.공통 섹션에서 후행을 제거합니다 .


솔루션에는 다음 두 부분이 포함됩니다.

  • sed두 줄로 작업을 시작합니다 . 이는 를 사용하여 수행되며 N입력에 문자가 없는 것이 보장되면 피할 수 있습니다. 예를 들어, 주어진 요소에 공백이 없기 때문에 대신 다음을 사용할 수 있습니다.

    printf "%s " "${a[1]}" "${a[2]}" | sed -r 's/^(.*)(\..*)? \1.*$/\1/'
    

    기본적으로 출력에서 ​​두 요소를 구분하는 문자 또는 문자열은 형식 지정 문자열 %s의 뒤 printf\1정규식의 앞에 사용되어야 합니다.

  • 정규식을 사용하여 반복되는 문자열 찾기 이에 대한 비결은 잘 알려져 있으며 항상 다음과 같은 변형을 사용합니다.

    (.*)\1
    

    .*모든 문자 집합과 일치하고 ()나중에 참조할 수 있도록 그룹화합니다 \1. 따라서 (.*)\1그 자체가 뒤에 오는 문자 시퀀스입니다.

답변2

여기 Perl 방식이 있습니다. 아이디어는 두 입력 문자열을 별도의 배열로 분할하고 배열을 반복하여 두 항목 모두에서 동일한 항목을 저장하는 것입니다.

perl -le '@A=split(//,$ARGV[0]);@B=split(//,$ARGV[1]); 
          for $i (0..$#A){$A[$i] eq $B[$i] ? push @S,$A[$i] : last} 
          print @S' "${a[0]}" "${a[1]}"
.1.3.6.1.4.1.759.

그러나 여기에는 후행 .. 출력은 (두 변수에서 동일함에도 불구하고) 그렇지 않으므로 제거하려면 대신 다음을 사용하십시오.

$ perl -le '@A=split(/\./,$ARGV[0]);@B=split(/\./,$ARGV[1]); 
            for $i (0..$#A){$A[$i] eq $B[$i] ? push @S,$A[$i] : last} 
            print join ".",@S' "${a[0]}" "${a[1]}"
.1.3.6.1.4.1.759

설명

  • -le: 새로 추가을 호출할 때마다 ine print에서 제공하는 스크립트를 실행합니다 -e.
  • @A=split(//,$ARGV[0]): $ARGV[0]명령줄에 제공된 첫 번째 인수입니다. 이렇게 하면 분할되어 각 문자가 array 의 요소가 됩니다 @A.
  • @B=split(//,$ARGV[1]);: 위와 동일하지만 두 번째 인수와 배열에 대한 것입니다 @B.
  • for $i (0..$#A): for 루프입니다. 이는 $i0으로 설정되고 배열의 요소 수 값을 가질 때까지 1씩 증가합니다 @A( $#A). 이는 , , ... , $A[$i]이기 때문에 배열의 모든 요소를 ​​반복하는 간단한 방법입니다 .$A[0]$A[1]$A[$#A]
  • $A[$i] eq $B[$i] ? push @S,$A[$i] : last: 이것은 C 스타일의 단축 표기법입니다. 일반적인 형식은 foo ? bar : baz"if foois true, do bar, else do 입니다 baz. 여기서 우리는 array의 nth(또는 $ith) 요소가 @Aarray의 해당 요소와 동일한 지 여부를 테스트하고 있습니다 @B. 그렇다면 우리는 세 번째 배열인 에 추가합니다 @S. 그렇지 않은 경우 를 사용하여 루프를 종료합니다 last.
  • print @S: 배열 @S, 공유 요소를 인쇄합니다.

두 가지 솔루션은 매우 유사합니다. 유일한 차이점은 로 @A=split(/\./,$ARGV[0])분할하여 .결과 배열에서 제거하고 그 사이 에 print join ".", @S의 모든 요소를 ​​인쇄한다는 것입니다 .@S.

답변3

질문 아래의 설명에서 언급했듯이 다소 간단한 awk해결책을 찾았습니다. 두 숫자를 연결하여 하나의 긴 문자열을 만들고 모든 점을 공백으로 바꾸고(awk에서 공백을 기본 필드 구분 기호로 사용할 수 있도록 허용) 파일+절반을 사용하여 문자열 비교 필드를 통과합니다.

기본 명령

printf ${a[1]}${a[2]} | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x };}'

나는 이것을 gawk와 mawk로 테스트했고 둘 다에서 일했습니다.

첫 번째 예( .1.3.6.1.4.1.232.13600256 및 .1.3.6.1.4.1.232.13600276 )의 출력은 다음과 같습니다.

$ printf ${a[1]}${a[2]} | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x };}'
.1.3.6.1.4.1.232

다중 비교

여러 문자열을 동시에 비교하려면 문자열을 연결하고 printf에서 줄 바꿈으로 구분한 다음 awk 명령 끝에 다음과 같이 printf를 추가하세요.

printf "${a[1]}${a[2]}\n${a[3]}${a[4]}" | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}'

산출:

$ printf "${a[1]}${a[2]}\n${a[3]}${a[4]}" | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}'
.1.3.6.1.4.1.232 # same for a[1] and a[2]
.1.3.6.1.4.1.759 # same for a[3] and a[4]

출력 제한

이제 kos의 의견은 OP가 7개의 숫자만 표시하기를 원한다는 점을 적절히 지적했습니다. 이를 위해 cut -d'.' -f1-8명령에 파이프를 추가할 수 있습니다. 다음과 같습니다:

printf "${a[5]}${a[6]}" | mawk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8

내 터미널의 샘플 출력은 다음과 같습니다.

$ a[5]=.1.3.6.1.4.1.232.13600256.885


$ a[6]=.1.3.6.1.4.1.232.13600256.885


$ printf "${a[5]}${a[6]}" | mawk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8
.1.3.6.1.4.1.232.13600256.885


 half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8                      <
.1.3.6.1.4.1.232

더욱 단순화

다시 말하지만, 모든 것을 awk 스크립트에 넣을 수 있습니다

#!/usr/bin/awk -f

{
 gsub("\\."," "); 
 half=NF/2
}; 

{ 
 for ( x=1; x<=half; x++ ) { 
    if ( $x==$(x + half) ) printf "."$x 
  }; 
  printf "\n"
}

샘플 실행:

$ printf "${a[5]}${a[6]}" | num-comp.awk | cut -d'.' -f1-8                     
.1.3.6.1.4.1.232

같지 않은 첫 번째 숫자까지 비교

substr(string,X,Y)Awk에는 첫 번째 문자(x)부터 끝 문자(Y)까지 문자열을 자르거나 "자르기"할 수 있는 매우 유용한 기능이 있습니다 . 이를 알고 있으므로 두 숫자를 한 문자열의 두 필드로 가져와 while 루프를 통해 실행해 보겠습니다. 더 이상 동일하지 않을 때까지 하위 문자열 길이(시작부터 끝까지)를 계속 늘릴 것입니다. 동일하지 않은 하위 문자열을 발견하면 종료하고 마지막으로 알려진 동일한 하위 문자열을 인쇄합니다.

echo ".1.3.6.1.4.1.232.13600256\t.1.3.6.1.4.1.232.13600276" | awk 'BEGIN{i=1}{ while(substr($1,1,i)==substr($2,1,i)){var=substr($1,1,i);i++};} END{print var}'

이전에는 존재조차 몰랐던 substr 함수 사용을 제안한 terdon에게 특별히 감사드립니다.

답변4

python작업을 수행할 수 있는 작은 함수를 정의할 수 있습니다 .

#!/usr/bin/env python2
import itertools
def common_portion(a):
    first = a[0].split('.')
    second = a[1].split('.')
    result = []
    for (i, j) in itertools.izip(first, second):
        if i == j:
            result.append(i)
        else:
            break
    return 'Similar part is ' + '.'.join(result)
  • 함수에 대한 입력으로 확인하려는 문자열이 포함된 목록을 제공해야 합니다.

  • first.변수에는 ( ) 로 분할된 입력 목록의 첫 번째 요소 부분이 포함됩니다 a[0].split. 마찬가지로 secondlist 의 두 번째 요소 부분도 포함됩니다 a.

  • 그런 다음 반복 first하여 second각 요소가 동일한 색인 대응 요소와 동일한지 확인합니다. 동일하면 그 중 하나가 별도의 목록에 저장됩니다 result. 첫 번째 차이점을 발견할 때마다 루프에서 벗어났습니다.

  • .마지막으로 필드를 s( '.'.join(result)) 로 결합하여 원하는 결과를 인쇄했습니다.

시험 :

print common_portion(['.1.3.6.1.4.1.232.13600256', '.1.3.6.1.4.1.232.13600276'])

Similar part is .1.3.6.1.4.1.232


print common_portion(['.1.3.6.1.4.1.759.2344.454545', '.1.3.6.1.4.1.759.3234.454545'])

Similar part is .1.3.6.1.4.1.759

관련 정보