假設我有一個名為 的陣列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
: 新增一個新的我的每次呼叫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
為真,則執行 dobar
,否則執行 dobaz
。這裡,我們正在測試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
。然後我們迭代
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