select具有n个相同字段的行,在一个哨兵字符之后

在我的文件中,每行包括由空格分隔的五个数字字段(前后有更多字段)。 通过shell脚本,我需要能够在这五个数字字段中正确地select具有3,4和5个相同条目的行(即,三个单独的search,使得在这些字段中search具有3个匹配的行不会也返回行在这些领域有4或5场比赛)。

要find相关的字段,我的search将不得不find第一个开放和闭合的圆括号对。 在括号closures之后,紧随其后的五个字段是我感兴趣的字段。一个潜在的复杂性:有时一个或多个数字字段被一个短划线/连字符代替,而不是一个数字。 一种可能的简化方法:五个字段将以(非严格)升序排列,并且任何连字符条目将总是在其余的数字字段之前。

我会感谢一些sed / awk的build议。 非常感谢!

[编辑]:我可以提取相关的领域(详见下面的评论),因此上面的透视段是不必要的。 下面是抽取相关字段的示例数据:

109 110 111 111 112 110 110 111 111 112 99 99 99 112 112 99 99 99 112 112 100 101 101 112 112 102 102 102 112 112 102 102 103 112 112 102 103 103 112 112 102 104 104 112 112 102 104 104 112 112 103 104 104 112 112 102 105 105 112 112 102 105 105 112 112 103 105 105 112 112 102 106 106 112 112 102 106 107 112 112 103 106 107 112 112 104 106 107 112 112 102 107 107 112 112 104 107 107 112 112 104 107 107 112 112 106 107 108 112 112 107 107 108 112 112 107 107 108 112 112 102 109 109 112 112 102 109 109 112 112 104 109 109 112 112 102 109 110 112 112 103 109 110 112 112 104 109 110 112 112 102 110 110 112 112 104 110 110 112 112 104 110 110 112 112 107 109 111 112 112 107 109 111 112 112 106 110 111 112 112 107 110 111 112 112 107 110 111 112 112 109 110 112 112 112 110 110 112 112 112 107 112 112 112 112 112 112 112 112 112 

当在这些行上n = 3时,这应该产生命中:

 99 99 99 112 112 99 99 99 112 112 102 102 102 112 112 109 110 112 112 112 110 110 112 112 112 

在这条线上n = 4时命中:

 107 112 112 112 112 

在这条线上有n = 5的命中:

 112 112 112 112 112 

这是一个使用awk的Bash脚本解决方案。 它逐行读取文件,并使用AWK关联数组来计算一个数字出现在行上的次数。 将filename.txt更改为包含数字的文件。

 n=3 while read line do echo "$line" | awk -vn="$n" ' { for(i=1; i <= NF; i++) { a[$i]++ } } { for(o in a) { if (a[o] == n) { print } } } ' done < filename.txt 

你也可以用sed来做。 你可以创建一个script

 n=$(($1-1)) sed -n "/\([0-9]*\)\( \1\)\{$n\}/p" filename 

像这样运行,只要提供n作为脚本参数:

 ./script.sh 3 

输出:

 99 99 99 112 112 99 99 99 112 112 102 102 102 112 112 109 110 112 112 112 110 110 112 112 112 

Awk唯一的解决方案作为一个单线程:

 awk -vn=3 '{for(i=1;i<=NF;i++)a[$i]++;for(o in a)if(a[o]==n)p=1} p; {p=0;delete a}' inputfile 

拆分为更容易阅读,这稍微类似badjr的解决方案。 (我已经用他的变量来比较。)

 { for (i=1;i<=NF;i++) # populate an array with counts of unique elements a[$i]++ for (o in a) # check the array for a matching count & set flag if (a[o]==n) p=1 } p; # if we've set our flag, print the current line. { # clear our workspace for the next line. p=0 delete a } 

如果你对bash-only解决方案感兴趣,下面实现相同的awk逻辑,只有没有awk:

 #!/usr/bin/env bash n=5 while read -aa; do unset b for i in "${!a[@]}"; do (( b[${a[$i]}]++ )) done for i in "${b[@]}"; do [ "$i" -eq "$n" ] && echo "${a[@]}" done done < inputfile 

请注意,因为这里的输出是使用数组元素打印的,输入文件中的空格将不会被保留。

这个解决方案只是因为使用数组而使用了bash。

另一个例子,不希望我的工作浪费;)

 #!/bin/bash while (($1 > 0)) do n="${n} \1" set ${1}-1 done sed -nr "\_\<([0-9]+)${n}\>_ p" 

编辑:在BSD sed (OS X)上,您需要分别用迷人的[[:<:]][[:>:]]替换\<\>