如何在awk操作中增加一个shell变量

4
我的shell脚本大概是这样的:

#!/bin/bash

global_var=0

func() {
 awk '$1 ~/^pattern/ {global_var=$((global_var+1))}' $1
}

func input_file_name

我想在awk操作中增加全局(shell)变量global_var的值。该如何操作?普通的shell样式增加似乎不起作用。

awk 命令中,访问变量时不需要使用 $ 符号。 - WKPlus
2
@WKPlus:一般来说是正确的,但这里的问题是尝试从_awk_程序内部访问_shell_变量(使用_shell_语法,但这是一个次要的问题)。 - mklement0
2
问题不仅在于从 awk 脚本内部访问 shell 变量,而且还在于尝试从 awk 脚本内部修改 shell 变量。 - Jonathan Leffler
1个回答

10

试一下这个:

func() {
 awk '$1~/^pattern/ {++awk_var} END {print awk_var+0}' "$1"
}

shell_var=$(func input_file_name)

Shell 和 awk 是独立的世界,你应该将它们视为两个不同的部分(*)(实际上,你已经这样做了,通过将你的 awk 程序放在单引号中,防止 shell 在处理 awk 程序 字符串 时扩展任何Shell变量引用)。

因此,使用 awk[-内部] 变量来进行计数(awk_var,并在完成处理输入文件后(在 END 块中),使用 print 输出 awk 变量到 stdout+0 部分是为了确保在找不到匹配项时默认输出为 0)。

请注意,一般情况下,awk 变量不需要显式初始化,因为它们在数字和布尔上下文中默认为 0,在字符串上下文中默认为 ""(空字符串)。

此外,请注意,awk 有自己的语法$((...)) 用于算术扩展的 shell 构造不适用于 awk。一般情况下,awk 变量仅仅通过名称进行引用(无需前缀的 $),并且可以直接应用算术运算,例如 ++

使用命令替换 - $(...) - 在 shell 中允许你捕获 awk 命令的输出

在你的特定情况下,你不需要将变量值传递给 awk 程序,但如果你需要这样做,你可以使用一个或多个 awk-v 选项; 例如:awk -v awk_var="$shell_var" ...

在 shell(bash)端,如果你想要awk 的输出添加到 shell 变量中而不仅仅是将其赋值给变量

declare -i shell_var                # make sure variable is an integer
shell_var+=$(func input_file_name)  # add function's output to existing value

(*) shell 和 awk 有完全独立的命名空间,它们之间没有直接相互作用的方法:awk 没有 shell 变量的概念,而 shell 没有 awk 变量的概念。

将 shell 变量值整合到 awk 程序中是技术上可行但不明智的 - 使用双引号字符串表示 awk 程序,在其中引用 shell 变量值,这些值在字符串被传递给 awk 之前会被 shell 展开一次。
但是不能从 awk 程序内部修改 shell 变量。

由于很快就会变得复杂,无法判断 awk 程序的哪些部分是由 shell 预先解释的,哪些部分是之后由 awk 解释的(例如,'$' 也有特殊含义),所以最佳方法是:

  • 使用单引号字符串来表示 awk 程序,以保护它免受 shell 解释
  • 如果需要传入值,请使用 -v 选项的实例
  • 如果需要传出信息,请从 awk 中打印到 stdout,并使用命令替换或重定向通过 shell 捕获它。

如果你export global_var,那么你可以从ENVIRON["global_var"]初始化awk_var。不是说这是一个好主意,特别是因为它是单向的,但是awk程序至少可以访问环境变量。也许这只会更加混乱而不是有帮助。 - rici
1
不确定为什么你在使用printf而不是print,但无论如何,它可以只是awk '$1 ~/^pattern/ {++awk_var} END {print awk_var+0}'。对于漂亮、详细的解释加一分。 - Ed Morton
1
@rici:这是一个有趣的替代方案,并且了解这一点很好,但由于它是单向的,因此最好使用“-v…”。 - mklement0
1
@EdMorton 谢谢,也感谢您的 +0 小技巧 - 已添加到答案中。我使用了 printf,因为知道结果会被捕获在一个变量中,但我同意这可能会令人困惑(并且通常没有格式字符串不太安全),所以我已经改用了 print - mklement0
我认为最后一个语句应该是 (( shell_var += $(func input_file_name) ))。否则awk应该有 -v "awk_var=$shell_var" - konsolebox
@konsolebox:感谢您的提示;我根据OP的代码做出了假设,即变量的值是完全在awk中执行的,shell变量只接收结果。我的回答正文确实提到了如何通过-v“awk_var=$shell_var”传递当前值以演示一般的变量传递。关于您的建议,我认为使用+=是更清晰的方法——我已经将其添加到答案中,尽管我选择了declare -i,这样就不必使用((...)) - mklement0

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