Warum enthält argv den Programmnamen?


106

Typische Unix / Linux-Programme akzeptieren die Kommandozeilen-Eingaben als Argument count ( int argc) und als Argument vector ( char *argv[]). Das erste Element von argvist der Programmname, gefolgt von den eigentlichen 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 in einer bestimmten execSituation)?


6
wie mv und cp?
Archemar

9
Auf Debian shist symlink zu dash. Sie verhalten sich anders, wenn sie aufgerufen wird, wie shoder alsdash
Motte001

21
@AlexejMagura Wenn Sie so etwas wie busybox(üblich bei Rettungsdiscs und dergleichen) verwenden, dann ist so ziemlich alles (cp, mv, rm, ls, ...) ein symbolischer Link zu busybox.
Baard Kopperud

11
Ich finde das wirklich schwer zu ignorieren, also werde ich es sagen: Sie wahrscheinlich bedeuten „GNU“ Programme ( gcc, bash, gunzip, die meisten der Rest des OS ...), da Linux nur der Kernel ist.
wizzwizz4

10
@ wizzwizz4 Was stimmt nicht mit "Typischen Unix / Linux-Programmen"? Ich las es wie "Typische Programme unter Unix / Linux". 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 ...
rudimeier

Antworten:


122

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

Angenommen, zum Beispiel execl:

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

/var/tmp/mybackdoorwird ausgeführt, argv[0]ist aber auf gesetzt top, und dies wird angezeigt psoder (der echte) top. Weitere Informationen finden Sie in dieser Antwort auf U & L SE.

Das alles beiseite legen: Vor dem Aufkommen von ausgefallenen Dateisystemen wie /proc, argv[0]war der einzige Weg für einen Prozess, etwas über seinen eigenen Namen zu lernen. Wofür wäre das gut?

  • Einige Programme passen ihr Verhalten an den Namen an, unter dem sie aufgerufen wurden (normalerweise über symbolische oder feste Verknüpfungen, z. B. die Dienstprogramme von BusyBox ; weitere Beispiele finden Sie in anderen Antworten auf diese Frage).
  • Darüber hinaus stellen Dienste, Daemons und andere Programme, die sich über syslog anmelden, ihren Namen häufig den Protokolleinträgen voran. Ohne dies wäre die Ereignisverfolgung so gut wie unmöglich.

18
Beispiele für solche Programme sind bunzip2, bzcatund bzip2, für die ersten zwei symbolische Links zu dem dritten sind.
Ruslan

5
@ Ruslan Interessanterweise zcatist kein Symlink. Sie scheinen die Nachteile dieser Technik zu vermeiden, indem sie stattdessen ein Shell-Skript verwenden. Sie können jedoch keine vollständige --helpAusgabe drucken, da jemand, der gzip-Optionen hinzugefügt hat, vergessen hat, auch zcat zu verwalten.
Rudimeier

1
Solange ich mich erinnern kann, haben die GNU-Codierungsstandards die Verwendung von argv [0] zur Änderung des Programmverhaltens unterbunden ( Abschnitt "Standards für Schnittstellen allgemein" in der aktuellen Version ). gunzipist eine historische Ausnahme.

19
Die Busybox ist ein weiteres hervorragendes Beispiel. Es kann mit 308 verschiedenen Namen aufgerufen werden, um verschiedene Befehle aufzurufen: busybox.net/downloads/BusyBox.html#commands
Pepijn Schmitz

2
Viele, viele weitere Programme fügen ihre argv[0]Nutzungs- / Hilfeausgabe hinzu, anstatt ihren Namen fest zu codieren. Einige sind vollständig, andere nur der Basisname.
Spektren

62

Reichlich:

  • Bash läuft im POSIX - Modus , wenn argv[0]ist sh. Es wird als Anmeldeshell ausgeführt, wenn argv[0]mit begonnen wird -.
  • Vim verhält sich anders , wenn laufen , wie vi, view, evim, eview, ex, vimdiff, usw.
  • Busybox, wie schon erwähnt.
  • In Systemen mit systemd als init, shutdown, rebootusw. sind Symlinkssystemctl .
  • und so weiter.

7
Ein anderer ist sendmailund mail. Jeder einzelne Unix-MTA wird mit einem Symlink für diese beiden Befehle ausgeliefert und soll das Verhalten des Originals nachahmen, wenn es als solches aufgerufen wird. Dies bedeutet, dass jedes Unix-Programm, das E-Mails senden muss, genau weiß, wie es dies tun kann.
Shadur

