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


106

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

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


6
mvやcpのような?
アーケマー

9
Debianにshはのシンボリックリンクがありdashます。shまたはとして呼び出された場合、それらは異なる動作をしますdash
-Motte001

21
@AlexejMagura busybox(rescue- discなどで一般的)のようなものを使用する場合、ほとんどすべて(cp、mv、rm、ls、...)がbusyboxへのシンボリックリンクになります。
バールドコッペルード

11
私はこれが見つけてる本当に無視するのは難しいので、私はそれを言うだろう:あなたは、おそらく「GNU」のプログラムを意味する(gccbashgunzip、OSの残りのほとんどを...)、Linuxは単なるカーネルであるとして。
wizzwizz4

10
@ wizzwizz4「典型的なUnix / Linuxプログラム」の何が問題になっていますか?「Unix / Linuxで実行される典型的なプログラム」のように読みます。特定のGNUプログラムに対する制限よりもはるかに優れています。デニスリッチーは確かにGNUプログラムを使用していませんでした。ところで、Hurdカーネルは、主な機能を持たないGNUプログラムの例です
...-rudimeier

回答:


122

まずargv[0]、プログラム名ではないことに注意してください。これは、呼び出し側が何に入れているargv[0]execveシステムコール(例えば参照スタックオーバーフローにこの質問を)。(の他のすべてのバリアントexecは、システムコールではなく、へのインターフェイスexecveです。)

たとえば、次を想定します(を使用execl):

execl("/var/tmp/mybackdoor", "top", NULL);

/var/tmp/mybackdoorは実行されますが、argv[0]に設定されてtopおり、これがps(実際の)top表示内容です。詳細については、U&L SEの回答をご覧ください。

さておき、このすべてを設定する:のような派手なファイルシステムが登場する前に/procargv[0]自分自身の名前を学ぶプロセスのための唯一の方法でした。それは何に役立つでしょうか?

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

18
そのようなプログラムの例はbunzip2bzcatおよびでありbzip2、最初の2つは3つ目のシンボリックリンクです。
ルスラン

5
興味深いことにzcat、@ Ruslan はシンボリックリンクではありません。代わりにシェルスクリプトを使用して、この手法の欠点を回避しているようです。しかし、--helpgzipにオプションを追加した誰かがzcatを維持するのを忘れたため、完全な出力を印刷できません。
ルディミエ

1
私が覚えている限り、GNUコーディング標準は、プログラムの動作を変更するためにargv [0]の使用を推奨していません(現行バージョンの「インターフェースの標準」セクション)。gunzip歴史的な例外です。

19
busyboxは別の優れた例です。308の異なる名前で呼び出されて、異なるコマンドを呼び出すことができます:busybox.net/downloads/BusyBox.html#commands
Pepijn Schmitz

2
多くの、より多くのプログラムもargv[0]、名前をハードコーディングする代わりに、使用法/ヘルプ出力に挿入します。一部は完全に、一部はベース名のみです。
スペクトル

62

たくさん:

  • bashはで実行POSIXモード時にargv[0]ありますsh。でargv[0]始まるログインシェルとして実行されます-
  • 実行するとVimは異なる挙動をviviewevimeviewexvimdiff、など
  • 既に述べたように、Busybox。
  • INITようにsystemdを搭載したシステムでは、shutdownreboot、などですへのシンボリックリンクsystemctl
  • 等々。

7
もう一つはあるsendmailmail。すべてのUnix MTAには、これら2つのコマンドのシンボリックリンクが付属しており、そのように呼び出されたときに元の動作をエミュレートするように設計されています。
シャドゥール

4
他の一般的なケース:testおよび[:前者を呼び出すとき、最後の引数がの場合、エラーを処理します]。(実際のDebian安定版では、これらのコマンドは2つの異なるプログラムですが、以前のバージョンとMacOはまだ同じプログラムを使用しています)。そしてtexlatexなど:バイナリは同じですが、それがどのように呼び出されたかを見て、適切な構成ファイルを選択します。init似ています。
ジャコモカテナッツィ

4
関連して、[最後の引数がでない 場合はエラーと見なします]
-chepner

これは2番目の質問には答えますが、最初の質問には答えません。一部のOSデザイナーが座り込んで言ったのは非常に疑わしい»「ねえ、同じプログラムがその実行可能ファイル名だけに基づいて異なることをするのはクールだろう。名前を引数配列に含めると思います。«
ジョーイ

