在 awk 中的模式内使用 shell 变量

Ali*_*Ali 1 awk

我有一个带有制表符分隔列的文本文件,我想使用 awk 处理它。

这是此类文件的示例:

size=1\tname=foo\tweight=1.2
weight=2.5\tname=bar\tsize=2
Run Code Online (Sandbox Code Playgroud)

我想要实现的是将内容类似于$field_name=<number>小数点后四位的列中的数值标准化,其余部分保持原样。这$field_name是一个传递给 awk 的 shell 变量,我想在正则表达式中使用它的值。

这是一个片段(当然不起作用)。我对修复以下 awk 脚本中的第 5 行特别感兴趣,而不是使用其他工具(例如 sed、perl、python 等)的解决方案。

$ cat "${file}" \                                       # 1
    | awk -F "\t" -v field_name="${external_var}" '     # 2
      {                                                 # 3
        for (i = 1; i <= NF; i++) {                     # 4
          if ($i ~ /$field_name=[0-9]*.?[0-9]+/) {      # 5
            split($i, kv, "=")                          # 6
            $i = sprintf("%s=%.4f", kv[1], kv[2])       # 7
          }                                             # 8
        }                                               # 9
        print $0                                        # 10
      }'
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 5

那应该是:

if ($i ~ field_name "=[0-9]*.?[0-9]+") ...
Run Code Online (Sandbox Code Playgroud)

或者:

 regexp = field_name "=[0-9]*.?[0-9]+"
 if ($i ~ regexp) ...
Run Code Online (Sandbox Code Playgroud)

请注意,.匹配任何单个字符。如果要匹配文字.,则需要regexp包含\.(必须在双引号内写入\\.)或[.].

 regexp = field_name "=[0-9]*\\.?[0-9]+"
Run Code Online (Sandbox Code Playgroud)

我还希望你想要锚定正则表达式:

 regexp = "^" field_name "=[0-9]*\\.?[0-9]+$"
Run Code Online (Sandbox Code Playgroud)

其他注意事项:

  • cat "${file}"是一个 UUOC,它也有一个缺点(通过重定向),它在$file开始时不起作用,如果无法打开文件-仍然运行awk
  • -v field_name="$external_data"mangles 反斜杠。另一种没有问题的方法是使用环境变量:FIELD="$external_data" awk ...并在awkas 中引用它ENVIRON["FIELD"]
  • 由于 的内容field_name被逐字复制到 中regexp,因此被视为正则表达式,因此如果$external_data包含正则表达式运算符 ( .+*?{}()[]\^%...),它可能无法正常工作。
  • 在某些语言环境和awk实现中,[0-9]匹配的字符不仅仅是0123456789(尽管我怀疑在您的输入中不太可能出现(非 ASCII)字符)。

perl

FIELD=size <"$file" perl -lpe '
  s{
    (?<![^\t])       # not-preceded by a non-TAB
    \Q$ENV{FIELD}=\E # contents of $FIELD taken literally
    \K               # matched portion starts here
    \d*\.?\d+
    (?![^\t])        # not followed by a non-TAB
  }{
    sprintf "%.4f", $&
  }gxe'
Run Code Online (Sandbox Code Playgroud)

这不会有上面讨论的任何问题(它也比awk包含无效文本的输入更有效,例如文本和二进制数据的混合,或者以与用户语言环境不同的字符集编码的文本)。