はbash
とexecve
(少なくとも、それはLinuxとFreeBSDに文書化されて、私は他のシステムは同様の文書を持っていると推定)の文書化動作であり、argv[0]
が構築されるさまざまな方法を反映しています。
提供されたコマンドラインから、さまざまな拡張を実行し、必要に応じて単語を再分割した後に、Bash(ほかのシェルと同様)がargv
を構成します。最終結果は、あなたが入力したとき
printargv
argv
が{ "printargv", NULL }
のように構成されていることであり、あなたが
to/printargv
argv
を入力すると{ "to/printargv", NULL }
として構成されています。だからそこに驚きはない。
は
(どちらの場合も、そこにコマンドライン引数されていた、彼らは位置1から始まるargv
に登場しているだろう)しかし、実行パスはその時点で発散します。コマンドラインの最初の単語に/が含まれている場合は、相対または絶対のいずれかのファイル名であるとみなされます。シェルはこれ以上の処理を行いません。指定されたファイル名がfilename
の引数で、argv
の配列がargv
の引数として構築されているだけで、execve
が呼び出されます。この場合、argv[0]
は正確filename
に対応するが、コマンドにはスラッシュを持っていないとき:
printargv
シェルは、より多くの作業を行います。
まず、それはかどうかをチェック名前がユーザー定義のシェル関数である場合そうであれば、それは配列から既に取り出された$1...$n
を使って実行します。 ($0
は引き続きスクリプト呼び出しからargv[0]
になります)。
次に、名前が組み込みのbashコマンドであるかどうかを確認します。そうであれば、それを実行します。組み込み関数がコマンドライン引数とどのように相互作用するかは、この回答の対象外であり、実際にユーザーが見ることはできません。
最後に、$PATH
のコンポーネントを検索し、実行可能ファイルを検索することによって、コマンドに対応する外部ユーティリティを見つけようとします。見つかった場合は、execve
を呼び出し、見つかったパスにfilename
引数を指定しますが、コマンドの単語からなるargv
配列を使用します。この場合、filename
とargv[0]
はdo ではなく、に対応しています。
したがって、両方の場合において、シェルは、filename
引数とargv
引数として単語分割コマンドとしてファイルパス(おそらく相対)を提供する、execve
を呼び出してしまいます。
指定されたファイルが実行可能イメージの場合は、それ以上のことは言いません。イメージはメモリにロードされ、main
が提供されたargv
ベクトルで呼び出されます。 argv[0]
は、最初に入力されたものだけに応じて、単一の単語または相対パスまたは絶対パスになります。
しかし、指定されたファイルがスクリプトの場合、ローダーはエラーを生成し、execve
はファイルがシバン(#!
)で始まるかどうかを確認します。 (POSIX 2008年以来、execve
はまた、シェバングラインとして#!/bin/sh
を持っていたかのように、システムのシェルを使用してファイルをスクリプトとして実行しようとします。)
はここでLinux上execve
のドキュメントです:
#! interpreter [optional-arg]
インタプリタは、実行可能ファイルの有効なパス名でなければなりません:インタプリタスクリプトが許可有効になっており、その最初の行の形式ですが、実行持つテキストファイルです。 execveのfilename引数は()インタプリタスクリプトを指定している場合、インタプリタは、次の引数で呼び出されます:arg...
は一連の単語である
interpreter [optional-arg] filename arg...
はargv[1]
から始まる、execve()
のargv
引数によって指さ。上記では、filename
引数はexecve
からfilename
引数であることを
注意。シェバングライン#!/bin/bash
を考えると、私たちは今、argv[0]
が効果的に消滅していることに注意してください
/bin/bash to/printargv # If the original invocation was to/printargv
または
/bin/bash /path/to/printargv # If the original invocation was printargv
のいずれかを持っています。
bash
このファイルでスクリプトを実行します。スクリプトを実行する前に、$0
に指定されたファイル名の引数(to/printargv
または/path/to/printargv
)を設定し、元のコマンドラインのコマンドライン引数からコピーされた残りの引数に$1...$n
を設定します。要約すると
あなたはスラッシュなしでファイル名を使用して、コマンドを呼び出す場合、:
あなたはスラッシュとファイル名を使用してコマンドを起動した場合は入力されたとして、両方のケースでは、(相対的かもしれないが、明らかに常にスラッシュを持っています)ARGV [0]ファイル名として表示されます。
一方、シェルインタープリタを明示的に起動してスクリプトを呼び出すと(bash printargv
)、スクリプトには入力されたとおりにファイル名として$0
が表示されます。これは相対的であるだけでなく、スラッシュも含まれません。
これは、あなたが模倣したいスクリプトをどのような形で呼び出すかを知っている場合にのみ、 "注意深くargv [0]"を模倣できることを意味します。
ユニットテストでこれを行う場合は、どの値をargvとして指定するかを指定する必要があります[ 0]。 $0
を解析しようとする多くのシェルスクリプトは、それがファイルパスであると仮定します。彼らはそれをするべきではないかもしれないので、それをするべきではありませんが、そこにあります。あなたはそれらのユーティリティを喫煙したい場合は、ごみの値を$0
としてください。それ以外の場合は、デフォルトとしてスクリプトファイルへのパスを指定することをお勧めします。
返信いただきありがとうございます。 printargs.shは確かにシバンを持っています。私は2行のソースコードを投稿しました。 これは依然として疑問の重要な部分を残しています:$ argv [0]をbashスクリプトで正確に模倣する正しい方法は何でしょうか? –
@jason、ええ、申し訳ありませんが、私は中断されました。私は数時間で答えを終わらせます。 – rici
@ jason:OK、答えを書き換えました。それが役に立てば幸い。私はargv [0]がこれかもしれないし、そうかもしれないし、理想的にはbashスクリプトがどちらかで動作するという "正しい方法"があるとは思わない。だから、もしあなたがテストしているなら、$ 0のさまざまな値を使ってスクリプトをテストするべきです。あなたがそれに妥当なものを与えようとしているだけの場合は、完全な絶対ファイル名を使用することをお勧めします。詳細な返信をいただきありがとうございます。 – rici