典型的なUnix/Linuxプログラムは、コマンドライン入力を引数カウント(int argc
)と引数ベクトル(char *argv[]
)。 argv
の最初の要素はプログラム名で、その後に実際の引数が続きます。
プログラム名が実行可能ファイルに引数として渡されるのはなぜですか?独自の名前を使用したプログラムの例はありますか(多分何らかのexec
状況)。
まず、argv[0]
は必ずしもプログラム名ではないことに注意してください。これは、呼び出し元がexecve
システムコールのargv[0]
に入力するものです(例: スタックオーバーフローに関するこの質問 を参照)。 (exec
の他のすべてのバリアントはシステムコールではなく、execve
へのインターフェイスです。)
たとえば、以下を想定します(execl
を使用):
execl("/var/tmp/mybackdoor", "top", NULL);
/var/tmp/mybackdoor
は実行されるものですが、argv[0]
はtop
に設定されており、これがps
または(実際の)top
が表示するものです。これについて詳しくは、U&L SEの this answer を参照してください。
これらすべてを脇に置く:/proc
のような豪華なファイルシステムが登場する前は、argv[0]
がプロセスが自身の名前を知る唯一の方法でした。それは何に適していますか?
歴史的に、argv
はコマンドラインの「単語」へのポインタの配列にすぎないため、プログラムの名前である最初の「単語」から始めるのが理にかなっています。
そして、それらを呼び出すために使用される名前に応じて異なる動作をするプログラムはかなり多くありますので、それらへの異なるリンクを作成し、異なる「コマンド」を取得することができます。私が考えることができる最も極端な例は busybox で、これは それがどのように呼び出されるかに応じて数十の異なる「コマンド」のように機能します です。
Edit:要求に応じて、Unix 1stエディションのリファレンス
たとえば、 cc
およびargc
がすでに使用されているargv
の- main 関数から Shell は、コマンドを引数と同じように扱いながら、ループのparbuf
部分内のnewarg
に引数をコピーします。 (もちろん、後で、コマンドの名前である最初の引数のみを実行します)。 execv
のようで、当時は親戚が存在していませんでした。
使用例:
プログラム名プログラムの動作を変更するを使用できます。
たとえば、実際のバイナリへのシンボリックリンクを作成できます。
この手法が使用される有名な例の1つは、単一のバイナリとそれに多数のシンボリックリンクをインストールするbusyboxプロジェクトです。 (ls、cp、mvなど)。彼らはそれをやっているストレージスペースを節約するために彼らのターゲットは小さな組み込みデバイスだからです。
これは、util-linuxのsetarch
でも使用されます。
$ ls -l /usr/bin/ | grep setarch
lrwxrwxrwx 1 root root 7 2015-11-05 02:15 i386 -> setarch
lrwxrwxrwx 1 root root 7 2015-11-05 02:15 linux32 -> setarch
lrwxrwxrwx 1 root root 7 2015-11-05 02:15 linux64 -> setarch
-rwxr-xr-x 1 root root 14680 2015-10-22 16:54 setarch
lrwxrwxrwx 1 root root 7 2015-11-05 02:15 x86_64 -> setarch
ここでは、基本的にこの手法を使用しています多くの重複するソースファイルを回避するためまたは単にソースを読みやすくするため。
別のユースケースは、実行時にいくつかのモジュールまたはデータをロードする必要があるプログラムです。プログラムパスがあると、プログラムの場所を基準にしたパスからモジュールをロードできますになります。
さらに、多くのプログラムプログラム名を含むエラーメッセージを出力。
なぜ:
man 3p execve
):argvは、新しいプログラムに渡される引数文字列の配列です。慣例により、これらの文字列の最初の文字列には、実行中のファイルに関連付けられているファイル名を含める必要があります。
Argcの値がゼロより大きい場合、argv [0]が指す文字列はプログラム名を表します。 argv [0] [0]は、プログラム名がホスト環境から使用できない場合はnull文字になります。
C標準では「ファイル名」ではなく「プログラム名」と記載されていることに注意してください。
呼び出された方法に応じて動作を変更するプログラムに加えて、次のように、プログラムの使用状況を出力するときにargv[0]
が便利です。
printf("Usage: %s [arguments]\n", argv[0]);
これにより、使用法メッセージは常に、それが呼び出された名前を使用します。プログラムの名前を変更すると、その使用法のメッセージもそれに応じて変わります。呼び出されたパス名も含まれます。
# cat foo.c
#include <stdio.h>
int main(int argc, char **argv) { printf("Usage: %s [arguments]\n", argv[0]); }
# gcc -Wall -o foo foo.c
# mv foo /usr/bin
# cd /usr/bin
# ln -s foo bar
# foo
Usage: foo [arguments]
# bar
Usage: bar [arguments]
# ./foo
Usage: ./foo [arguments]
# /usr/bin/foo
Usage: /usr/bin/foo [arguments]
それはいい感じです。特に、あちこちに存在する可能性のある小さな特殊用途のツールやスクリプトにとっては。
これは、GNUツールでも一般的な方法のようです。たとえば、ls
を参照してください。
% ls --qq
ls: unrecognized option '--qq'
Try 'ls --help' for more information.
% /bin/ls --qq
/bin/ls: unrecognized option '--qq'
Try '/bin/ls --help' for more information.
次のタイプのプログラムを実行します:program_name0 arg1 arg2 arg3 ...
。
したがって、シェルはすでにトークンを分割しているはずであり、最初のトークンはすでにプログラム名です。ところで、プログラム側とシェルには同じインデックスがあります。
これは(ごく初期の段階で)単なる便宜的なトリックだったと思います。他の回答でわかるように、これも非常に便利だったので、この伝統は継続され、APIとして設定されました。
基本的に、argvにはプログラム名が含まれているため、prgm: file: No such file or directory
のようなエラーメッセージを書き込むことができます。これは、次のようなもので実装されます。
fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[1] );
このアプリケーションのもう1つの例は、y
ではないものを入力するまで、このプログラム自体を...自体に置き換えるプログラムです。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char** argv) {
(void) argc;
printf("arg: %s\n", argv[1]);
int count = atoi(argv[1]);
if ( getchar() == 'y' ) {
++count;
char buf[20];
sprintf(buf, "%d", count);
char* newargv[3];
newargv[0] = argv[0];
newargv[1] = buf;
newargv[2] = NULL;
execve(argv[0], newargv, NULL);
}
return count;
}
明らかに、興味深いと思われる例ですが、これは本当の用途があると思います。たとえば、自己更新バイナリは、ダウンロードまたは変更した自身の新しいバージョンで自身のメモリ空間を書き換えます。
例:
$ ./res 1
arg: 1
y
arg: 2
y
arg: 3
y
arg: 4
y
arg: 5
y
arg: 6
y
arg: 7
n
7 | $
プログラムへのパスはargv[0]
であるため、プログラムはインストールディレクトリから構成ファイルなどを取得できます。
これはargv[0]
なしでは不可能です。
ccache は、コンパイラバイナリへのさまざまな呼び出しを模倣するために、このように動作します。 ccacheはコンパイルキャッシュです。重要なのは、同じソースコードを2回コンパイルすることではなく、可能であればキャッシュからオブジェクトコードを返すことです。
ccacheのマニュアルページ から、「ccacheを使用する方法は2つあります。コンパイルコマンドの前にccacheを付けるか、シンボリックリンクを作成してccacheをコンパイラーに見せかけることができます(コンパイラー)からccacheへ。最初の方法は、ccacheを試してみたい場合や、特定のプロジェクトで使用したい場合に最も便利です。2番目の方法は、すべてのコンパイルでccacheを使用したい場合に最も便利です。 "
Symlinksメソッドには、次のコマンドの実行が含まれます。
cp ccache /usr/local/bin/
ln -s ccache /usr/local/bin/gcc
ln -s ccache /usr/local/bin/g++
ln -s ccache /usr/local/bin/cc
ln -s ccache /usr/local/bin/c++
... etc ...
...その効果により、ccacheはコンパイラに送信されたはずのコマンドを取得でき、ccacheはキャッシュされたファイルを返すか、コマンドを実際のコンパイラに渡すことができます。