比較 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/'
    

    本質上,分隔輸出中兩個元素的字元或字串應%sprintf格式字串之後和\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: 新增一個新的的每次呼叫print並運行 給出的腳本-e
  • @A=split(//,$ARGV[0]):$ARGV[0]是命令列上給出的第一個參數。這將拆分它,使每個字元成為 array 中的元素@A
  • @B=split(//,$ARGV[1]);:與上面相同,但對於第二個參數和陣列@B
  • for $i (0..$#A): for 迴圈。這設定為 0 並加一,直到達到數組( )$i中元素數量的值。這是迭代數組中所有元素的簡單方法,因為will be , , ... , 。@A$#A$A[$i]$A[0]$A[1]$A[$#A]
  • $A[$i] eq $B[$i] ? push @S,$A[$i] : last: 這是 C 風格的簡寫符號。一般格式是foo ? bar : baz和 意思是「如果foo為真,則執行 do bar,否則執行 do baz。這裡,我們正在測試array 的n第 th (或$i本例中為 th )元素是否@A與 array 中的相應元素相同@B。如果是,我們將其新增至第三個陣列中,@S如果不是,請使用 退出循環last
  • print @S:列印數組@S,共享元素。

這兩個解決方案非常相似,唯一的區別是,@A=split(/\./,$ARGV[0])將在 上拆分.,將它們從結果數組中刪除,並print join ".", @S列印 的所有元素,@S並在它們之間使用 a .

答案3

正如我在問題下面的評論中提到的,我找到了一個稍微簡單的awk解決方案:連接兩個數字以創建一個長字串,用空格替換所有點(以允許使用空格作為awk 中的預設欄位分隔符),以及使用 file+half 遍歷字串比較欄位。

基本指令

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

比較直到第一個不相等的數字

awk 有一個非常有用的函數substr(string,X,Y),它允許剪切或「裁剪」字串,從第一個字元 (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}'

特別感謝 terdon 建議使用 substr 函數,我以前甚至不知道它的存在

答案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。同樣,second將包含 list 第二個元素的部分a

  • 然後我們迭代firstsecond檢查每個元素與其相同索引對應項的相等性,如果它們相同,則其中一個元素保存在單獨的列表中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

相關內容