如何使用带有 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】:

为什么不使用bashgawk 本身,跳过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

(-当然也可以使用 sedtail 来实现同样的效果,但我认为有某种美感仅取决于 bashgawk 本身;)

【讨论】:

【参考方案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,它甚至没有被正确记录。

关于它的作用有一个粗略的共识:在 !\nexec 它之间获取所有内容。假设!\n 之间的所有内容都是通向解释器的完整绝对路径。如果它包含空格会发生什么,目前还没有达成共识。

    某些操作系统只是将整个事物视为路径。毕竟,在大多数操作系统中,空格或破折号在路径中是合法的。 某些操作系统会在空格处拆分,并将第一部分视为解释器的路径,将其余部分视为单独的参数。 某些操作系统在 first 空白处拆分,并将前面部分视为通向 interpeter 的路径,其余部分视为 single 参数(这就是您所看到的)。 有些甚至根本不支持 shebang 行

谢天谢地,1. 和 4. 似乎已经消失了,但 3. 相当普遍,所以你根本不能指望能够传递多个参数。

由于在 POSIX 或 SUS 中也没有指定命令的位置,您通常通过将可执行文件的 name 传递给 env 来用完该单个参数,以便 it 可以确定可执行文件的位置;例如:

#!/usr/bin/env gawk

[很明显,这个仍然假设env有一个特定的路径,但只有极少数系统存在它在/bin中,所以这通常是安全的。 env 的位置比gawk 的位置更标准化,甚至更糟糕的是pythonrubyspidermonkey。]

这意味着你实际上不能使用任何个参数根本

【讨论】:

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 脚本文件显示窗口?

我应该使用 hashbang/shebang 吗?

为啥这个带有 shebang #!/bin/sh 和 exec python 的片段在 4 个单引号内起作用?

如何使用awk将带有标题的新列添加到csv

如何使用 Shebang Line (Python 虚拟环境)

如何使用 Shebang Line (Python 虚拟环境)