「argv [0] =実行可能ファイルの名前」は、受け入れられた標準ですか、それとも一般的な規約ですか?


102

main()CまたはC ++アプリケーションで引数を渡す場合、argv[0]常に実行可能ファイルの名前になりますか?それともこれは単なる一般的な慣習であり、100%真実であるとは限りませんか?


19
Unixでは、以下を考慮してくださいexecl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);。実行可能ファイルの名前は、の値とは関係ありませんargv[0]
ジョナサンレフラー

回答:


118

推測作業(教育を受けた推測作業でも)は楽しいですが、確実に標準のドキュメントにアクセスする必要があります。たとえば、ISO C11は次のように述べています(私の強調):

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

そのため、いいえ、その名前が利用可能な場合、それはプログラム名だけです。そして、それはプログラム名を「表す」ものであり、必ずしもプログラム名であるとは限りません。その前のセクションは次のように述べています:

の値がargcゼロより大きい場合、配列のメンバーargv[0]argv[argc-1]包括的に、文字列へのポインターが含まれます。これには、プログラムの起動前にホスト環境によって実装定義の値が与えられます。

これは、以前の標準であるC99から変更されておらず、されておらずによっても標準によって規定されていない。完全に実装次第です。

ホスト環境があれば、プログラム名を空にすることができることをこれ意味しないホスト環境があればそれを提供し、他に何もありません「何か」が何らかの形でプログラム名を表していることを提供し、それを提供します。私のよりサディスティックな瞬間には、それをスワヒリ語に変換し、置換暗号を実行してから、逆のバイト順で格納することを検討します:-)。

ただし、実装定義 ISO標準で特定の意味を持ってます-実装はそれがどのように機能するかを文書化する必要があります。したがって、呼び出しargv[0]execファミリで好きなものを入れることができるUNIXでさえ、それを文書化する必要があります(実際に文書化します)。


3
それは標準かもしれませんが、unixは単にそれを強制しないので、あなたはそれに頼ることができません。
dmckee ---元モデレーターの子猫2010

4
質問は、UNIXについては言及しなかったすべてで。これは単純で単純なCの質問だったので、ISO Cは参考文献です。プログラム名は標準で定義された実装なので、実際の名前ではない何かを許可するなど、実装は自由に実行できます。最後から2番目の文でそれを明確にしたと思います。
paxdiablo

2
パックス、私はあなたに反対票を投じませんでした、そしてこの回答が得ることができるのと同じくらい権威があるので、投票した人たちを承認しません。しかし、私はの値の信頼性の欠如はargv[0]、現実の世界でのプログラミングには適切だと思います。
dmckee ---元モデレーターの子猫2010

4
@caf、それは正しいです。プログラムのフルパス( '/ progpath / prog')、ファイル名( 'prog')、わずかに変更された名前( '-prog')、わかりやすい名前( ' prog-進行のためのプログラム ')と何もない(' ')。実装はそれが何を保持するかを定義する必要がありますが、それは標準が要求するすべてです。
paxdiablo

3
みんな、ありがとう!(一見)単純な質問からの素晴らしい議論。Richardの回答は* nixオペレーティングシステムに対して有効ですが、特定のOSの動作にはあまり興味がなく、主に受け入れられた標準の存在(または不在)に関心があるため、paxdiabloの回答を選びました。(もし興味があれば:元の質問のコンテキストで-私はオペレーティングシステムを持っていません。組み込みデバイスにロードされた実行可能ファイルの生のargc / argvバッファーを構築するコードを書いていて、何をすべきかを知る必要がありました。 argv [0]を使用)。StackOverflowを+1して素晴らしいものに!
Mike Willekes、2013年

48

以下の下*nixでのタイプのシステムexec*()コール、argv[0]だろうに何でも発信者プットargv0のスポットexec*()コール。

シェルはこれがプログラム名であるという規則を使用し、他のほとんどのプログラムは同じ規則に従います。 argv[0]、通常はプログラム名です。

