【问题标题】:AWK script shebang to allow dash-prefixed argumentsAWK 脚本 shebang 允许以破折号为前缀的参数
【发布时间】:2019-08-11 09:20:55
【问题描述】:

我想写一个相当复杂的 AWK 脚本,它会接受一堆命令行参数,解析它们然后执行一些工作。

不幸的是,我在尝试将破折号前缀 (-arg) 参数传递给脚本时遇到了麻烦,因为它们正在由 AWK 解释。

$ ./script.awk -arg
awk: not an option: -arg

我注意到-- 选项,但我不确定如何在shebang 中有意义地使用它。我找不到任何方法来获取文件名并在脚本的 shebang 中引用它(类似于#!/usr/bin/awk -f $FILE --)。

然后我想也许-W exec 选项可以用来解决这个问题,但我不断收到以下错误(即使没有尝试使用-- 选项),这似乎表明该文件甚至没有真正附加到 shebang 命令的末尾。

$ ./script.awk
awk: vacuous option: -W  exec
awk: 1: unexpected character '.'

有没有办法制作一个独立的(单个文件,无包装脚本)可执行的 AWK 脚本,它可以接受以破折号为前缀的参数?


为什么我要滥用 AWK 到这种程度?主要是出于好奇,也是为了摆脱包装器外壳脚本,我目前必须使用它来执行 AWK 脚本:

#!/bin/sh
awk -f script.awk -- "$@"

该解决方案应该符合 POSIX(假设 AWK 的路径是 /usr/bin/awk)。即使您有不符合 POSIX 标准的解决方案,也请分享。

【问题讨论】:

  • 如果没有 shell 脚本包装器,可能是不可能的。阅读您的 execve(2) 手册页
  • @glennjackman 这就是我一开始的假设,但后来我想起 shell / -nix 中的所有内容通常都有一个 hacky 解决方案。值得庆幸的是,似乎确实有一种方法可以实现这一点(见下文)。
  • 如果您创建了minimal reproducible example,我们可以向您展示如何最好地实施它。
  • @EdMorton 我不太确定你在这种情况下的意思。我已经给出了一个传递给脚本的参数可能是什么样子的示例,但其余的只是一个通用的 AWK 脚本(其中大部分甚至还没有编写,因为这是我开始的第一个问题之一处理)。我需要拥有所有标准的 AWK 功能。
  • 我看不出如果你的 shell 脚本是 #!/bin/awk shebang 下的 200 行 awk 代码或包裹在 awk '...' 中的 200 行 awk 代码会有什么不同。无论哪种方式,都是相同的 200 行 awk 代码。

标签: shell awk sh posix


【解决方案1】:

了解问题:

据我了解,OP 有一个名为script.awk 的复杂脚本:

#!/usr/bin/awk -f
BEGIN{print "ARGC", ARGC; for(i=0;i<ARGC;++i) print "ARG"i,ARGV[i]}

OP 希望使用各种传统的 POSIX 风格的单字母选项或 GNU 风格的长选项来调用它。 POSIX 选项以单个 字符 (-) 开头,而长选项以两个 字符 (--) 开头。然而,这失败了,因为 awk 正在解释这些参数以传递给 awk 本身而不是脚本参数列表。例如。

$ ./script.awk
ARGC 1
ARG0 awk
$ ./script.awk -arg
awk: not an option: -arg

问题:有没有办法编写一个符合 POSIX 的脚本来处理这种连字符的参数? (建议在原始问题中提出。)

观察 1: 虽然不是很清楚,但必须指出错误消息是由 ma​​wk 生成的,而不是更常见的 GNU 版本 gawk强>。在 mawk 失败的地方,gawk 不会:

$ mawk -f script.awk -arg
mawk: not an option -arg
$ gawk -f script.awk -arg
ARGC 2
ARG0 gawk
ARG1 -arg

然而,必须提到的是,对于 gawk 和 mawk,当参数与 awk 的可选参数发生冲突时,可以观察到不同的行为。示例:

$ mawk -f script.awk -var   # this fails as gawk expects -v ar=foo
mawk: improper assignment: -v ar
$ gawk -f script.awk -var   # this fails as gawk expects -v ar=foo
gawk: `oo' argument to `-v' not in `var=value' form
$ gawk -f script.awk -var=1 # this works and creates variable ar
$ mawk -f script.awk -var=1 # this works and creates variable ar
$ mawk -f script.awk -foo  # this fails as it expects a file oo
mawk: cannot open oo (No such file or directory)
$ gawk -f script.awk -foo  # this fails as it expects a file oo
gawk: fatal: can't open source file `oo' for reading (No such file or directory)

观察 2: OP 建议使用双 来表示连续选项只是 awk 的一部分。然而,这是 mawk 和 gawk 的扩展,而不是 POSIX standard 的一部分。

--:表示选项的明确结束。 来源:man mawk
--:发出期权结束信号。这对于允许 AWK 程序本身的进一步参数以 - 开头很有用。这提供了与大多数其他 POSIX 程序使用的参数解析约定的一致性。 来源:man gawk

此外,双连字符的使用假定-- 之后的所有参数都是文件:

$ ./script.awk -- -arg1 file
ARGC 3
ARG0 mawk
ARG1 -arg1
ARG2 file
mawk: cannot open -arg1 (No such file or directory)

建议 1: 虽然标志的概念是一个不错的选择,但您可以考虑使用标准的 POSIX compliant 赋值作为参数:

$ ./script.awk arg1=1 arg2=1 arg3=1 file

但是,这样做的缺点是这些分配仅在执行BEGIN 块之后才会处理。 (参见POSIX standard

建议 2: 一个简单的改进是使用 ARGVARGC 并使用无连字符的参数。这有点像 BSD(cfr ps aux),可能看起来像:

$ ./script.awk arg1 arg2 arg3
ARGC 4
ARG0 gawk
ARG1 arg1
ARG2 arg2
ARG3 arg3

建议 3: 如果以上选项都不符合您的喜好,您必须考虑使用 shawk 之间的混合。混合这个词意味着我们编写的语法可以被shawk 识别。一个 awk 程序由以下形式的对组成:

pattern { action }

pattern 可以忽略。这与sh 的复合命令语法非常相似:

{ compound-list ; }

这使我们现在可以编写以下 shell 脚本script.sh

#!/bin/sh
{ "awk" "-f" "$0" "--" "${@}" ; "exit" ;}
# your awk script comes here

通过这种方式编写,awk 会将第一个操作解释为字符串的串联。另一方面,sh 名义上会执行它。

遗憾的是,虽然看起来很有希望,但由于双连字符的影响,这确实起作用。

$ ./script.sh file   # this works
ARGC 2
ARG0 awk
ARG1 file

$ ./script.sh -arg file   # this does not work
ARGC 3
ARG0 mawk
ARG1 -arg1
ARG2 file
mawk: cannot open -arg1 (No such file or directory)

一个丑陋的解决方案可能是开始解析脚本本身以删除前两行,然后再将其传递回 awk。但这只会解决只有 BEGIN 块的脚本的问题。

【讨论】:

  • 对我来说,这个解决方案在脚本执行结束时打印 Hangup(可能是 WSL Debian 的 sh 报告 SIGHUP 的怪癖)。在我看来,用普通的旧 exit 替换 kill 命令可以解决这个问题。我错过了什么吗?另外,我相信您已经省略了以破折号为前缀的参数工作所需的关键部分(AWK 调用应该是 awk -f "$0" "--" "$@",除非我弄错了)。
  • @EdMorton 我必须将所有代码放在一个可执行文件中。如果我将代码从 shell 脚本传递给 awk,我将必须拥有两个代码副本才能获得 AWK 语法突出显示(shell 字符串内的 AWK 代码通常不会正确突出显示)。这使得在复制代码时很容易出错或忘记同步任何更改,基本上是违反 DRY。
  • 所以我们要解决的问题是,当awk代码在单引号字符串中时,您使用的编辑器没有正确突出显示awk语法?
  • 您可以使用{ exec awk ... 删除kill 行,这将替换 shell 进程
  • @EdMorton 这与 my 编辑器无关。这是任何编辑器的普遍问题。将 AWK 代码包含在另一种语言的代码中非常不方便、不切实际并且是一种糟糕的做法,这很容易导致大量无法预料的问题。我正在寻找特定问题的解决方案。如果我想将我的 AWK 代码嵌入到 shell 代码中,我可以很容易地做到这一点,但我想执行 AWK 代码而不必担心它会被另一个程序意外操作。
猜你喜欢
  • 1970-01-01
  • 2019-10-31
  • 2017-12-27
  • 1970-01-01
  • 1970-01-01
  • 2010-11-27
  • 1970-01-01
  • 1970-01-01
  • 2019-03-09
相关资源
最近更新 更多