【问题标题】:Pipe symbol | in AWK field delimiter管道符号 |在 AWK 字段分隔符中
【发布时间】:2017-04-27 00:11:30
【问题描述】:

我有一个文件foo,其中包含以下数据:

A<|>B<|>C<|>D
1<|>2<|>3<|>4

我想使用 awk 正确访问每一列,但它没有正确解释字段分隔符。

当我跑步时:

head foo | \
  awk 'BEGIN {FS="<|>"} {out=""; for(i=1;i<=NF;i++){out=out" "$i}; print out}'

而不是打印

A B C D
1 2 3 4

打印出来

A | B | C | D 
1 | 2 | 3 | 4

这背后的原因是什么?

【问题讨论】:

  • 只是一个提示:你可以在awk 中使用NR&lt;11 而不是head foo

标签: bash awk


【解决方案1】:

管道是正则表达式中的特殊字符,因此您需要使用反斜杠对其进行转义。但是这个反斜杠也是字符串文字的特殊字符,所以需要再次转义。所以你最终得到以下结果:

awk -F '<\\|>' '{$1=$1}1'

awk 'BEGIN {FS="<\\|>"} {$1=$1}1' 

这里很好地解释了这种语法的原因:http://www.gnu.org/software/gawk/manual/gawk.html#Computed-Regexps。简而言之,表达式被解析了两次。

【讨论】:

  • @EdMorton 您指的是原始修订版,还是 hek2mgl 的编辑?老实说,我只是在三个人要求我之后才发布答案(参见另一个答案的 cmets),但我无法删除它,因为它已被接受。
  • 进行了编辑以澄清这一点。在我看来,这个问题很有趣。
【解决方案2】:

Awk 将您的分隔符读取为正则表达式“&lt;&gt;”。您必须转义管道字符(两次,看到诸如字段分隔符之类的动态正则表达式是scanned twice):"&lt;\\|&gt;"

您也可以将字段分隔符指定为参数:

awk -F '<\\|>' '{out=""; for(i=1;i<=NF;i++){out=out" "$i}; print out}' <<< 'A<|>B<|>C<|>D'
 A B C D

根据您的 awk 版本,您可能只需要一次转义即可。对我来说,mawk 1.3.3 可以同时使用 -F '&lt;\|&gt;'-F '&lt;\\|&gt;',而 gawk 4.0.1 需要 -F '&lt;\\|&gt;'。我不完全确定 POSIX awk 的方向,但在 --posix 模式下运行 gawk 也需要双重转义。

【讨论】:

  • 可以这样写:awk 'BEGIN {FS="&lt;\\|&gt;"} {$1=$1}1' 甚至awk -F '&lt;\|&gt;' '{$1=$1}1'
  • @user000001 为什么不将后者放入答案中?也想回答这个问题:)但应该是awk -F'&lt;\\|&gt;' '$1=$1'
  • 我最初尝试过这个。取消引用 |不能正常工作。它说以下 awk:警告:转义序列\|' treated as plain |'我不太明白
  • @user000001 公平点。但是你仍然需要-F'&lt;\\|&gt;' 而不是-F'&lt;\|&gt;'
  • @Andrew 原因是反斜杠 `\` 是字符串文字和正则表达式的特殊字符。所以你需要双重转义它。
【解决方案3】:

顺便说一句,这里也可以使用sed

sed 's/<|>/ /g' file

如果你想“包含”head 命令:

sed -n '1,10s/<|>/ /gp' file

【讨论】:

    猜你喜欢
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    • 1970-01-01
    • 2020-12-02
    • 2014-10-14
    • 1970-01-01
    相关资源
    最近更新 更多