しかし、悪意のあるUnixプログラムは好きなものを呼び出しexec()て作成できるargv[0]ので、C標準が何と言っても、この100%の時間を当てにすることはできません。


4
これは、上記のpaxdiabloよりも良い答えです。規格ではこれを「プログラム名」と呼んでいますが、私の知る限り、これは強制されていません。Unixカーネルは、execve()に渡された文字列を変更せずに子プロセスに渡します。
アンディロス

4
C標準は、「execve()」などを認識していないため、言うことができる範囲が制限されています。POSIX標準(opengroup.org/onlinepubs/9699919799/functions/execve.html)は、言うべきことは多くあります-明確にすることargv [0]の内容は、「execve()」(または関連する)システムコールを実行するプロセスの気まぐれにあること
ジョナサンレフラー、2010年

1
@Andy、あなたは自由にあなたの意見を持つことができます:-)しかし、あなたは強制について間違っています。実装が標準に従っていない場合、それは不適合です。そして実際に、それの実装定義「プログラム名が」あるものにのようにあるため、UNIXなどのOSをされている限り、それは名前が何であるかを指定するよう適合する。これには、execファミリーの呼び出しで必要なものをargv [0]にロードすることにより、プログラム名を露骨に偽造できることが含まれます。
paxdiablo

これが、標準でargv [0](「プログラム名を表す」)およびargv [1..N](「プログラムの引数を表す」)を指す場合の「表す」という単語の美しさです。「unladen swallow」は有効なプログラム名です。
Richard Pennington

8

C ++標準に従って、セクション3.6.1:

argv [0]は、プログラムの呼び出しに使用された名前または ""を表すNTMBSの最初の文字へのポインターです

ですから、少なくとも標準では保証されていません。


5
nullで終了するマルチバイト文字列だと思いますか?
paxdiablo

5

ISO-IEC 9899は次のように述べています。

5.1.2.2.1プログラムの起動

の値がargcゼロより大きい場合、が指す文字列argv[0]はプログラム名を表します。argv[0][0]ホスト環境からプログラム名を取得できない場合は、ヌル文字になります。の値がargc1より大きい場合、argv[1]throughが指す文字列argv[argc-1]プログラムパラメータを表します

私も使用しました:

#if defined(_WIN32)
  static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
  {
    return GetModuleFileNameA(NULL, pathName, (DWORD)pathNameCapacity);
  }
#elif defined(__linux__) /* elif of: #if defined(_WIN32) */
  #include <unistd.h>
  static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
  {
    size_t pathNameSize = readlink("/proc/self/exe", pathName, pathNameCapacity - 1);
    pathName[pathNameSize] = '\0';
    return pathNameSize;
  }
#elif defined(__APPLE__) /* elif of: #elif defined(__linux__) */
  #include <mach-o/dyld.h>
  static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
  {
    uint32_t pathNameSize = 0;

    _NSGetExecutablePath(NULL, &pathNameSize);

    if (pathNameSize > pathNameCapacity)
      pathNameSize = pathNameCapacity;

    if (!_NSGetExecutablePath(pathName, &pathNameSize))
    {
      char real[PATH_MAX];

      if (realpath(pathName, real) != NULL)
      {
        pathNameSize = strlen(real);
        strncpy(pathName, real, pathNameSize);
      }

      return pathNameSize;
    }

    return 0;
  }
#else /* else of: #elif defined(__APPLE__) */
  #error provide your own implementation
#endif /* end of: #if defined(_WIN32) */

次に、文字列を解析して、パスから実行可能ファイル名を抽出する必要があります。


2
/proc/self/path/a.outシンボリックリンクは、Solaris 10とアップに使用可能であってもよいです。
ephemient

コードに賛成(理想的または正しいと言っているわけではありません。たとえば、WindowsではGetModuleFileNameW、任意のパスを取得できるようにするために使用する必要がありますが、コードの存在のみが適切なガイダンスを構成します)。
乾杯とhth。-アルフ

4

このページは述べています:

