Warum enthält argv den Programmnamen?

Typische Unix / Linux-Programme akzeptieren die Befehlszeileneingaben als Argumentanzahl (int argc) und als Argumentvektor (char *argv[]). Das erste Element von argv ist der Programmname – gefolgt von den tatsächlichen Argumenten.

Warum wird der Programmname als Argument an die ausführbare Datei übergeben? Gibt es Beispiele für Programme, die ihren eigenen Namen verwenden (möglicherweise eine exec Situation)?

Kommentare

  • wie mv und cp?
  • Auf Debian sh ist ein Symlink zu dash. Sie verhalten sich anders, wenn sie als sh oder als dash
  • @AlexejMagura aufgerufen werden, wenn Sie so etwas wie busybox (häufig bei Rettungsdiscs und dergleichen), dann ist so ziemlich alles (cp, mv, rm, ls, …) eine symbolische Verbindung zur Busybox.
  • Ich ‚ finde das wirklich schwer zu ignorieren, also ‚ ll Sagen Sie es: Sie meinen wahrscheinlich “ GNU “ Programme (gcc, bash, gunzip, der größte Teil des restlichen Betriebssystems …), da Linux nur der Kernel ist.
  • @ wizzwizz4 Was ‚ ist falsch an “ Typische Unix / Linux-Programme „? Ich habe es wie folgt gelesen: “ Typische Programme, die unter Unix / Linux ausgeführt werden „. Das ‚ ist viel besser als Ihre Beschränkung auf bestimmte GNU-Programme. Dennis Ritchie verwendete sicherlich keine GNU-Programme. Übrigens ist der Hurd-Kernel ein Beispiel für ein GNU-Programm, das keine Hauptfunktion hat …

Antwort

Beachten Sie zunächst, dass argv[0] nicht unbedingt der Programmname ist. Dies ist, was der Aufrufer in argv[0] des execve Systemaufrufs einfügt (siehe z. B. diese Frage zum Stapelüberlauf ). (Alle anderen Varianten von exec sind keine Systemaufrufe, sondern Schnittstellen zu execve.)

Nehmen wir zum Beispiel an Folgendes (unter Verwendung von execl):

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

