使用“\t”来 grep 查找制表符分隔值有什么问题?

使用“\t”来 grep 查找制表符分隔值有什么问题?

我有一个包含四个值的 .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 上默认具有-PPerl 兼容正则表达式,可让您使用\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]+$"

相关内容