使用AWK使用多个字符作为字段分隔符

4
我遇到了AWK的字段分隔符问题, 输入文件如下所示
1 | all | | 同义词 | 1 | root | | 科学名称 | 2 | Bacteria | Bacteria | 科学名称 | 2 | Monera | Monera | 部分内容 | 2 | Procaryotae | Procaryotae | 部分内容 | 2 | Prokaryota | Prokaryota | 部分内容 | 2 | Prokaryotae | Prokaryotae | 部分内容 | 2 | bacteria | bacteria | 爆炸名字 |
这里的字段分隔符是制表符,管道符号,制表符 \t | \ t 因此,在尝试仅打印第1列和第2列时
awk -F'\t|\t' '{print $1 "\t" $2}' nodes.dmp | less

而不是期望的输出,输出结果是第一列后跟着一个竖杠。我尝试转义这个竖杠\t\|\t,但输出结果仍然相同。

1 |
1 |
2 |
2 |
2 |
2 |

打印第一列和第三列,可以得到最初预期的输出结果。
awk -F'\t|\t' '{print $1 "\t" $3}' nodes.dmp | less

但我很困惑为什么这不像预期的那样工作。

我知道下面的perl一行代码可以工作,但我真正想要的是使用awk。

perl -aln -F"\t\|\t" -e 'print $F[0],"\t",$F[1]' nodes.dmp | less
3个回答

6
管道符号 | 似乎让 awk 感到困惑,认为 \t|\t 意味着字段分隔符可能是 其中一个 \t 或者 \t。请告诉 awk 字面地解释 |
$ awk -F'\t[|]\t' '{print $1 "\t" $2}'
1   all
1   root
2   Bacteria
2   Monera
2   Procaryotae
2   Prokaryota
2   Prokaryotae
2   bacteria

这是否意味着方括号内的任何内容都将被解释为它本身,而不是awk正则表达式中的某个元字符。但是,当我们转义管道符时,为什么它没有起作用?\|并且在类似上面示例的情况下,[]是转义元字符的首选方式吗? - Buthetleon
@WesleyGoi [] 中的任何内容都表示一个字符类,并按原样解释。当然,您也可以通过输入 \| 来转义 |,这也可以起作用。不过,使用 [|] 看起来更加清晰。 - devnull
2
要明确一点 - 使用\t|\t不会让awk感到困惑;awk完全理解它的意思,即FS是\t\t。混淆的是OP对如何编写表示\t PIPE-SYMBOL \t的ERE感到困惑。此外,使用\|也行不通,因为在字符串中指定的正则表达式会被解析两次,一次在读取时,一次在使用时,所以您需要两次转义元字符,即\\| - Ed Morton
感谢你澄清,但好像双重转义没有起作用。 - Buthetleon
1
@WesleyGoi 使用 awk -F'\t\\|\t' '{print $1 "\t" $2}' 对我来说是有效的,但实际上,使用字符类更容易阅读。 - Adrian Frühwirth

1

根据您发布的输入:

  1. 您的行可以以 | 结尾,而不是 |\t,并且
  2. 您有一些情况(前两行)输入包含 |\t|,并且
  3. 您的行以制表符开头

因此,tab-pipe-tab 的 FS 是错误的,因为它不会匹配上述任何一种情况,因为第一种情况只是 tab-pipe,第二种情况中间的制表符将匹配前一个字段的 tab-pipe-tab,但接下来只剩下 pipe-tab 的字段,第一个则留下了不必要的前导制表符。

实际上,您需要设置 FS 为仅为 tab-pipe,然后从每个字段中去掉前导制表符:

awk -F'\t|' -v OFS='\t' '{gsub(/(^|[|])\t/,""); print $1, $2}' file

那样你就可以将所有从1到NF-1的字段同等地处理。

嗨Ed,当我粘贴示例输入时,它似乎切掉了最后一个制表符,要么是我的错。 关于|\t|的出现,它应该是一个空字段,即\t|\t\t|\t,但当我输入时,网站似乎将其更改了。对于3也是如此。 - Buthetleon
我已经根据描述修改了上面的示例,感谢您指出! - Buthetleon

0

使用 cut 命令:

 cut -f1,2 -d'|' file.txt 

输出中没有管道

 cut -f1,2 -d'|' file.txt | tr -d '|'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接