web-dev-qa-db-ja.com

シバンかシバンでないか

シバンでプログラムを使用したいので、<myscript>という名前のスクリプトを作成します。

#!<mypgm>

また、コマンドプロンプトから直接<mypgm>を実行できるようにしたいと考えています。

<mypgm> args...

これまでのところ、問題はありません。

コマンドプロンプトから引数を指定して<myscript>を実行できるようにしたい。

<myscript> blabla

次に、Shebangは<mypgm>を次の引数で呼び出します。

<mypgm> <myscript> blabla

今、私は<mypgm> <myscript> blablaがShebangを使用して呼び出されるかどうかを知る必要があります:

myscript blabla # uses the Shebang
-or-
<mypgm> myscript blabla   # directly in the command Prompt.

環境変数(編集:<===間違ったアサーション(¬、¬”))をプロセステーブル(親プロセスも)で調べましたが、違いを生む方法が見つかりませんでした。

私がこれまでに見つけた唯一のものは:

grep nonvoluntary_ctxt_switches /proc/$$/status

この行がシバンの直後にある場合、シバンを介して呼び出される場合、値は多くの場合2(場合によっては3)であり、直接呼び出しの場合は1(場合によっては2)です。不安定で、プロセスのスケジューリング(プロセスがCPUから切り離された回数)に依存しているため、ここで誰かがより良い解決策を持っているのではないかと思っています。

4
Jacques

次の環境変数がすべてを実行することに気づきました:$ _

<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

(私はここで少し表をひっくり返しており、これが移植可能なソリューションであることを確認する必要があることを知っています)。

0
Jacques

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 '...'の動作に似ています。

21
filbranden

実際の問題は、<mypgm>のコマンドライン構文の設計方法です。引数を解釈する2つの方法をサポートする代わりに、代わりに2つの方法で呼び出します。

シバンコマンドは、スクリプトのコンテンツを実行するスクリプトエンジンを意味します。 bashPerlなどの場合がありますが、実行するスクリプトのファイル名を指定して呼び出されることが予想されます。 bashはどのように実行しますか?それは推測しません。オプションのように見えない引数(またはオプションの引数)を検出した場合は、実行するスクリプトとして扱います。その後の引数はスクリプトに渡されます。例えば:

/bin/bash -x -e somename foo bar

ここで、bashはファイルsomenameを探し、引数foobarを使用してスクリプトとして実行しようとします。 可能性があるコマンドラインで<mypgm> <myscript>をいつか書きたいので、同じことをする必要があります。

スクリプトなしで<mypgm>をデフォルトで使用する場合は、<mypgm> -f <myscript>を使用してスクリプトを渡すように要求できます。これがsedのやり方です。次に、次のようなシバン行で使用します。

#!<mypgm> -f

bashPerlのように、スクリプトケースをデフォルトにしたい場合は、「今回はスクリプトがない」というオプションを作成します。このために--を使用して、<mypgm> -- one two threeone(またはその他)をスクリプトとして実行しないようにすることができます。その場合、Shebangの行は次のようになります。

#!<mypgm>
8
alexis

今、私はシバンを使用して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/statproc(5) マンページに記載されている51番目のフィールド)でも利用できます。

#! /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 vectorエントリを導入しました( commit を参照)。しかし、実行可能ファイル名は、それよりずっと前にスタックの最後にありました(linux- 2.0から1996)。

5
mosvy

通常のcalcプログラムは、calc PI + 3(および拡張子calc -f script_file_name)のように使用できます。

Shebangで使用するためのリンクの作成(ハードリンクのみが機能します)。 calcf、次にcalcプログラムで実行可能ファイル名を確認します(C/C++の場合は、関数mainargv[0]を確認してください)。スクリプトに#! /some/path/calcfが含まれています。

そうすれば、コマンドラインで-cのようなオプションを使用する必要がなくなり(3つのキーストロークが保存されます)、Shebangでオプションが不要になります(Scottのコメントの時点で問題になる可能性があります Shebangまたはnot Shebang ) 。

2
user351677