如何使用带有 shebang 的 awk 的多个参数(即#!)?
Posted
技术标签:
【中文标题】如何使用带有 shebang 的 awk 的多个参数(即#!)?【英文标题】:How to use multiple arguments for awk with a shebang (i.e. #!)? 【发布时间】:2011-05-17 05:32:41 【问题描述】:我想使用 shebang 执行带有 --re-interval
的 gawk 脚本。
#!/usr/bin/gawk --re-interval -f
... awk script goes here
不起作用,因为 gawk 是使用第一个参数 "--re-interval -f"
调用的(没有在空格周围分割),它不理解。有解决办法吗?
当然,您可以不直接调用 gawk,而是将其包装到拆分第一个参数的 shell 脚本中,或者制作一个 shell 脚本,然后调用 gawk 并将脚本放入另一个文件中,但我想知道是否有一些在一个文件中执行此操作的方法。
shebang 行的行为因系统而异——至少在Cygwin 中,它不会用空格分割参数。我只关心如何在这样的系统上执行此操作;该脚本不是可移植的。
【问题讨论】:
我刚刚做的一个愚蠢的实验是在 shebang 行上使用一个脚本使用另一个脚本,它确实正确地拆分了参数。 @Hasturkun,这引发了另一个问题,即 shebang 行的行为也因系统而异,无论调用的程序本身是否可以是脚本。 ***.com/questions/17458528/why-does-this-snippet-work 对于最新版本的 gawk (>= 4.0),不再需要--re-interval
(参见 [gnu.org/software/gawk/manual/…)。
【参考方案1】:
虽然不完全可移植,但从 coreutils 8.30 和 according to its documentation 开始,您将能够使用:
#!/usr/bin/env -S command arg1 arg2 ...
所以给定:
$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too
你会得到:
% ./test.sh
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'
如果你好奇showargs
是:
#!/usr/bin/env sh
echo "\$0 is '$0'"
i=1
for arg in "$@"; do
echo "\$$i is '$arg'"
i=$((i+1))
done
原答案here。
【讨论】:
仅供参考,FreeBSD 已经使用 -S 多年了(从 6.0 开始)。这是对 coreutils 的一个受欢迎的可移植性补充。【参考方案2】:#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''
上面的shell shebang技巧比/usr/bin/env
更便携。
【讨论】:
''':' 是一个保留,因为我最初的解决方案是针对 python 脚本,所以 ''':' 告诉 python 解释器忽略 exec 部分。 我认为你被否决了,因为你的解决方案是针对python
,但这个问题是关于awk
。
python 的好方法。
单引号的用途是什么? ''':' and '''
@Walid – 在 python 中(但不是 awk 和 posix shell!),三重撇号可用于字符串,而不必担心其中的引号或撇号(除非有 3 个以上的连续撇号)。【参考方案3】:
为什么不使用bash
和gawk
本身,跳过shebang,阅读脚本,并将其作为文件传递给gawk [--with-whatever-number-of-params-you-need]
的第二个实例?
#!/bin/bash
gawk --re-interval -f <(gawk 'NR>3' $0 )
exit
print "Program body goes here"
print $1
(-当然也可以使用 sed
或 tail
来实现同样的效果,但我认为有某种美感仅取决于 bash
和 gawk
本身;)
【讨论】:
【参考方案4】:在 Cygwin 和 Linux 下,shebang 路径之后的所有内容都作为一个参数解析到程序中。
可以通过在 shebang 中使用另一个 awk
脚本来解决这个问题:
#!/usr/bin/gawk system("/usr/bin/gawk --re-interval -f " FILENAME); exit
这将在 awk 中执行 system("/usr/bin/gawk --re-interval -f " FILENAME); exit
。
这将在您的系统 shell 中执行 /usr/bin/gawk --re-interval -f path/to/your/script.awk
。
【讨论】:
如果你将参数传递给脚本,这将不起作用【参考方案5】:这似乎适用于 (g)awk。
#!/bin/sh
arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"
# The real awk program starts here
print $0
注意#!
运行/bin/sh
,所以这个脚本首先被解释为一个shell 脚本。
起初,我只是简单地尝试了"exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"
,但 awk 将其视为命令并无条件地打印出每一行输入。这就是为什么我输入arbitrary_long_name==0
- 它应该一直失败。你可以用一些乱码替换它。基本上,我在 awk 中寻找一个不会对 shell 脚本产生不利影响的错误条件。
在 shell 脚本中,arbitrary_long_name==0
定义了一个名为 arbitrary_long_name
的变量并将其设置为等于 =0
。
【讨论】:
这是我的答案,但我想知道它是否足够便携和健壮。它是否特别依赖于bash
,或者它是否适用于任何POSIX sh
?而且我不经常使用awk
,所以我不确定我在第二行的技巧是强制awk
忽略该行的好方法。
正是我想知道的,+1,但可能是不可取的(因此相对投票)。
你能解释一下这可能有什么问题吗,@AaronHall?只要变量arbitrary_long_name
不与真正的awk 程序中使用的变量发生冲突,我就看不到任何问题。有什么我想念的吗?
使用#!/bin/sh -
而不是#!/bin/sh
来保护脚本,如果使用以-
作为第一个字符的第零个参数调用,可能会以危险的方式出现错误行为。这可能会在 C 等编程语言中意外发生,因为忘记将调用的程序名称作为参数数组的一部分传递给 execve
和类似函数,很容易意外搞砸,如果人们习惯性地忘记防范它,它也可能最终成为可恶意利用漏洞的最后一步,让攻击者获得交互式 shell。【参考方案6】:
shebang 线从未被指定为 POSIX、SUS、LSB 或任何其他规范的一部分。 AFAIK,它甚至没有被正确记录。
关于它的作用有一个粗略的共识:在 !
和 \n
和 exec
它之间获取所有内容。假设!
和\n
之间的所有内容都是通向解释器的完整绝对路径。如果它包含空格会发生什么,目前还没有达成共识。
-
某些操作系统只是将整个事物视为路径。毕竟,在大多数操作系统中,空格或破折号在路径中是合法的。
某些操作系统会在空格处拆分,并将第一部分视为解释器的路径,将其余部分视为单独的参数。
某些操作系统在 first 空白处拆分,并将前面部分视为通向 interpeter 的路径,其余部分视为 single 参数(这就是您所看到的)。
有些甚至根本不支持 shebang 行。
谢天谢地,1. 和 4. 似乎已经消失了,但 3. 相当普遍,所以你根本不能指望能够传递多个参数。
由于在 POSIX 或 SUS 中也没有指定命令的位置,您通常通过将可执行文件的 name 传递给 env
来用完该单个参数,以便 it 可以确定可执行文件的位置;例如:
#!/usr/bin/env gawk
[很明显,这个仍然假设env
有一个特定的路径,但只有极少数系统存在它在/bin
中,所以这通常是安全的。 env
的位置比gawk
的位置更标准化,甚至更糟糕的是python
或ruby
或spidermonkey
。]
这意味着你实际上不能使用任何个参数根本。
【讨论】:
FreeBSD 的 env 有一个-S
开关,这在这里有帮助,但它在我的 Linux env
上不存在,我怀疑在 gygwin 上也不可用。 @hstoerr,其他情况不同的用户稍后可能会阅读您的问题,因此即使您现在不需要可移植性,通常便携式答案也是可取的。
所以我们不能在 shebang 中便携地使用参数。但是,如果我们需要任何必要的论证怎么办?我猜解决方案是编写一个包含#!/bin/sh
和/usr/bin/env gawk --re-interval -f my-script.awk
的包装shell 脚本。对吗?
听起来像-S
选项在添加到env
的GNU coreutils 版本中会很有用??和/或修改为类似于选项 2 的 linux 内核 shebang 行为...尽管我认为后一种更改会存在兼容性问题。
@Kaz:是的,但是由于许多二进制文件的路径都没有标准化,所以你用了#!/usr/bin/env ruby
之类的参数。
@Pacerier:更改 POSIX 规范并等待 20-30 年,直到所有系统都更新为符合规范。【参考方案7】:
只是为了好玩:有以下非常奇怪的解决方案,通过文件描述符 3 和 4 重新路由标准输入和程序。您还可以为脚本创建一个临时文件。
#!/bin/bash
exec 3>&0
exec <<-EOF 4>&0
BEGIN print "HALLO"
print \$1
EOF
gawk --re-interval -f <(cat 0>&4) 0>&3
有一点很烦人:shell 在脚本上进行变量扩展,所以你必须引用每个 $(就像在脚本的第二行中所做的那样),而且可能不止于此。
【讨论】:
【参考方案8】:对于可移植的解决方案,请使用awk
而不是gawk
,使用您的shebang 调用标准BOURNE shell (/bin/sh
),然后直接调用awk
,在命令行上将程序作为here 文档传递而不是通过标准输入:
#!/bin/sh
gawk --re-interval <<<EOF
PROGRAM HERE
EOF
注意:-f
没有 awk
的参数。这使得stdin
可供awk
读取输入。假设您已安装 gawk
并在您的 PATH
上,这实现了我认为您尝试使用原始示例执行的所有操作(假设您希望文件内容是 awk 脚本而不是输入,我认为您的 shebang方法会将其视为)。
【讨论】:
这对我不起作用。 bash 人说 这是一种贬义。如果它是#!/bin/bash
或者 /bin/sh
实际上是 bash,这将起作用。如果它是破折号或一些类似的简单 posix shell,它将无法工作。【参考方案9】:
我遇到了同样的问题,但没有明显的解决方案,因为 shebang 中处理空格的方式(至少在 Linux 上)。
但是,您可以在 shebang 中传递多个选项,只要它们是 短选项 并且它们可以连接(GNU 方式)。
例如,你不能拥有
#!/usr/bin/foo -i -f
但你可以拥有
#!/usr/bin/foo -if
显然,这仅在选项具有短等效项且不带参数时才有效。
【讨论】:
【参考方案10】:在 gawk 手册 (http://www.gnu.org/manual/gawk/gawk.html) 中,第 1.14 节的末尾请注意,当从 shebang 行运行 gawk 时,您应该只使用单个参数。它表示操作系统会将 gawk 路径之后的所有内容视为单个参数。也许还有另一种方法来指定--re-interval
选项?也许您的脚本可以在 shebang 行中引用您的 shell,将 gawk
作为命令运行,并将您的脚本文本作为“此处文档”包含在内。
【讨论】:
似乎没有其他方法可以指定该选项。你是对的: gawk -f - here 文档占用了gawk
的标准输入流,但您仍然可以通过 stderr 管道输入某些内容(即,在管道输入此脚本之前将 stdout 重定向到 stderr)。我从来没有真正尝试过,但只要第一个进程没有在 stderr 上发出任何东西,它就可以工作。如果您想确保没有其他人在使用它,您还可以创建一个命名管道 (linuxjournal.com/content/using-named-pipes-fifos-bash)。以上是关于如何使用带有 shebang 的 awk 的多个参数(即#!)?的主要内容,如果未能解决你的问题,请参考以下文章
如何从 OSX Swift 命令行工具或 shebang 脚本文件显示窗口?
为啥这个带有 shebang #!/bin/sh 和 exec python 的片段在 4 个单引号内起作用?