样本数据
wolf@linux:~$ cat data.csv
A,4.4.4.4,4.4.4.5,4.4.4.6,3.3.3.3,3.3.3.4
B,1.1.1.1,1.1.1.1,1.1.1.2,1.1.1.3,3.3.3.3
C,1.1.1.1,1.1.1.1,1.1.1.1,1.1.1.1,1.1.1.1
D,2.2.2.1,2.2.2.1,2.2.2.2,2.2.2.3,2.2.2.4
wolf@linux:~$
该样本中有一些重复的数据。例如,有两个3.3.3.3
位于 A 行和 B 行。
wolf@linux:~$ egrep 3.3.3.3 data.csv
A,4.4.4.4,4.4.4.5,4.4.4.6,3.3.3.3,3.3.3.4
B,1.1.1.1,1.1.1.1,1.1.1.2,1.1.1.3,3.3.3.3
wolf@linux:~$
现在我只对第一列A,B,C,D
以及相关数据感兴趣。
这是我需要找到的 4 个数据。
2.2.2.3
3.3.3.3
4.4.4.4
5.5.5.5
所以,我在egrep中使用|
and-o
wolf@linux:~$ egrep '2.2.2.3|3.3.3.3|4.4.4.4|5.5.5.5' data.csv
A,4.4.4.4,4.4.4.5,4.4.4.6,3.3.3.3,3.3.3.4
B,1.1.1.1,1.1.1.1,1.1.1.2,1.1.1.3,3.3.3.3
D,2.2.2.1,2.2.2.1,2.2.2.2,2.2.2.3,2.2.2.4
wolf@linux:~$
输出很好,但是里面的数据太多了。我只想要相关数据和第一列。
所以,这是另一个尝试-o
wolf@linux:~$ egrep -o '2.2.2.3|3.3.3.3|4.4.4.4|5.5.5.5' data.csv
4.4.4.4
3.3.3.3
3,3.3.3
2.2.2.3
wolf@linux:~$
现在的问题是我无法得到第一列(A/B/C/D
)
下次尝试
wolf@linux:~$ egrep '2.2.2.3|3.3.3.3|4.4.4.4|5.5.5.5' data.csv | cut -d , -f 1
A
B
D
wolf@linux:~$
我得到了第一列,但没有得到数据。通过查看输出,我不知道A
同时代表3.3.3.3
和 ,4.4.4.4
因为它没有显示在输出上。
这并不是我真正想要的输出。我现在的想法是这样的
所需输出
输入
2.2.2.3
3.3.3.3
4.4.4.4
5.5.5.5
输出
D 2.2.2.3
A,B 3.3.3.3
A 4.4.4.4
- 5.5.5.5
不幸的是,我现在想不出解决方案。请帮忙
答案1
$ cat script.awk
NR == FNR {
a[$0]
next
}
{
for (i = 2; i <= NF; i++) {
for (k in a) {
if ($i == k) {
a[k] = a[k] ? a[k] "," $1 : $1
}
}
}
}
END {
for (k in a) {
print a[k] "\t" k
}
}
运行它作为:
$ cat search.txt
2.2.2.3
3.3.3.3
4.4.4.4
5.5.5.5
$ awk -F, -f script.awk search.txt data.csv
A,B 3.3.3.3
D 2.2.2.3
A 4.4.4.4
5.5.5.5
答案2
首先请注意,正则表达式不仅2.2.2.3
会匹配(与匹配任何字符的正则表达式运算符一样)也会匹配内部。2.2.2.3
212.243
.
22.2.2.36
在这里,我会使用perl
:
<data.csv perl -F, -lane '
BEGIN {for (@l = qw{2.2.2.3 3.3.3.3 4.4.4.4 5.5.5.5}) {$v{$_} = []}}
for (grep $v{$_}, @F[1..$#F]) {push @{$v{$_}}, $F[0]}
END {for (@l) {print(join(",", @{$v{$_}}) || "-", "\t$_")}}'
这使:
D 2.2.2.3
A,B 3.3.3.3
A 4.4.4.4
- 5.5.5.5
要回答主题中的问题,为了grep -o
(-o
顺便说一句,作为非标准扩展)在输出行上报告输入行的多个部分,您可以使用pcregrep
:
<data.csv pcregrep -o1 -o2 --om-separator=$'\t' \
'^([^,]*).*?,(2\.2\.2\.3|3\.3\.3\.3|4\.4\.4\.4|5\.5\.5\.5)(,|$)'
但这只能报告每行一个单词。这里给出:
A 4.4.4.4
B 3.3.3.3
D 2.2.2.3
使用grep -Po
,假设grep
使用类似 perl 的正则表达式支持构建(顺便说一句,在大多数实现中使用 PCRE 进行类似 perl 的正则表达式匹配,因此它与 相同pcregrep -o
),您可以这样做:
$ grep -Po '^[^,]*+(?=.*?(?1))|((?<![^,])(2\.2\.2\.3|3\.3\.3\.3|4\.4\.4\.4|5\.5\.5\.5)(?![^,]))' data.csv
A
4.4.4.4
3.3.3.3
B
3.3.3.3
D
2.2.2.3
这是first-field-provided-there-is-matching-data|matching-data
.
这里provided-there-is-matching-data
是用(?=...)
正向前瞻运算符实现的,这意味着前提是接下来的内容匹配...
,这里(?1)
,正则表达式存储在第一个捕获组中,因此匹配数据后面有任意数量的字符 ( .*?
)。
对于匹配数据,我们使用(2\.2\.2\.3|3\.3\.3\.3|4\.4\.4\.4|5\.5\.5\.5)
s.
进行转义,但使用一些否定环视运算符 ((?<!...)
和(?!...)
) 表示:前提是之前和之后的内容是不是字符以外的字符,
确保它们与 csv 字段的内容完全匹配。
答案3
一种简单的方法如下。要查找的键在名为keys.txt 的文件中每行列出一个键,并放在perl 命令的标准输入上,同时要搜索的数据作为参数放置。
$ < keys.txt \
perl -F, -lane '
$.==1 && chomp(@keys = <STDIN>);
$_ .= "$F[0]," for @h{
grep { my $k = $_; grep(($_ eq $k), @F)} @keys;
}}{$, = "\t";
print((($h{$_} //= "-") =~ s/,$//r), $_) for @keys;
' data.csv
D 2.2.2.3
A,B 3.3.3.3
A 4.4.4.4
- 5.5.5.5
答案4
使用米勒(https://github.com/johnkerl/miller)并运行
mlr --c2t -N reshape -r "^[^1]$" -o item,value \
then filter '$value=~"(2\.2\.2\.3|3\.3\.3\.3|4\.4\.4\.4|5\.5\.5\.5)"' \
then cut -x -f item \
then nest --implode --values --across-records -f 1 --nested-fs "," \
then reorder -f value data.csv >output
你将会拥有
4.4.4.4 A
3.3.3.3 A,B
2.2.2.3 D
然后使用您的过滤器列表
$ cat list
8.8.8.8
2.2.2.3
3.3.3.3
4.4.4.4
5.5.5.5
你可以跑
mlr --tsv -N join --ul -j 1 -f list then unsparsify output
具有
4.4.4.4 A
3.3.3.3 A,B
2.2.2.3 D
8.8.8.8
5.5.5.5