如何使用多个参数与一个 shebang (即 # !) awk?

我想使用一个 shebang 来执行 发呆脚本和 --re-interval脚本

#!/usr/bin/gawk --re-interval -f
... awk script goes here

不起作用,因为 gawk 是使用第一个参数 "--re-interval -f"调用的(没有在空格周围拆分) ,它不理解这个参数。有解决办法吗?

当然,您可以不直接调用 gawk,而是将其封装到一个分割第一个参数的 shell 脚本中,或者创建一个 shell 脚本,然后调用 gawk 并将该脚本放到另一个文件中,但我想知道是否有办法在一个文件中实现这一点。

Shebang 行的行为因系统而异——至少在 Cygwin中它没有用空格分割参数。我只关心如何在这样的系统上执行; 脚本并不是可移植的。

44344 次浏览

在 gawk 手册( http://www.gnu.org/manual/gawk/gawk.html )中,第1.14节的末尾指出,当从 shebang 行运行 gawk 时,应该只使用一个参数。它说操作系统将把路径之后的所有东西都视为一个参数。也许还有另一种方法来指定 --re-interval选项?也许您的脚本可以在 shebang 行中引用您的 shell,以命令的形式运行 gawk,并将脚本的文本包含为“ here document”。

Shebang 系列从未被指定为 POSIX、 SUS、 LSB 或任何其他规范的一部分。AFAIK,它甚至没有正式的文件。

有一个大致的共识,它做什么: 采取一切之间的 !\nexec它。假设 !\n之间的所有内容都是到解释器的完全绝对路径。对于如果它包含空格会发生什么,目前还没有一致的意见。

  1. 有些操作系统只是简单地将整个事物视为路径。毕竟,在大多数操作系统中,空格或破折号在某条路径上是合法的。
  2. 有些操作系统在空白处分割,将第一部分作为解释器的路径,将其余部分作为单独的参数。
  3. 一些操作系统在 第一空白处分裂,将前部分视为通往 Interpeter 的路径,将其余部分视为 单身参数(这就是您正在看到的)。
  4. 有些甚至不支持 shebang 线 完全没有

谢天谢地,1。四。似乎已经消失了,但是3。是非常普遍的,所以你不能仅仅依靠能够通过一个以上的论点。

由于 POSIX 或 SUS 中也没有指定命令的位置,所以通常只需要将可执行文件的 姓名传递给 env,这样 就可以确定可执行文件的位置,例如:

#!/usr/bin/env gawk

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

这意味着实际上不能使用 任何参数 完全没有

我遇到了同样的问题,由于空格在 shebang 中的处理方式(至少在 Linux 上) ,没有明显的解决方案。

但是,您可以在一个 shebang 中传递几个选项,只要它们是 做空期权并且可以是 连在一起(GNU 方式)。

例如,你不能

#!/usr/bin/foo -i -f

但你可以拥有

#!/usr/bin/foo -if

显然,只有当选项有短的等价物并且没有参数时,这种方法才有效。

对于一个可移植的解决方案,使用 awk而不是 gawk,使用 shebang 调用标准的 BOURNE shell (/bin/sh) ,并直接调用 awk,在命令行上将程序作为 here 文档而不是通过 stdin 传递:

#!/bin/sh
gawk --re-interval <<<EOF
PROGRAM HERE
EOF

注意: awk没有 -f参数。这使得 stdin可供 awk从中读取输入。假设您已经安装了 gawk并且安装在 PATH上,那么就可以实现我认为您在原始示例中尝试做的所有事情(假设您希望文件内容是 awk 脚本,而不是输入,我认为您的 shebang 方法会将其视为输入)。

只是为了好玩: 下面有一个非常奇怪的解决方案,它通过文件描述符3和4重新路由 stdin 和程序。您还可以为脚本创建一个临时文件。

#!/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 对脚本进行变量扩展,因此必须引用每个 $(就像在脚本的第二行中做的那样) ,可能还不止这些。

#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''

上面的 shell shebang 技巧比 /usr/bin/env更具可移植性。

这对我来说似乎很有效。

#!/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

在 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

为什么不使用 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
}

(同样的事情也可以用 sed或者 tail来完成,但是我认为只有 bashgawk本身才有某种美感;)

虽然不是完全便携的,但从 coreutils 8.30和 根据它的文件开始,你将能够使用:

#!/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

原答案。