@Joeyはい、文言はそれを伝えることを意図しています(Q:「ありますか?」A:「たくさん:...」)
muru

34

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

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

編集:要求に応じて、Unix 1stエディションの参照

一つは、例えばから見ることができる主な機能ccそれargcargvすでに使用されました。シェルのコピーへの引数parbuf内部newargループの一部、引数と同様に、コマンド自体を処理しています。(もちろん、後でコマンドの名前である最初の引数のみを実行します)。それはそのように見えexecv、親relativeはその時存在しませんでした。


1
これをバックアップする参照を追加してください。
レスマナ

簡単なスキミングから、exec実行するコマンドの名前とcharポインターのゼロで終わる配列を取得します(minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.sで最もよく見られexecます)ラベル2およびラベル1への参照、およびラベルに2:が表示されetc/init\0、ラベル1:にラベル2への参照と終了ゼロexecveが表示されますenvp
ninjalj

1
execvそしてexecl「永久に」(つまり、1970年代初期から中期まで)存在していました— execvシステムコールであり、それexeclを呼び出すライブラリ関数でした。   execveその時は環境が存在しなかったので、その時は存在しませんでした。家族の他のメンバーは後で追加されました。
G-マン

@ G-Man execvリンクしたv1ソースで私を指摘できますか?ちょっと興味があるんだけど。
dirkt

22

ユースケース:

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

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

この手法が使用される有名な例の1つは、1つのバイナリと多くのシンボリックリンクのみをインストールするbusyboxプロジェクトです。(ls、cp、mvなど)。ターゲットは小さな組み込みデバイスであるため、ストレージスペースを節約するためにそれ行っています。

これはsetarch、util-linuxからも使用されます。

$ 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]はヌル文字でなければなりません。

C標準では、「ファイル名」ではなく「プログラム名」と表示されています。


3
別のシンボリックリンクからシンボリックリンクに到達した場合、これは壊れませんか?
Mehrdad

3
@Mehrdad、はい、それは欠点であり、ユーザーを混乱させる可能性があります。
ルディミエ

@rudimeier:あなたの「なぜ」アイテムは本当の理由ではなく、単なる「ホムンクルス」です。つまり、なぜ規格がこれを要求するのかという疑問を招きます。
アインポクルム

@einpoklum OPの質問は、プログラム名が実行可能ファイルに渡されるのはなぜですか?私が答えたのは、POSIXとC標準がそうするように指示しているからです。それは本当に理由ではないとどう思いますか?私が引用したドキュメントが存在しない場合、おそらく多くのプログラムがプログラム名を渡さないでしょう。
ルディミエ

OPは、「POSIXおよびC規格はなぜこれを行うのですか?」言葉遣いは抽象化されたレベルであったことは確かですが、それは明らかです。現実的には、知る唯一の方法は発信者に尋ねることです。
user2338816

21

呼び出された方法に応じてプログラムの動作を変更するプログラムに加えて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.

3
+1。私は同じことを提案しようとしていました。奇妙なことに、多くの人々が行動の変化に焦点を当てており、おそらく最も明白ではるかに普及している使用法について言及していない。
ヴィー

5

次のように入力してプログラムを実行します program_name0 arg1 arg2 arg3 ...

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

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


4

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

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

2

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

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


1000に達しおめでとうございます
G-マンを

0

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


2
これは特に良い説明ではありません(char *path_to_program, char **argv, int argc)
-moopet

私の知る限り、ほとんどのプログラムは、標準の場所から設定を引き出します(~/.<program>/etc/<program$XDG_CONFIG_HOME)とのいずれか、それを変更したり、バイナリに定数で焼くコンパイル時のオプションを持っているパラメータを取ります。
熊Chiamiov

0

ccacheは、コンパイラバイナリへのさまざまな呼び出しを模倣するために、このように動作します。ccacheはコンパイルキャッシュです-全体のポイントは、同じソースコードを2回コンパイルすることではなく、可能であればキャッシュからオブジェクトコードを返すことです。

ccacheのmanページ、「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がキャッシュされたファイルを返すか、コマンドを実際のコンパイラーに渡すことを可能にすることです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.