4
Ein weiterer häufiger Fall: testund [: Wenn Sie den ersteren aufrufen, wird ein Fehler behandelt, wenn das letzte Argument lautet ]. (Auf Debian Stable sind diese Befehle zwei verschiedene Programme, aber frühere Versionen und MacOs verwenden immer noch dasselbe Programm). Und tex, latexund so weiter: die binären gleich ist, aber auf der Suche , wie es genannt wurde, wählen sie die richtige Konfiguration Datei. initist ähnlich.
Giacomo Catenazzi

4
Verwandte, [betrachtet es als Fehler, wenn das letzte Argument nicht ist ] .
Chepner

Ich denke, das 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 dasselbe Programm verschiedene Dinge nur basierend auf seinem ausführbaren Namen ausführt. Ich nehme an, ich werde den Namen dann in das Argument-Array aufnehmen. «
Joey,

@Joey Ja, die Formulierung soll vermitteln, dass (F: "Gibt es ...?" A: "Plenty: ...")
muru

34

Historisch gesehen argvist es nur ein Array von Zeigern auf die "Wörter" der Kommandozeile, daher ist es sinnvoll, mit dem ersten "Wort" zu beginnen, das zufällig der Name des Programms ist.

Und es gibt eine ganze Reihe von Programmen, die sich je nach dem Namen, unter dem sie aufgerufen werden, unterschiedlich verhalten. Sie können also einfach verschiedene Links zu ihnen erstellen und verschiedene "Befehle" abrufen. Das extremste Beispiel, an das ich denken kann, ist die Busybox , die sich je nach Aufruf wie ein Dutzend verschiedener "Befehle" verhält .

Bearbeiten : Referenzen für Unix 1. Ausgabe, wie angefordert

Man kann beispielsweise von der siehe Hauptfunktion dass und wurden bereits verwendet. Die Shell - Kopien Argumente an die im Inneren der Teil der Schlaufe, während die sich als die Argumente in der gleichen Art und Weise der Behandlung von Befehl. (Später führt es natürlich nur das erste Argument aus, das der Name des Befehls ist.) Anscheinend gab es damals keine Verwandten.ccargcargvparbufnewargexecv


1
Bitte fügen Sie Referenzen hinzu, die dies unterstützen.
Lesmana

