了解在shebang行中的awk -f选项

5
我在阅读某人的awk脚本,头部以#!/usr/bin/env awk -f开头。env命令没有-f选项,因此他们必须将-f选项传递给awk命令。我查看了awk的man页面。它说Awk会扫描每个输入文件,寻找与prog或-f progfile中指定的任何一组模式匹配的行。对于每个模式,当文件的一行与该模式匹配时,都可以执行一个相关联的操作。 据我理解,这意味着awk通过搜索在progfile/prog中指定的模式来处理输入文件,并根据使用的模式对找到的输入文件中的行执行相关联的操作。我的问题是...在运行awk脚本文件时,它如何工作?我们没有在#!/usr/bin/env awk -f命令行中指定progfile。awk脚本将使用哪些模式?或者这是否意味着我们必须在运行awk脚本时传递progfile?如果是这种情况,那么在脚本中指定-f选项是否是多余的?如果我们不指定progfile,-f选项会被默认忽略还是会引发错误?
为了更好地理解这一点,我编写了一个简单的awk脚本,并将其保存为test.awk。
#!/usr/bin/env awk -f

BEGIN { print "START" }

当我运行这个程序时,屏幕上会打印出字符串"START"。
prachis-mbp-2:~ pskhadke$ ./test.awk
START

如果我从awk脚本的第一行中删除-f选项并运行它,我将会得到以下错误:
prachis-mbp-2:~ pskhadke$./test.awk
awk: syntax error at source line 1
 context is
     >>> . <<< /test.awk

同样地,
prachis-mbp-2:~ pskhadke$ awk test.awk
awk: syntax error at source line 1
 context is
     >>> test. <<< awk
awk: bailing out at source line 1

因为某些原因,如果没有使用-f选项,它无法正确解析参数。但是为什么呢?

4个回答

5
命令的shebang行末尾附加文件名。因此,对于带有头部“#!/usr/bin/env awk -f”的文件test.awk,执行的有效命令行将是awk -f test.awk,将test.awk视为要执行的脚本文件,而不是数据输入文件。
最好的示例:创建一个文件test,唯一内容为#!/bin/rm,使其可执行(例如chmod 755),并尝试通过运行./test来执行它。现在,那个文件去哪了 :)


尽管其他答案都是正确的,但我接受了你的答案,因为你简要解释了我们需要-f的主要原因。我不知道脚本名称会附加在awk -f的末尾。我点赞了所有正确的答案。 - Prachi
1
不,没有人指出awk永远不应该通过shebang调用。你能解释一下为什么吗? - Prachi
这个回答与这个有什么关系?对我来说,#!/usr/bin/env awk -f会导致错误。 - Enlico
1
@JamesBrown 因为它与从shell脚本中调用awk相比没有任何有用的优势,而且它有一个显著的缺点,即您无法将shell脚本参数分离为shell参数、awk参数和awk变量设置。例如,请参见https://unix.stackexchange.com/a/563456/133219。 - Ed Morton
1
@Prachi 抱歉我没有看到你4年前的评论,但是现在我回复了,供参考 :-). - Ed Morton
显示剩余2条评论

3
所以,他们必须为awk命令传递-f选项。

是的,没错。Shebang行在调用时由内核解释。如果它读取#!/usr/bin/env awk -f,则意味着当这个文件被作为可执行文件调用时(即当它被传递作为七个exec函数之一的程序参数时),"执行"它的正确方式是通过执行awk -f 。换句话说:exec函数将使用正确的参数调用解释器,而不是尝试直接执行文件(因为它不是二进制文件)。

-f选项是必需的,因为awk(1)默认从参数中读取程序;如果你想从文件中读取它,你需要-f。

根据我的理解,这意味着awk通过在progfile/prog中指定的模式中搜索输入文件,具体取决于是否使用了awk的-f选项。

awk(1)总是处理输入文件以查找匹配项。-f选项仅控制awk程序从何处读取。如果启用,则表示第一个文件名实际上是包含awk程序的文件名。否则,第一个文件名是开始查找模式的第一个文件。如果没有指定文件,则只是与stdin中的行匹配。

我们没有在#!/usr/bin/env awk -f行中指定progfile

内核会为你做到这一点。再次说明,shebang行的意思是:当你想要执行这个文件(称之为X)时,请使用awk -f来执行它。所以它等价于awk -f X。

如果我从awk脚本的第一行中删除-f选项并运行它,我会得到以下错误:

因为那样就相当于:

$ awk ./test.awk

没有使用-f选项是无意义的,因为如果没有它,awk会尝试将./test.awk解释为awk程序,导致错误。

请注意,对于编辑我的答案以及同意编辑的审核者们:是内核读取和解释 shebang 行。另外,请不要取消格式化我的帖子,也不要用 ' 替换反引号。 - Filipe Gonçalves
对不起,我当时有点困,搞砸了,然后完全忘记回来撤销它。 - mrh

2
#!/usr/bin/env awk -f
#!后面的字符串会在脚本名称后附加后作为命令调用。
如果阅读env命令的文档,您会发现(在没有任何NAME=VALUE或其他选项的情况下),它将其第一个参数作为命令调用,并将任何后续参数传递给该命令。因此,env将调用awk -f name-of-script
您需要使用-f的原因很简单,那就是这是awk处理其命令行参数的方式。如果在awk的命令行上传递一个字符串而不指定选项名称,则它将将该字符串评估为awk代码:
$ awk 'BEGIN {print "hello, world"}'
hello, world

要让 awk 执行文件的内容,你需要使用 -f 选项:

$ echo 'BEGIN { print "hello, world"}' > hello.awk
$ awk -f hello.awk
hello, world

与大多数其他解释器相比,这实际上有点不寻常。例如,perl命令默认情况下将命令行参数视为脚本名称;要在命令行上传递Perl代码,您必须使用-e选项:

$ perl -e 'print "hello, world\n"'
hello, world

大多数Shell都是相同的。

请注意,一些旧系统限制了您在#!行上可以拥有的参数数量,因此#!/usr/bin/env awk -f可能无法工作。

如果您知道awk解释器命令的确切位置,则可以直接使用它,而不是使用/usr/bin/env

#!/usr/bin/awk -f

请查看此问题我的回答,讨论#!/usr/bin/env的技巧。

1

Shebang行将由内核解释,内核将使用shebang指定的解释器以可执行文件名(您的脚本)作为参数调用解释器。请参见man 2 execve,第"Interpreter scripts"节。


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