要素argv [0]には通常、プログラムの名前が含まれていますが、これに依存しないでください-とにかく、プログラムが自分の名前を知らないのは珍しいことです!

ただし、他のページは、それが常に実行可能ファイルの名前であるという事実を裏付けているようです。これは述べています:

argv [0]はプログラム自体のパスと名前です。これにより、プログラムはそれ自体に関する情報を発見できます。また、プログラムの引数の配列にもう1つ追加するため、コマンドライン引数をフェッチするときの一般的なエラーは、argv [1]が必要なときにargv [0]を取得することです。


11
一部のプログラムは、起動に使用された名前がわからないという事実を利用しています。BusyBox(busybox.net/about.html)はこのように動作すると思います。多くの異なるコマンドラインユーティリティを実装する実行可能ファイルは1つだけです。一連のシンボリックリンクとargv [0]を使用して、実行するコマンドラインツールを決定します
Trent

ええ、私は「gunzip」が「gzip」へのシンボリックリンクであったことに気づいて、それがどのように機能したのかしばらく疑問に思いました。
David Thornley、2010年

2
多くのプログラムはargv [0]を調べて情報を得ます。たとえば、名前の最後のコンポーネントがダッシュ( '/ bin / -sh'など)で始まっている場合、シェルはログインシェルの場合と同様にプロファイルやその他のものを実行します。
Jonathan Leffler、2010年

2
@ジョン:私はログインシェルが始まったと思ったargv[0]="-/bin/sh"?とにかく、私が使用したすべてのマシンに当てはまります。
ephemient

3

argv[0] !=実行可能名を持つアプリケーション

  • 多くのシェルは、をチェックすることで、それらがログインシェルかどうかを判断しますargv[0][0] == '-'。ログインシェルにはさまざまなプロパティがあります。特に、次のようないくつかのデフォルトファイルをソースとしています。/etc/profile

    通常は、それ自体がinitであるかgetty、先頭-にが追加されます。https//unix.stackexchange.com/questions/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152#300152

  • マルチコールバイナリ、おそらく最も顕著なのはBusyboxです。これらのシンボリックリンクは複数の名前を含み、たとえば/bin/sh、使用するツールを認識する/bin/ls単一の実行可能ファイル/bin/busyboxにシンボリックリンクしますargv[0]

    これにより、複数のツールを表す静的にリンクされた単一の実行ファイルを作成することが可能になり、基本的にどのLinux環境でも機能します。

参照:https : //unix.stackexchange.com/questions/315812/why-does-argv-include-the-program-name/315817

実行可能な名前の実行可能なPOSIXのexecveargv[0] !=

その他の言及 execしましたが、ここでは実行可能な例です。

交流

#define _XOPEN_SOURCE 700
#include <unistd.h>

int main(void) {
    char *argv[] = {"yada yada", NULL};
    char *envp[] = {NULL};
    execve("b.out", argv, envp);
}

紀元前

#include <stdio.h>

int main(int argc, char **argv) {
    puts(argv[0]);
}

次に:

gcc a.c -o a.out
gcc b.c -o b.out
./a.out

与える:

yada yada

はい、またargv[0]可能性があります:

Ubuntu 16.10でテスト済み。


2

それがほぼ普遍的な慣習であるか標準であるかはわかりませんが、どちらの方法でも遵守する必要があります。ただし、UnixおよびUnixライクなシステム以外で悪用されることはありません。Unix環境では(おそらくおそらく昔は)、プログラムは呼び出された名前によって動作が大幅に異なる可能性があります。

編集済み:他の投稿と同時に、誰かが特定の規格に由来するものであると特定したことを確認しましたが、この規約はその規格よりもずっと前から存在していると思います。


1
人々が私の返答を「マークダウン」するつもりなら、彼らは彼らがそれについて好きではないことをある程度示すだろうと確信しています。
Joe Mabel

0

ワークベンチでAmigaプログラムを開始した場合、argv [0]は設定されず、CLIによってのみ設定されます。

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