/var/tmp/mybackdoor ist Was ausgeführt wird, aber argv[0] wird auf top gesetzt, und dies ist, was ps oder ( Die reale top wird angezeigt. Weitere Informationen hierzu finden Sie unter dieser Antwort in U & L SE.

Festlegen aller Abgesehen davon: Vor dem Aufkommen ausgefallener Dateisysteme wie /proc war argv[0] die einzige Möglichkeit für einen Prozess, seinen eigenen Namen kennenzulernen. Wofür wäre das gut?

  • Mehrere Programme passen ihr Verhalten an den Namen an, unter dem sie aufgerufen wurden (normalerweise über symbolische oder feste Links, z. B. Dienstprogramme von BusyBox ; mehrere weitere Beispiele finden Sie in anderen Antworten auf diese Frage.
  • Darüber hinaus stellen Dienste, Dämonen und andere Programme, die sich über syslog anmelden, häufig ihren Namen vor Protokolleinträge, ohne diese wäre die Ereignisverfolgung nahezu unmöglich.

Kommentare

  • Beispiele für solche Programme sind bunzip2, bzcat und bzip2, für die die ersten beiden Symlinks zum dritten sind.
  • @Ruslan Interessanterweise ist zcat kein Symlink. Sie scheinen die Nachteile dieser Technik zu vermeiden, indem sie stattdessen ein Shell-Skript verwenden. Sie drucken jedoch kein vollständiges --help Ausgabe, weil jemand, der Optionen zu gzip hinzugefügt hat, vergessen hat, main zu verwenden tain zcat auch.
  • Seit ich denken kann, haben die GNU-Codierungsstandards von der Verwendung von argv [0] zur Änderung des Programmverhaltens abgeraten (Abschnitt “ Standards für Schnittstellen Im Allgemeinen “ in der aktuellen Version ). gunzip ist eine historische Ausnahme.
  • Busybox ist ein weiteres hervorragendes Beispiel. Es kann unter 308 verschiedenen Namen aufgerufen werden, um verschiedene Befehle aufzurufen: busybox.net/downloads/BusyBox.html#commands
  • Viele, viele Weitere Programme fügen auch ihre argv[0] in ihre Verwendungs- / Hilfeausgabe ein, anstatt ihren Namen fest zu codieren. Einige vollständig, andere nur der Basisname.

Antwort

Viel:

  • Bash wird im POSIX-Modus ausgeführt, wenn argv[0] sh ist. Es wird als Anmeldeshell ausgeführt, wenn argv[0] mit - beginnt.
  • Vim verhält sich anders, wenn es ausgeführt wird als vi, view, evim, eview, ex, vimdiff usw.
  • Busybox, wie bereits erwähnt.
  • In Systemen mit systemd als init sind shutdown, reboot usw. Symlinks zu systemctl .
  • und so weiter.

Kommentare

  • Eine andere ist sendmail und mail. Jeder einzelne Unix-MTA wird mit einem Symlink für diese beiden Befehle geliefert und emuliert das Verhalten des ursprünglichen ‚, wenn es als solches aufgerufen wird. Dies bedeutet, dass jedes Unix-Programm, das E-Mails senden muss, dies weiß genau, wie sie dies tun können.
  • ein anderer häufiger Fall: test und [: wenn Sie den ersteren aufrufen wird ein Fehler behandelt, wenn das letzte Argument ] ist. (Auf Debian Stable sind diese Befehle zwei verschiedene Programme, aber frühere Versionen und MacOs verwenden immer noch dasselbe Programm). Und tex, latex und so weiter: Die Binärdatei ist dieselbe, aber wenn man sieht, wie sie aufgerufen wurde, wählt sie die richtige Konfigurationsdatei . init ist ähnlich.
  • [ betrachtet es als Fehler, wenn das letzte Argument nicht ].
  • Ich denke, dies beantwortet die zweite Frage, aber nicht die erste. Ich bezweifle sehr, dass sich ein OS-Designer hinsetzte und sagte: » Hey, es wäre cool, wenn ich dasselbe Programm hätte, das verschiedene Dinge nur basierend auf seinem ausführbaren Namen macht. Ich denke, ich ‚ werde dann den Namen in sein Argumentarray aufnehmen. «
  • @Joey Ja, das Der Wortlaut soll Folgendes vermitteln: (F: “ Gibt es …? “ A: “ Viele: … „)

Antwort

Historisch gesehen ist argv nur ein Array von Zeigern auf die „Wörter“ der Befehlszeile. Es ist daher sinnvoll, mit dem ersten „Wort“ zu beginnen, das zufällig das ist Name des Programms.

Und es gibt einige Programme, die sich unterschiedlich verhalten, je nachdem, welcher Name zum Aufrufen verwendet wird. Sie können also einfach verschiedene Links zu ihnen erstellen und verschiedene „Befehle“ abrufen Das extremste Beispiel, das ich mir vorstellen kann, ist Busybox , wobei wie mehrere Dutzend verschiedene „Befehle“ wirkt, je nachdem, wie es heißt .

Bearbeiten

: Referenzen für Unix 1st Edition, wie angefordert

Man kann z. von der Hauptfunktion von cc, dass argc und argv wurden bereits verwendet. Die -Shell kopiert Argumente in die parbuf im newarg -Teil von die Schleife, während der Befehl selbst genauso behandelt wird wie die Argumente. (Natürlich führt es später nur das erste Argument aus, nämlich den Namen des Befehls). Es sieht so aus, als ob execv und Verwandte damals nicht existierten.

Kommentare

  • Bitte fügen Sie Referenzen hinzu, die Sichern Sie dies.
  • Nach einem kurzen Überfliegen verwendet exec den Namen des auszuführenden Befehls und ein nullterminiertes Array von Zeichenzeigern (am besten bei minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s , wobei exec Verweise auf Label 2 und Label 1 und bei Label 2: erscheint etc/init\0 und bei Label 1: erscheint als Verweis auf Label 2 und als abschließende Null). Dies ist im Grunde das, was execve heute minus envp tut.
  • execv und execl existieren “ für immer “ (dh seit Anfang bis Mitte der 1970er Jahre) – execv war ein Systemaufruf und war eine Bibliotheksfunktion, die es aufrief. execve existierte damals nicht ‚, weil die Umgebung ‚ damals nicht existierte. Die anderen Familienmitglieder wurden später hinzugefügt.
  • @ G-Man Können Sie mich auf execv in der von mir verlinkten v1-Quelle verweisen? Nur neugierig.

Antwort

Anwendungsfälle:

Sie können den Programmnamen verwenden, um das Programmverhalten zu ändern .

Sie können beispielsweise einige Symlinks zur eigentlichen Binärdatei erstellen.

Ein bekanntes Beispiel für diese Technik ist das Busybox-Projekt, bei dem nur eine einzige Binärdatei und viele Symlinks installiert werden. (ls, cp, mv usw.). Sie tun dies , um Speicherplatz zu sparen , da ihre Ziele kleine eingebettete Geräte sind.

Dies gilt auch verwendet in setarch von 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 

Hier verwenden sie diese Technik im Grunde , um viele doppelte Quelldateien zu vermeiden oder nur um die Quellen besser lesbar zu halten.

Ein weiterer Anwendungsfall wäre ein Programm, das benötigt wird um einige Module oder Daten zur Laufzeit zu laden. Mit dem Programmpfad können Sie Module aus einem Pfad relativ zum Programmspeicherort laden.

Darüber hinaus drucken viele Programme Fehlermeldungen, einschließlich des Programmnamens .

Warum :

  1. Weil es sich um eine POSIX-Konvention handelt ( man 3p execve):

argv ist ein Array von Argumentzeichenfolgen, die an das neue Programm übergeben werden. Konventionell sollte die erste dieser Zeichenfolgen den Dateinamen enthalten, der der ausgeführten Datei zugeordnet ist.

  1. Es ist C. Standard (mindestens C99 und C11):

Wenn der Wert von argc größer als Null ist, zeigt die Zeichenfolge, auf die argv [0 zeigt ] steht für den Programmnamen; argv [0] [0] ist das Nullzeichen, wenn der Programmname in der Hostumgebung nicht verfügbar ist.

Beachten Sie, dass der C-Standard „program“ sagt Name „nicht“ Dateiname „.

Kommentare

  • ‚ t diese Unterbrechung nicht, wenn Sie die erreichen Symlink von einem anderen Symlink?
  • @Mehrdad, Ja, dass ‚ der Nachteil ist und für den Benutzer verwirrend sein kann.
  • @rudimeier: Ihre ‚ Warum ‚ Elemente keine wirklichen Gründe sind, sind ‚ nur ein “ homunculus „, dh es stellt sich nur die Frage, warum der Standard dies erfordert.
  • @ einpoklum OP ‚ stellte sich die Frage: Warum wird der Programmname an die ausführbare Datei übergeben? Ich antwortete: Weil POSIX und C Standard uns dazu auffordern. Wie denken Sie, dass ‚ nicht wirklich ein Grund ist ? Wenn die von mir ‚ zitierten Dokumente nicht existieren würden, würden wahrscheinlich viele Programme den Programmnamen nicht übergeben.
  • Das OP fragt effektiv nach “ WARUM sagen die POSIX- und C-Standards dies? “ Zugegeben, der Wortlaut war auf einer abstrakten Ebene, aber es scheint klar zu sein. Realistisch gesehen besteht die einzige Möglichkeit, dies zu wissen, darin, die Urheber zu fragen.

Antwort

Zusätzlich zu Programmen, die ihre ändern Verhalten je nachdem, wie sie aufgerufen wurden, finde ich argv[0] nützlich beim Drucken der Verwendung eines Programms, wie folgt:

printf("Usage: %s [arguments]\n", argv[0]); 

Dadurch verwendet die Verwendungsnachricht immer den Namen, unter dem sie aufgerufen wurde. Wenn das Programm umbenannt wird, ändert sich seine Verwendungsnachricht mit. Es enthält sogar den Pfadnamen, mit dem es aufgerufen wurde:

# 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] 

Es ist eine nette Geste, insbesondere für kleine Spezialwerkzeuge / -skripte, die möglicherweise überall verwendet werden the place.

Dies scheint auch in GNU-Tools üblich zu sein. Siehe ls zum Beispiel:

% 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. 

Kommentare

  • +1. Ich wollte dasselbe vorschlagen. Seltsam, dass sich so viele Menschen auf Verhaltensänderungen konzentrieren und die wahrscheinlich offensichtlichsten und viel weiter verbreitete Verwendung.

Antwort

Man führt die Programmeingabe aus: program_name0 arg1 arg2 arg3 ....

Die Shell sollte also das Token bereits teilen, und das erste Token ist bereits der Programmname. Übrigens gibt es auf der Programmseite und auf der Shell dieselben Indizes.

Ich denke, dies war nur ein Convenience-Trick (von Anfang an), und wie Sie in anderen Antworten sehen, war er auch sehr praktisch, sodass diese Tradition fortgesetzt wurde und s et as API.

Antwort

Grundsätzlich enthält argv den Programmnamen, damit Sie Fehlermeldungen wie prgm: file: No such file or directory, das mit so etwas implementiert würde:

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

Antwort

Ein weiteres Beispiel für eine Anwendung hierfür ist dieses Programm, das sich durch … selbst ersetzt, bis Sie etwas eingeben, das nicht „t y ist.

#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; } 

Natürlich eine Art erfundenes, wenn auch interessantes Beispiel, aber ich denke, dies kann echte Verwendungszwecke haben – zum Beispiel eine sich selbst aktualisierende Binärdatei, die neu geschrieben wird seinen eigenen Speicherplatz mit einer neuen Version von sich selbst, die heruntergeladen oder geändert wurde.

Beispiel:

$ ./res 1 arg: 1 y arg: 2 y arg: 3 y arg: 4 y arg: 5 y arg: 6 y arg: 7 n 7 | $ 

Quelle und einige weitere Informationen .

Kommentare

  • Herzlichen Glückwunsch zum Erreichen von 1000.

Antwort

Der Pfad zum Programm lautet argv[0], damit das Programm dies kann Konfigurationsdateien usw. aus dem Installationsverzeichnis abrufen.
Dies wäre ohne argv[0] nicht möglich.

Kommentare

  • Das ‚ ist keine besonders gute Erklärung – es gibt ‚ keinen Grund, warum wir nicht ‚ haben nicht auf etwas wie (char *path_to_program, char **argv, int argc) zum Beispiel
  • Afaik, die meisten Programme ziehen die Konfiguration von einem Standardspeicherort (~/.<program>, /etc/<program, $XDG_CONFIG_HOME ) und nehmen Sie entweder einen Parameter, um ihn zu ändern, oder haben Sie eine Option zur Kompilierungszeit, die eine Konstante in die Binärdatei backt.

Antwort

ccache verhält sich zum Nachahmen so verschiedene Aufrufe von Compiler-Binärdateien. ccache ist ein Kompilierungscache – der springende Punkt ist, niemals denselben Quellcode zweimal zu kompilieren, sondern den Objektcode nach Möglichkeit aus dem Cache zurückzugeben.

Von ccache-Manpage : „Es gibt zwei Möglichkeiten, ccache zu verwenden. Sie können Ihren Kompilierungsbefehlen entweder ccache voranstellen oder ccache als Compiler maskieren lassen, indem Sie einen symbolischen Link (als Compiler bezeichnet) zum ccache erstellen Die erste Methode ist am bequemsten, wenn Sie nur den Ccache ausprobieren oder für bestimmte Projekte verwenden möchten. Die zweite Methode ist am nützlichsten, wenn Sie den Ccache für alle Ihre Kompilierungen verwenden möchten. „

Die Bei der Symlinks-Methode werden die folgenden Befehle ausgeführt:

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 ... 

… was dazu führt, dass ccache alle Befehle abfangen kann, die sonst an die Compiler gegangen wären. Auf diese Weise kann ccache eine zwischengespeicherte Datei zurückgeben oder den Befehl an den eigentlichen Compiler weiterleiten.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.