grep 输出从长到宽

grep 输出从长到宽

我有一个模式文件,我想返回找到该模式的所有行号,但采用宽格式,而不是长/分散。例子:

文件A.txt

Germany
USA
UK

文件B.txt

USA
USA
Italy
Germany
UK
UK
Canada
Canada
Germany
Australia
USA

我做过这样的事情:

grep -nf fileA.txt fileB.txt

这让我返回:

1:USA
2:USA
4:Germany
5:UK
6:UK
9:Germany
11:USA

但是,我想要这样的东西:

Germany 4 9
USA 1 2 11
UK 5 6

答案1

使用 GNU datamash

$ grep -n -x -F -f fileA.txt fileB.txt | datamash -s -t : -g 2 collapse 1
Germany:4,9
UK:5,6
USA:1,2,11

首先用于grep获取与fileB.txt中的行完全匹配的行fileA.txt,并输出匹配的行号以及行本身。

除了问题中使用的选项之外,我还使用-x和。我这样做是为了避免从正则表达式 ( )-F中读取模式,并匹配完整的行,而不是子字符串 ( )。fileA.txt-F-x

然后,该datamash实用程序将其解析为由:- 分隔的字段 ( -t :) 组成的行,-s在第二个字段(-g 2;国家/地区)上对其进行排序 ( ),并将第一个字段(collapse 1;行号)折叠到每个国家/地区的列表中。

tr ':,' '\t\t'然后,您显然可以使用或 以类似的方式将冒号和逗号替换为制表符。

$ grep -n -x -f fileA.txt -F fileB.txt | datamash -s -t : -g 2 collapse 1 | tr ':,' '\t\t'
Germany 4       9
UK      5       6
USA     1       2       11

答案2

使用awk

awk 'NR==FNR        { country[$0]= country[$0]? country[$0] FS NR: NR; next }
     ($0 in country){ print $0, country[$0] }' fileB fileA

或报告“计数:0" 如果 fileA 中的国家/地区名称未出现在 fileB 中,请执行以下操作:

awk 'NR==FNR        { country[$0]= country[$0]? country[$0] FS NR: NR; next }
     ($0 in country){ print $0, country[$0]; next } { print $0, "0" }' fileB fileA

答案3

您可以将 grep 命令输出与 Miller (https://github.com/johnkerl/miller)并运行

grep -nf fileA.txt fileB.txt | \
mlr --c2n --ifs ":" --implicit-csv-header --headerless-csv-output reorder -f 2  then \
nest --implode --values --across-records --nested-fs " " -f 1

你将会拥有

Germany 4 9
USA 1 2 11
UK 5 6

答案4

$ grep -nxFf fileA.txt you fileB.txt   \
| awk -F: '$0 = (length($2) FS $0)' \
| sort -t: -k1,1nr -k3,3 -k2,2n \
| cut -d: -f2- \
| sed -Ee '
  :a
    $!N;y/:/ /
    s/( \S+)\n(.*\1)$/ \2/
  ta
  s/([^\n]*) ([^\n]*)((\n.*)?)$/\2 \1\3/
  P;D
'

输出:

Germany 4 9
USA 1 2 11
UK 5 6

注意:这需要 GNU sed 能够运行,因为使用了字符类中的 \S 和 \n。

相关内容