Nimmt nach einem schnellen Überfliegen execden Namen des auszuführenden Befehls und ein nullterminiertes Array von Zeichenzeigern (am besten zu sehen unter minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s , wo execnimmt Verweise auf Label 2 und Label 1 und auf Label 2:erscheinen etc/init\0, und auf Label 1:erscheint ein Verweis auf Label 2 und eine abschließende Null, was im Grunde das ist, was execveheute minus bedeutet envp.
Ninjalj

1
execvund execlexistierten "für immer" (dh seit Anfang bis Mitte der 1970er Jahre) - execvwar ein Systemaufruf und execlwar eine Bibliotheksfunktion, die ihn aufrief.   execvegab es damals nicht, weil es die umgebung damals nicht gab. Die anderen Familienmitglieder wurden später hinzugefügt.
G-Man

@ G-Man Kannst du mich auf execvdie v1-Quelle verweisen, auf die ich verlinkt habe? Nur neugierig.
Dirkt

22

Anwendungsfälle:

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

Beispielsweise könnten Sie einige Symlinks zur eigentlichen Binärdatei erstellen.

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

Dies wird auch in setarchutil-linux verwendet:

$ 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 Wesentlichen , um viele doppelte Quelldateien zu vermeiden oder um die Quellen besser lesbar zu halten.

Ein weiterer Anwendungsfall wäre ein Programm, das zur Laufzeit einige Module oder Daten laden muss. Mit dem Programmpfad können Sie Module von einem Pfad relativ zum Programmspeicherort laden .

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

Warum :

  1. Weil es eine POSIX-Konvention ist ( man 3p execve):

argv ist ein Array von Argumenten, die an das neue Programm übergeben werden. Gemäß der Konvention 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, repräsentiert der String, auf den argv [0] zeigt, den Programmnamen. argv [0] [0] muss das Nullzeichen sein, wenn der Programmname in der Hostumgebung nicht verfügbar ist.

Beachten Sie, dass der C-Standard "Programmname" und nicht "Dateiname" sagt.


3
Bricht dies nicht, wenn Sie den Symlink von einem anderen Symlink aus erreichen?
Mehrdad

3
@Mehrdad, ja das ist der Nachteil und kann für den Benutzer verwirrend sein.
Rudimeier

@rudimeier: Ihre "Warum" -Elemente sind keine wirklichen Gründe, sie sind nur ein "Homunkulus", dh es stellt sich nur die Frage, warum der Standard dies erfordert.
Einpoklum

Die Frage von @einpoklum OP lautete: Warum wird der Programmname an die ausführbare Datei übergeben? Ich antwortete: Weil POSIX und C uns dies vorschreiben. Wie denkst du, ist das nicht wirklich ein Grund ? Wenn die von mir zitierten Dokumente nicht existieren würden, würden wahrscheinlich viele Programme den Programmnamen nicht weitergeben.
Rudimeier

Das OP fragt effektiv: "WARUM schreiben die POSIX- und C-Standards dies vor?" Zugegeben, der Wortlaut war auf einer abstrakten Ebene, aber es scheint klar zu sein. Realistisch gesehen ist der einzige Weg zu wissen, die Urheber zu fragen.
user2338816

21

Zusätzlich zu Programmen, die ihr Verhalten je nach Aufruf ändern, kann ich argv[0]die Verwendung eines Programms folgendermaßen drucken:

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

Dies bewirkt, dass die Verwendungsnachricht immer den Namen verwendet, unter dem sie aufgerufen wurde. Wenn das Programm umbenannt wird, ändert sich seine Verwendungsmeldung 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, besonders für kleine Spezialwerkzeuge / Skripte, die überall zu finden sind.

Dies scheint auch in GNU-Tools üblich zu sein, siehe lszum 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.

3
+1. Ich würde dasselbe vorschlagen. Seltsamerweise konzentrieren sich so viele Menschen auf das Ändern von Verhaltensweisen und erwähnen nicht die offensichtlichste und am weitesten verbreitete Verwendung.
The Vee

5

Man führt das Programm die Eingabe: program_name0 arg1 arg2 arg3 ....

Die Shell sollte das Token also bereits teilen, und das erste Token ist bereits der Programmname. Übrigens gibt es auf der Programmseite und auf der Shell die gleichen 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 und als API festgelegt wurde.


4

Grundsätzlich enthält argv den Programmnamen, so dass Sie Fehlermeldungen wie prgm: file: No such file or directorydie folgenden schreiben können :

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

2

Ein weiteres Anwendungsbeispiel hierfür ist dieses Programm, das sich durch ... sich selbst ersetzt, bis Sie etwas eingeben, das nicht mehr vorhanden ist 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;
}

Offensichtlich ein ausgeklügeltes, wenn auch interessantes Beispiel, aber ich denke, dies könnte realen Nutzen haben - zum Beispiel eine sich selbst aktualisierende Binärdatei, die ihren eigenen Speicherplatz mit einer neuen Version von sich selbst überschreibt, die sie heruntergeladen oder geändert hat.

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 ein paar mehr Infos .


Herzlichen Glückwunsch zum Erreichen von 1000.
G-Man

0

Der Pfad zum Programm lautet argv[0], damit das Programm Konfigurationsdateien usw. aus seinem Installationsverzeichnis abrufen kann.
Ohne wäre das unmöglich argv[0].


2
Das ist keine besonders gute Erklärung - es gibt keinen Grund , warum wir nicht auf so etwas wie standardisiert könnten (char *path_to_program, char **argv, int argc)zum Beispiel
moopet

AFAIK, die meisten Programme ziehen Konfiguration aus einer Standardposition ( ~/.<program>, /etc/<program, $XDG_CONFIG_HOME) und entweder einen Parameter nehmen , sie zu ändern oder eine Compiler-Option, die die binären in einem konstanten backt.
Xiong Chiamiov

0

ccache verhält sich so, um verschiedene Aufrufe von Compiler-Binärdateien nachzuahmen. ccache ist ein Kompilierungscache - der springende Punkt ist, niemals denselben Quellcode zweimal zu kompilieren, sondern den Objektcode, wenn möglich, aus dem Cache zurückzugeben.

Auf der Manpage " ccache " gibt es 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) zu ccache erstellen. Die erste Methode Dies ist am praktischsten, wenn Sie Ccache nur ausprobieren oder für bestimmte Projekte verwenden möchten. Die zweite Methode ist am nützlichsten, wenn Sie Ccache für alle Ihre Kompilierungen verwenden möchten. "

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

... die Folge ist, dass ccache alle Befehle abfängt, die ansonsten an die Compiler gegangen wären. Auf diese Weise kann ccache eine zwischengespeicherte Datei zurückgeben oder den Befehl an den eigentlichen Compiler weiterleiten.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.