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

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

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

コメント

  • mvやcpのように?
  • Debianでは、shdashへのシンボリックリンクです。 shまたはdash
  • @AlexejMaguraとして呼び出されると、動作が異なります。

(レスキューディスクなどで一般的)、ほとんどすべて(cp、mv、rm、ls、…)はbusyboxへのシンボリックリンクです。

  • ‘これは本当に無視するのが難しいと思っているので、’ llつまり、おそらく” GNU “プログラム(gccbashgunzip、残りのOSのほとんど…)、Linuxは単なるカーネルです。
  • @ wizzwizz4 “一般的なUnix / Linuxプログラム”の’の何が問題になっていますか? ” Unix / Linuxで実行される一般的なプログラム”のように読みました。 ‘は、特定のGNUプログラムへの制限よりもはるかに優れています。デニス・リッチーは確かにGNUプログラムを使用していませんでした。ところで、Hurdカーネルは主な機能を持たないGNUプログラムの例です…
  • 回答

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

    たとえば、次のようになります(execlを使用):

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

    /var/tmp/mybackdoorは実行されるものですが、argv[0]topに設定されており、これがpsまたは(実際の)topが表示されます。詳細については、U & LSEのこの回答を参照してください。

    すべての設定これはさておき、/procのような派手なファイルシステムが登場する前は、argv[0]がプロセスが自分の名前を知る唯一の方法でした。

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

    コメント

    • このようなプログラムの例はbunzip2bzcatbzip2。最初の2つは3番目のシンボリックリンクです。
    • @Ruslan興味深いことにzcatはシンボリックリンクではありません。代わりにシェルスクリプトを使用してこの手法の欠点を回避しているようですが、完全な出力は、gzipにオプションを追加した誰かがメインに忘れたためですzcatも含む。
    • 私が覚えている限り、GNUコーディング標準では、プログラムの動作を変更するためにargv [0]を使用することを推奨していません(セクション”インターフェースの標準一般”現在のバージョン)。 gunzipは歴史的な例外です。
    • busyboxはもう1つの優れた例です。さまざまなコマンドを呼び出すために、308の異なる名前で呼び出すことができます: busybox.net/downloads/BusyBox.html#commands
    • 多く、多くより多くのプログラムは、名前をハードコーディングする代わりに、usage / help出力にargv[0]を挿入します。完全なものもあれば、ベース名だけのものもあります。

    回答

    たっぷり:

    • argv[0]shの場合、Bashは POSIXモードで実行されます。 argv[0]-で始まる場合、ログインシェルとして実行されます。
    • viviewevimeviewexvimdiffなど
    • すでに述べたように、ビジーボックス。
    • systemdがinitであるシステムでは、shutdownrebootなどは systemctl へのシンボリックリンク。
    • など。

    コメント

    • もう1つはsendmailmailです。すべてのUNIXMTAには、これら2つのコマンドのシンボリックリンクが付属しており、元の’の動作をエミュレートするように設計されています。つまり、メールを送信する必要のあるUNIXプログラムはすべて正確にその方法。
    • 他の一般的なケース:testおよび[:前者に電話する場合、最後の引数が]の場合はエラーを処理します。 (実際のDebian安定版では、これらのコマンドは2つの異なるプログラムですが、以前のバージョンとMacOは引き続き同じプログラムを使用します)。そして、texlatexなど:バイナリは同じですが、呼び出された方法を見て、適切なを選択します。構成ファイル。 initも同様です。
    • 関連して、[は、最後の引数が not ]
    • これは2番目の質問に答えると思いますが、最初の質問には答えません。一部のOS設計者が座って、»と言ったのではないかと疑っています。同じプログラムで、実行可能ファイルの名前だけに基づいて異なることを実行できたら、すばらしいと思います。 ‘名前を引数配列に含めると思います。«
    • @Joeyはい、言葉遣いはそれを伝えることを目的としています(Q:”何かありますか…?” A:”たっぷり:… “)

    回答

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

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

    編集

    :要求に応じたUnix第1版のリファレンス

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

    コメント

    • その参照を追加してくださいこれをバックアップします。
    • 簡単なスキミングから、execは、実行するコマンドの名前と、ゼロで終了するcharポインターの配列を取得します( minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s 、ここでexecラベル2とラベル1を参照し、ラベル2:etc/init\0が表示され、ラベル1:は、ラベル2への参照と、終了ゼロ)を表示します。これは、基本的に、execveからenvpを引いたものです。
    • execvexeclは”永遠に存在します”(つまり、1970年代初頭から中期以降)— execvはシステムコールであり、は、それを呼び出すライブラリ関数でした。 execveは’存在しませんでした。これは、環境が’存在しなかったためです。家族の他のメンバーは後で追加されました。
    • @ G-Manリンクしたv1ソースの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 

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

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

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

    理由

    1. POSIX規則であるため( man 3p execve):

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

    1. C標準(少なくともC99およびC11):

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

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

    コメント

    • ‘に到達した場合、このブレークは発生しません。別のsymlinkからのsymlink?
    • @Mehrdad、はい、’は欠点であり、ユーザーを混乱させる可能性があります。
    • @rudimeier: ‘理由’アイテムは実際には理由ではなく、’は単なる” homunculus “、つまり、なぜ標準でこれが当てはまるのかという疑問を投げかけるだけです。
    • @ einpoklum OP ‘の質問は次のとおりです。なぜプログラム名が実行可能ファイルに渡されるのですか?私は答えました:POSIXとC標準はそうするように私たちに指示しているからです。 ‘が本当に理由ではないとどう思いますか?私が引用した’のドキュメントが存在しない場合、おそらく多くのプログラムがプログラム名を渡さないでしょう。
    • OPは事実上” POSIXおよび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. 

    コメント

    • +1。同じことを提案するつもりでした。非常に多くの人が行動の変化に焦点を合わせており、おそらく最も明白で、はるかに広く使用されています。

    回答

    プログラムの入力を実行します:program_name0 arg1 arg2 arg3 ...

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

    これは(最初は)便利なトリックだったと思います。他の回答にあるように、これも非常に便利だったので、この伝統は継続されました。 et asAPI。

    回答

    基本的に、argvにはプログラム名が含まれているため、prgm: file: No such file or directory、次のように実装されます:

     fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[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に到達しました。

    回答

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

    コメント

    • その’は特に良い説明ではありません-‘私たちができなかった理由はありません’たとえば、(char *path_to_program, char **argv, int argc)のようなもので標準化されていません
    • Afaik、ほとんどのプログラムは標準の場所(~/.<program>/etc/<program $XDG_CONFIG_HOME )、パラメータを使用して変更するか、定数をバイナリにベイクするコンパイル時オプションを使用します。

    回答

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

    コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です