施邦还是不施邦

施邦还是不施邦

我想在 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 命令是执行脚本内容的脚本引擎;它可能是bashperl或其他任何内容,但期望它是使用要执行的脚本的文件名来调用的。是如何bash做到的呢?它不会猜测。如果它遇到任何看起来不像选项(或选项的参数)的参数,它会将其视为要执行的脚本;之后的参数将传递给脚本。例如:

/bin/bash -x -e somename foo bar

在这里,bash 将查找该文件并尝试将其作为带有参数和 的somename脚本运行。你也应该做同样的事情,因为你foobar可能有一天想<mypgm> <myscript>在命令行上写。

如果您希望默认使用无脚本<mypgm>,则可以要求使用 传递脚本<mypgm> -f <myscript>。这是怎么sed做到的。然后你可以在 shebang 行中使用它,如下所示:

#!<mypgm> -f

如果您希望脚本大小写成为默认值,例如使用bashperl,请创建一个选项“这次没有脚本”。您可以使用--它,这样<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/perlperl ./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

(我知道我在这里稍微翻转了桌子,我们仍然必须确保这是一个便携式解决方案)。

相关内容