我有一个包含四个值的 .tsv 文件(由制表符分隔的值)。因此,每一行应该只有三个选项卡,并且每个选项卡周围都有一些文本,如下所示:
value value2 value3 value4
但看起来有些行被破坏了(有超过三个选项卡)。我需要找出这些线。
我想出了以下 grep 模式。
grep -v "^[^\t]+\t[^\t]+\t[^\t]+\t[^\t]+$"
我的想法:
- 第一个 ^ 匹配开头
- [^\t]+ 匹配多个“无制表符”
- \t 匹配单个制表符
- $ 匹配结束
然后我只是将其按正确的顺序排列正确的次数。这应该匹配正确的行。所以我通过 -v 选项恢复它以获得错误的行。
但是使用 -v 选项,它会匹配文件中的任何行以及我尝试过的一些随机文本,其中没有任何选项卡。
请问我的错误是什么?
编辑:我正在使用 debian 和 bash。
答案1
正如您已经看到的,\t
对于基本正则表达式来说并不特殊,并且grep
默认使用 BRE。 GNU grep
,Linux 上默认具有-P
Perl 兼容正则表达式,可让您使用\t
制表符。
然而,你想要的事情用 来实现要容易得多awk
。只需将输入字段分隔符设置为制表符 ( -F '\t'
),然后打印字段数 ( NF
) 不为 3 的任何行:
awk -F'\t' 'NF!=3' file
这将打印包含file
多于或少于三个字段的所有行。要限制为仅超过三个字段,请使用:
awk -F'\t' 'NF>3' file
答案2
grep -v "^[^\t]+\t[^\t]+\t[^\t]+\t[^\t]+$"
在这里,grep 将使用基本正则表达式 (BRE),因为它没有提供该-E
选项。与扩展正则表达式 (ERE) 不同,+
BRE 中的 并不特殊并且与自身匹配。此外,在标准正则表达式中,反斜杠在括号组中并不特殊,因此[\t]
匹配反斜杠或字母t
,并[^\t]
匹配除这些之外的任何内容。
在括号组之外,\t
标准未指定匹配的内容,并且实际上随实现的不同而变化。例如,对于 GNU grep
,它匹配t
,而对于 ast-open grep
,它匹配 TAB 字符。
如果您想匹配标准正则表达式中的制表符,则需要将文字制表符传递给grep
,例如使用$'...'
许多 shell 支持的引用形式。 (尽管这还不是标准的;您必须printf
在标准 shell 中使用才能获取制表符。)
因此grep $'a\tb'
将查找并由a
制表b
符分隔,and grep $'a\t\t*b'
or grep $'a\t\\{1,\\}b'
orgrep -E $'a\t+b'
将查找a
并由b
至少一个制表符分隔。
答案3
好的,所以我找出问题所在。我无法使用\t像这样在 grep 中。它只匹配普通字母t。
如何匹配制表符的选项可见于这个问题关于SO。
我通过在命令中添加 -P 选项解决了我的情况,所以这有效:
grep -Pv "^[^\t]+\t[^\t]+\t[^\t]+\t[^\t]+$"
指出了另一个选项@Philippos在注释中(仅匹配至少有四个选项卡的行)。但它还需要 -P 选项:
grep -P '\t.*\t.*\t.*\t'
答案4
正如其他人已经指出的那样,\t
正则表达式并不代表 TAB。因此,显而易见的解决方案是添加一个文字 TAB 字符,这可能会让 BASH 有点棘手。但是,您可以使用^V
( Control+ )输入文本制表符vTAB。
也许设置TAB='
Control+更方便v TAB'
。另一件事是+
在非扩展正则表达式 (BRE) 中按字面意思处理(请参阅“基本正则表达式与扩展正则表达式” 中man grep
),所以使用:
grep -v "^[^$TAB]\+$TAB[^$TAB]\+$TAB[^$TAB]\+$TAB[^$TAB]\+$"
(这里你也可以将变量缩短为T
,并且没有必要使用${TAB}
( 或${T}
) ,但要做好意外的准备)
另外,当您喜欢使用时,egrep
可以使用重复组,如下所示:
egrep -v "^([^$TAB]+$TAB){3}[^$TAB]+$"