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


118

我想使用shebang 执行gawk脚本--re-interval。“天真”的方法

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

这是行不通的,因为gawk是用第一个参数"--re-interval -f"(不能在空白处拆分)调用的,它无法理解。有没有解决方法?

当然,您不能直接调用gawk,而是将其包装到一个拆分第一个参数的shell脚本中,或者制作一个外壳脚本,然后再调用gawk并将该脚本放入另一个文件中,但是我想知道是否有某种方法可以执行在一个文件中。

shebang行的行为因系统而异-至少在Cygwin中,它不按空格分隔参数。我只是关心如何在这样的系统上进行操作。该脚本不是可移植的。


1
我刚刚做的一个愚蠢的实验是在shebang行上使用另一个脚本使用一个脚本,该脚本确实正确地分割了参数。
Hasturkun 2010年

@Hasturkun提出了另一个问题,不管调用的程序本身是否可以是脚本,shebang行的行为在系统之间也有所不同。
dubiousjim 2012年


对于最新版本的gawk(> = 4.0),--re-interval不再需要(请参阅[ gnu.org/software/gawk/manual/…)。

Answers:


25

(g)awk似乎对我有用。

#!/bin/sh
arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"


# The real awk program starts here
{ print $0 }

注意#!runs /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经常使用,因此我不确定我在第二行上的trick俩是否是强迫awk忽略该行的好方法。
亚伦·麦克戴德2014年

我想知道的是+1,但可能不明智(因此是相对票数)。
亚伦·霍尔

您能解释一下@AaronHall这可能会有什么问题吗?只要该变量arbitrary_long_name不与实际awk程序中使用的变量冲突,我就看不到任何问题。有什么我想念的吗?
亚伦·麦克戴德

如果使用以第一个字符作为第零个参数调用脚本,请使用#!/bin/sh -代替而不是#!/bin/sh保护脚本避免以危险的方式出现异常-。这在C语言这样的编程语言中可能会偶然发生,在这种情况下,很容易因忘记将调用的程序名称作为参数数组的一部分传递给execve类似函数而容易被搞乱,而且如果人们习惯性地忘记对此加以保护,最终成为可恶意利用的漏洞的最后一步,该漏洞使攻击者可以获得交互式外壳。
mtraceur

161

从未将shebang行指定为POSIX,SUS,LSB或任何其他规范的一部分。AFAIK,甚至还没有正确记录。

关于什么它粗糙的共识:取之间的一切!\nexec它。假定在!和之间的所有内容都是\n通往解释器的完整绝对路径。对于包含空格的情况,目前尚无共识。

  1. 一些操作系统只是将整个过程视为路径。毕竟,在大多数操作系统中,空格或破折号在路径中都是合法的。
  2. 某些操作系统在空格处拆分,并将第一部分作为通往解释器的路径,其余部分作为单独的参数。
  3. 某些操作系统在第一个空格处拆分,并将前端部分作为交织器的路径,其余部分作为单个参数(这就是您所看到的)。
  4. 有些人甚至不支持家当线在所有

值得庆幸的是,1.和4.似乎已经消失了,但是3.相当普遍,因此您根本不能依靠能够传递多个论点。

而且,由于还没有POSIX或SUS指定命令的位置,您通常使用最多的是通过将可执行文件的单个参数名称,以env使能够确定可执行文件的位置; 例如:

#!/usr/bin/env gawk

[显然,这仍然是针对的特定路径env,但是它所在的系统很少/bin,因此通常是安全的。的位置env比的位置多了很多标准化的gawk或者类似的东西更糟糕pythonrubyspidermonkey]

这意味着你不能真正使用任何参数在所有


1
FreeBSD的env​​有一个-S开关,可以在这里提供帮助,但是我的Linux上没有它env,而且我怀疑gygwin也没有。@hstoerr,其他情况不同的用户稍后可能会阅读您的问题,因此,即使您现在不需要便携性,通常也最好使用便携式答案。
dubiousjim 2012年

4
因此,我们无法在shebang中方便地使用参数。但是,如果我们需要采取任何必要的手段怎么办?我猜测解决方案是编写包含#!/bin/sh和的包装器外壳脚本/usr/bin/env gawk --re-interval -f my-script.awk。那是对的吗?
罗里·奥肯

1
我不同意。您可以非常方便地使用一个参数。不能使用任何参数的任何系统都将无法实现这种传统的Unixism,这就是hash-bang。如果非实施是公平的游戏,那么我们可以肯定地说它#!本身不是可移植的。例如,Windows根本不“本机”识别此约定。传统上,Unix上需要一个参数就可以做到#!/usr/bin/awk -f
卡兹(Kaz)2014年

7
@Kaz:是的,但是由于许多二进制文件的路径不是标准化的,因此您将用完一个参数#!/usr/bin/env ruby或类似参数。
约尔格W¯¯米塔格

3
@Pacerier:更改POSIX规范,并等待20-30年,直到所有系统都更新为符合该规范。
约尔格W¯¯米塔格

18

尽管不是完全可移植,但是从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

原始答案在这里


1
仅供参考,FreeBSD多年来一直使用-S(自6.0开始)。这是coreutils受欢迎的可移植性补充。
胡安

12

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

但是,您可以在Shebang中传递几个选项,只要它们是简短选项并且可以将它们串联即可(GNU方式)。

例如,您不能

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

但你可以有

#!/usr/bin/foo -if

显然,这仅在选项具有短等价且不带参数的情况下有效。


11

在Cygwin和Linux下,shebang路径之后的所有内容都作为一个参数解析到程序中。

可以通过awk在shebang中使用另一个脚本来解决这个问题:

#!/usr/bin/gawk {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}

这将{system("/usr/bin/gawk --re-interval -f " FILENAME); exit}在awk中执行。
这将/usr/bin/gawk --re-interval -f path/to/your/script.awk在您的系统外壳中执行。


2
如果您已将参数传递给脚本,则此方法将不起作用
Steven Penny

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

上面的shell shebang技巧比/usr/bin/env


''':是一个保留项,因为我最初的解决方案是针对python脚本的,所以''':'告诉python解释器忽略exec部分。
user3123730 2014年

4
我认为您之所以被否决是因为您的解决方案是针对的python,但是这个问题是关于awk
亚伦·麦克戴德2014年

1
python很棒的工具。
Zaar Hai

3

在gawk手册(http://www.gnu.org/manual/gawk/gawk.html)中,请注意第1.14节的结尾,从shebang行运行gawk时,只应使用单个参数。它说,操作系统会将gawk路径之后的所有内容都视为一个参数。也许还有另一种方法来指定--re-interval选项?也许您的脚本可以在shebang行中引用您的shell,gawk作为命令运行,并将脚本的文本作为“此处文档”包含在内。


似乎没有其他方法可以指定该选项。您是对的:gawk -f-<< EOF,一些脚本行,EOF可以工作,但是它使我无法使用gawk读取标准输入。
汉斯·彼得·斯托尔

这里的文档占用了的标准输入流gawk,但是您仍然可以通过stderr传递内容(即,在传递到此脚本之前,将stdout重定向到stderr)。我从来没有尝试过,但是只要第一个进程在stderr上不发出任何东西,它就可以工作。如果要确保没有其他东西在使用它,还可以创建一个命名管道(linuxjournal.com/content/using-named-pipes-fifos-bash)。
2010年

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
}

(-the同样可以自然也可以用如完成sedtail,但我觉得有只依赖某种美感bashgawk自身;)


0

只是为了好玩:以下很奇怪的解决方案可以通过文件描述符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在脚本上进行变量扩展,因此您必须引用每个$(如脚本第二行所示),并且可能还要更多。


-1

对于便携式解决方案,请使用awk而不是,使用shebang gawk调用标准的BOURNE shell(/bin/sh),然后awk直接调用,将程序作为此处文档而不是通过stdin传递到命令行:

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

注:没有-f参数awk。这样就stdin可以awk从中读取输入。假设您已经gawk安装并安装了,就可以PATH实现我认为您要使用原始示例进行的所有操作(假设您希望文件内容是awk脚本而不是输入,我认为您的shebang方法会将其视为)。


3
那对我没用。ash子说:<<< blabla把blabla放在stdin上。您是说<<-EOF吗?无论哪种方式,都将程序放在stdin上。
汉斯·彼得·斯托尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.