如何使用给定值的字段数过滤制表符分隔的输入?

时间:2016-05-26 03:06:42

标签: bash csv awk sed grep

我的数据(标签分隔):

1   0   0   1   0   1   1   0   1
1   1   0   1   0   1   0   1   1
1   1   1   1   1   1   1   1   1
0   0   0   0   0   0   0   0   0
...

我怎么能用精确的线条,例如5'1, 理想输出:

1   0   0   1   0   1   1   0   1

另外,我怎么能用等于或大于(> =)5'1的行来画线, 理想输出:

1   0   0   1   0   1   1   0   1
1   1   0   1   0   1   0   1   1
1   1   1   1   1   1   1   1   1

我试过了,

grep 1$'\t'1$'\t'1$'\t'1$'\t'1

然而,这只会输出连续的'1',这不是我想要的全部。

我想知道是否会有任何简单的方法来实现这一点,谢谢!

5 个答案:

答案 0 :(得分:4)

John Bollinger's helpful answeranishane's answer表明可以grep完成,但是,正如已经指出的那样,这非常繁琐,因为正则表达式不是为计算而设计的。

相比之下,

awk是为基于字段的解析和计算(通常组合与正则表达式来识别而构建的字段分隔符,或者如下所示,字段本身。)

假设您有 GNU awk ,您可以使用以下内容:

正好5 1 s:

awk -v FPAT='\\<1\\>' 'NF==5' file

5个或更多1 s:

awk -v FPAT='\\<1\\>' 'NF>=5' file
  • 特殊变量FPAT GNU awk扩展程序,允许您通过描述字段本身的正则表达式来标识字段 ,与使用正则表达式在字段之间定义分隔符的标准方法(通过特殊变量FS或选项-F)形成对比:

    • '\\<1\\>'根据字边界断言1\<将任何“孤立的”\>(由非字字符包围)标识为字段; \必须加倍,以便awk执行的初始字符串解析不会“吃掉”单个\
  • 标准变量NF包含当前行中输入字段的 count ,可以轻松进行数值比较。如果条件计算结果为true,则隐式打印手头的输入行(换句话说:NF==5隐含地与NF==5 { print }相同,更详细地说,NF==5 { print $0 })。

符合POSIX标准的awk解决方案稍微复杂一点:

正好5 1 s:

awk '{ l=$0; gsub("[\t0]", "") }; length($0)==5 { print l }' file

5个或更多1 s:

awk '{ l=$0; gsub("[\t0]", "") }; length($0)>=5 { print l }' file
  • l=$0将输入行($0)以原始格式保存在变量l中。

  • gsub("[\t0]", "")取代所有\t0个字符。在带有空字符串的输入行中,即有效地删除它们,并且只留下(直接连接)1个实例(如果有的话)。

  • length($0)==5 { print l }只有在l s的结果字符串(即现在存储的1的数量)时才打印原始输入行(1)在修改输入行($0)中匹配指定的计数。

答案 1 :(得分:2)

您可以使用grep。但那将是对正则表达式的滥用。

$ cat countme
1   0   0   1   0   1   1   0   1
1   1   0   1   0   1   0   1   1
1   1   1   1   1   1   1   1   1
0   0   0   0   0   0   0   0   0

$ grep -P '^[0\t]*(1[0\t]*){5}[0\t]*$' countme # Match exactly 5
1   0   0   1   0   1   1   0   1

$ grep -P '^[0\t]*(1[0\t]*){5,}[0\t]*$' countme # Match >=5
1   0   0   1   0   1   1   0   1
1   1   0   1   0   1   0   1   1
1   1   1   1   1   1   1   1   1

答案 2 :(得分:2)

你可以这样做以获得恰好五个'1'的行:

grep '^[^1]*\(1[^1]*\)\{5,5\}[^1]*$'

您可以将其简化为至少五个'1:

grep '\(1[^1]*\)\{5,\}'

枚举量词(\{n,m\})使您可以方便地指定子模式的连续匹配数的特定数量或范围。但是,为了避免匹配具有额外匹配的行到这种模式,您还必须将其锚定到行的开头和结尾。

另一个诀窍是确保第一个1之前,1之间和最后一个1之间的差距匹配。在您的情况下,所有这些差距都可以非常简单地表示为除1以外的零个或多个字符的范围:[^1]*。将这些部分放在一起就可以得到上述正则表达式。

答案 3 :(得分:1)

sed -nE '/^([^1]*1[^1]*){5}$/p' your_file

正好5场比赛和

sed -nE '/^([^1]*1[^1]*){5,}$/p' your_file

5场或更多场比赛。

注意:在GNU sed中,您可能无法在联机帮助页中看到-E选项,但它受支持。使用-E可以移植到Mac OSX。

答案 4 :(得分:1)

perl

$ perl -ane 'print if (grep {$_==1} @F) == 5' ip.txt 
1   0   0   1   0   1   1   0   1

$ perl -ane 'print if (grep {$_==1} @F) >= 5' ip.txt 
1   0   0   1   0   1   1   0   1
1   1   0   1   0   1   0   1   1
1   1   1   1   1   1   1   1   1
  • -a自动拆分空格上的输入行并保存到@F数组
  • grep {$_==1} @F返回包含@F数组中元素的数组,这些元素完全等于1
  • 标量上下文中的
  • (grep {$_==1} @F) == 5,将根据数组
  • 的元素数进行比较
  • 有关-ane选项
  • 的详细信息,请参阅http://perldoc.perl.org/perlrun.html#Command-Switches