我想在 shebang 中使用一个程序,因此我创建了一个名为 <myscript> 的脚本:
#!<mypgm>
我还希望能够直接从命令提示符运行 <mypgm> 。
<mypgm> args...
到目前为止,没有问题。
我希望能够使用参数从命令提示符运行 <myscript> 。
<myscript> blabla
反过来,shebang 会使用以下参数调用 <mypgm>:
<mypgm> <myscript> blabla
现在,我需要知道何时使用 shebang 调用 <mypgm> <myscript> blabla:
myscript blabla # uses the shebang
-or-
<mypgm> myscript blabla # directly in the command prompt.
我查看了环境变量(编辑:<===错误的断言(??,??”) ),在进程表(也是父进程),但没有找到任何方法来有所作为。
到目前为止我唯一发现的是:
grep nonvoluntary_ctxt_switches /proc/$$/status
当该行紧接在 shebang 之后时,通过 shebang 调用时该值通常为 2(有时为 3),而通过直接调用时该值通常为 1(有时为 2)。由于不稳定并且依赖于进程调度(进程从 CPU 中脱离的次数),我想知道这里是否有人可能有更好的解决方案。
答案1
与其myprg
神奇地检测它是否在 shebang 中使用,为什么不通过使用命令行标志(例如-f
)将文件作为脚本传递给它来明确这一点?
从您在评论中的示例:
在上面的计算理论示例中。
calc PI + 1
应该返回 4.14159... 现在添加对 shebang 的支持(即文件名作为第一个参数)将返回文件中包含的计算。
获取calc
脚本文件-f
,然后使用以下命令创建脚本:
#!/usr/local/bin/calc -f
$1 + 1
假设您调用该文件addone.calc
并使其可执行。然后你可以用以下方式调用它:
$ ./addone.calc PI
4.141592...
该调用将转换为 的调用/usr/local/bin/calc -f ./addone.calc PI
,因此很清楚哪个参数是脚本文件,哪个参数是脚本的参数。
awk
这与行为方式和行为类似sed
。
类似(但相反)的方法是calc
默认采用脚本文件参数(这简化了其与 shebang 的使用),但添加命令行标志以将其与参数中的表达式一起使用。这与工作原理类似sh -c '...'
。
答案2
真正的问题是您设计<mypgm>
.不要试图支持两种解释其参数的方式,而是提供两种调用它的方式。
Shebang 命令是执行脚本内容的脚本引擎;它可能是bash
、perl
或其他任何内容,但期望它是使用要执行的脚本的文件名来调用的。是如何bash
做到的呢?它不会猜测。如果它遇到任何看起来不像选项(或选项的参数)的参数,它会将其视为要执行的脚本;之后的参数将传递给脚本。例如:
/bin/bash -x -e somename foo bar
在这里,bash 将查找该文件并尝试将其作为带有参数和 的somename
脚本运行。你也应该做同样的事情,因为你foo
bar
可能有一天想<mypgm> <myscript>
在命令行上写。
如果您希望默认使用无脚本<mypgm>
,则可以要求使用 传递脚本<mypgm> -f <myscript>
。这是怎么sed
做到的。然后你可以在 shebang 行中使用它,如下所示:
#!<mypgm> -f
如果您希望脚本大小写成为默认值,例如使用bash
和perl
,请创建一个选项“这次没有脚本”。您可以使用--
它,这样<mypgm> -- one two three
就不会尝试one
作为脚本运行(或其他任何内容)。在这种情况下,shebang 行将只显示:
#!<mypgm>
答案3
现在,我需要知道何时使用 shebang 调用 blabla:
在 C 中,您可以通过以下方式获取该信息getauxval(AT_EXECFN)
,它会告诉您原始可执行文件的名称(即传递给 的第一个参数execve(2)
)[1]。
但是该字符串被立即放置在内存中,位于命令行参数和环境字符串之后,位于内存区域的末尾[stack]
,因此可以直接从那里获取它。
例如,以下 perl 脚本(将其命名为foo.pl
),如果使用 使其可执行,则在直接运行和运行时chmod 755 foo.pl
将打印:./foo.pl
/usr/bin/perl
perl ./foo.pl
#! /usr/bin/perl
open my $maps, "/proc/self/maps" or die "open /proc/self/maps: $!";
my $se;
while(<$maps>){ $se = hex($1), last if /^\w+-(\w+).*\[stack\]$/ }
open my $mem, "/proc/self/mem" or die "open /proc/self/mem: $!";
sysseek $mem, $se - 512, 0;
sysread $mem, $d, 512 or die "sysread: $!";
print $d =~ /([^\0]+)\0+$/, "\n";
在较新的 (>=3.5) Linux 内核上,环境结束也可以在/proc/PID/stat
(第 51 个字段中,如文档中所述)proc(5)
联机帮助页)。
#! /usr/bin/perl
open my $sh, "/proc/self/stat" or die "open /proc/self/stat: $!";
my @s = <$sh> =~ /\(.*\)|\S+/g;
open my $mem, "/proc/self/mem" or die "open /proc/self/mem: $!";
seek $mem, $s[50], 0;
$/ = "\0";
my $pn = <$mem> or die "readline: $!"; chomp $pn; print "$pn\n";
[1] 2.6.26 之后的 Linux 内核引入了指向它的 aux 向量条目(请参阅犯罪),但可执行文件名称早在这之前就已在堆栈末尾可用(自 1996 年的 linux-2.0 起)。
答案4
刚刚意识到以下环境变量可以完成这一切:$_
使用 <myscript> 启动时,其值为“./<myscript>”
当使用 <mypgm> <myscript> 启动时,其值是 <mypgm> 的完整路径。
就我而言,就这么简单:
#!/bin/bash
how_called=$_
if [[ "X$how_called" == X$0 || "X$how_called" ==X$BASH ]]; then
# ^in this case, if the login shell is not bash
shebang=0
else
shebang=1
fi
bn=$(basename $0)
稍后(为了我的目的):
if (( shebang == 1 )) || [[ ! -z $1 && "X$1" != X-* && "X$1" == X*\.${bn:0:3} && -x $1 ]]; then
# ^ shebang: first argument is the script file
# ^ or not shebang: first argument **may** be a script file name
# ^ ensure that this is a script by script extension
# (otherwise just use the more verbose but standard --script=...)
shebang_fn="$1"
shift 1
set -- --script="$shebang_fn" "$@" # fall back on standard way.
fi
(我知道我在这里稍微翻转了桌子,我们仍然必须确保这是一个便携式解决方案)。