假设我有一个名为的数组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]
是命令行中给出的第一个参数。这将拆分它,使每个字符成为数组中的一个元素@A
。@B=split(//,$ARGV[1]);
:与上面相同,但针对的是第二个参数和数组@B
。for $i (0..$#A)
:for 循环。这将设置$i
为 0,并将其加一,直到其值为数组中元素的数量@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
为真,则执行bar
,否则执行baz
”。在这里,我们测试数组的n
第 th 个(或$i
本例中的第 th 个)元素@A
是否与数组中的对应元素相同@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
比较至第一个不等的数字
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
将包含列表的第二个元素的部分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