web-dev-qa-db-ja.com

argvにプログラム名が含まれているのはなぜですか?

典型的なUnix/Linuxプログラムは、コマンドライン入力を引数カウント(int argc)と引数ベクトル(char *argv[])。 argvの最初の要素はプログラム名で、その後に実際の引数が続きます。

プログラム名が実行可能ファイルに引数として渡されるのはなぜですか?独自の名前を使用したプログラムの例はありますか(多分何らかのexec状況)。

109

まず、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]がプロセスが自身の名前を知る唯一の方法でした。それは何に適していますか?

  • いくつかのプログラムは、呼び出された名前に応じて動作をカスタマイズします(通常は、シンボリックリンクまたはハードリンク、たとえば BusyBoxのユーティリティ 。この質問の他の回答では、いくつかの例が提供されています)。
  • さらに、syslogを介してログを記録するサービス、デーモン、およびその他のプログラムは、多くの場合、ログエントリの前に名前を付加します。これがなければ、イベント追跡は実行不可能に近くなります。
123
countermode

たくさん:

  • argv[0]shの場合、Bashは POSIXモード で実行されます。 argv[0]-で始まる場合、ログインシェルとして実行されます。
  • viviewevimeviewexvimdiffなどとして実行すると、Vimの動作が異なります。
  • すでに述べたように、Busybox。
  • Systemdがinitのシステムでは、shutdownrebootなどは systemctl へのシンボリックリンクです。
  • 等々。
62
muru

歴史的に、argvはコマンドラインの「単語」へのポインタの配列にすぎないため、プログラムの名前である最初の「単語」から始めるのが理にかなっています。

そして、それらを呼び出すために使用される名前に応じて異なる動作をするプログラムはかなり多くありますので、それらへの異なるリンクを作成し、異なる「コマンド」を取得することができます。私が考えることができる最も極端な例は busybox で、これは それがどのように呼び出されるかに応じて数十の異なる「コマンド」のように機能します です。

Edit:要求に応じて、Unix 1stエディションのリファレンス

たとえば、 ccおよびargcがすでに使用されているargvの-​​ main 関数から Shell は、コマンドを引数と同じように扱いながら、ループのparbuf部分内のnewargに引数をコピーします。 (もちろん、後で、コマンドの名前である最初の引数のみを実行します)。 execvのようで、当時は親戚が存在していませんでした。

35
dirkt

使用例:

プログラム名プログラムの動作を変更するを使用できます。

たとえば、実際のバイナリへのシンボリックリンクを作成できます。

この手法が使用される有名な例の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

ここでは、基本的にこの手法を使用しています多くの重複するソースファイルを回避するためまたは単にソースを読みやすくするため。

別のユースケースは、実行時にいくつかのモジュールまたはデータをロードする必要があるプログラムです。プログラムパスがあると、プログラムの場所を基準にしたパスからモジュールをロードできますになります。

さらに、多くのプログラムプログラム名を含むエラーメッセージを出力

なぜ

  1. それはPOSIX規約(man 3p execve):

argvは、新しいプログラムに渡される引数文字列の配列です。慣例により、これらの文字列の最初の文字列には、実行中のファイルに関連付けられているファイル名を含める必要があります。

  1. それはC標準です(少なくともC99とC11):

Argcの値がゼロより大きい場合、argv [0]が指す文字列はプログラム名を表します。 argv [0] [0]は、プログラム名がホスト環境から使用できない場合はnull文字になります。

C標準では「ファイル名」ではなく「プログラム名」と記載されていることに注意してください。

22
rudimeier

呼び出された方法に応じて動作を変更するプログラムに加えて、次のように、プログラムの使用状況を出力するときに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.
21
marcelm

次のタイプのプログラムを実行します:program_name0 arg1 arg2 arg3 ...

したがって、シェルはすでにトークンを分割しているはずであり、最初のトークンはすでにプログラム名です。ところで、プログラム側とシェルには同じインデックスがあります。

これは(ごく初期の段階で)単なる便宜的なトリックだったと思います。他の回答でわかるように、これも非常に便利だったので、この伝統は継続され、APIとして設定されました。

5

基本的に、argvにはプログラム名が含まれているため、prgm: file: No such file or directoryのようなエラーメッセージを書き込むことができます。これは、次のようなもので実装されます。

    fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[1] );
4
user628544

このアプリケーションのもう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 | $

ソース、およびその他の情報

2
cat

プログラムへのパスはargv[0]であるため、プログラムはインストールディレクトリから構成ファイルなどを取得できます。
これはargv[0]なしでは不可能です。

0
bob cook

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はキャッシュされたファイルを返すか、コマンドを実際のコンパイラに渡すことができます。

0