我有一个带有制表符分隔列的文本文件,我想使用 awk 对其进行处理。
下面是此类文件的示例:
size=1\tname=foo\tweight=1.2
weight=2.5\tname=bar\tsize=2
我想要实现的是将内容类似于小数点后四位的列中的数值标准化$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
}'
答案1
那应该是:
if ($i ~ field_name "=[0-9]*.?[0-9]+") ...
或者:
regexp = field_name "=[0-9]*.?[0-9]+"
if ($i ~ regexp) ...
请注意,.
匹配任何单个字符。如果您想匹配文字.
,则需要regexp
包含\.
(必须在双引号内写入\\.
)或[.]
。
regexp = field_name "=[0-9]*\\.?[0-9]+"
我还希望您想要锚定正则表达式:
regexp = "^" field_name "=[0-9]*\\.?[0-9]+$"
其他注意事项:
cat "${file}"
$file
是一个 UUOC,它也有一个缺点(通过重定向),即它在启动时不起作用,并且如果无法打开文件-
仍然运行。awk
-v field_name="$external_data"
破坏反斜杠。另一种没有问题的方法是使用环境变量:并在asFIELD="$external_data" awk ...
中引用它。awk
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'
这不会出现上面讨论的任何问题(它也比包含无效文本的输入更好awk
,例如文本和二进制数据的混合,或者以与用户区域设置不同的字符集编码的文本)。