Quando si passa un argomento a main()
in un'applicazione C o C ++, sarà argv[0]
sempre il nome dell'eseguibile? O è solo una convenzione comune e non è garantito che sia vero il 100% delle volte?
Quando si passa un argomento a main()
in un'applicazione C o C ++, sarà argv[0]
sempre il nome dell'eseguibile? O è solo una convenzione comune e non è garantito che sia vero il 100% delle volte?
Risposte:
Le supposizioni (anche le congetture istruite) sono divertenti, ma per essere sicuri devi davvero consultare i documenti degli standard. Ad esempio, ISO C11 afferma (la mia enfasi):
Se il valore di
argc
è maggiore di zero, la stringa puntata daargv[0]
rappresenta il nome del programma;argv[0][0]
deve essere il carattere null se il nome del programma non è disponibile dall'ambiente host.
Quindi no, è solo il nome del programma se quel nome è disponibile. E "rappresenta" il nome del programma, non necessariamente è il nome del programma. La sezione prima di quella afferma:
Se il valore di
argc
è maggiore di zero, i membri dell'arrayargv[0]
tramiteargv[argc-1]
inclusivo conterranno puntatori a stringhe, a cui vengono forniti valori definiti dall'implementazione dall'ambiente host prima dell'avvio del programma.
Questo è invariato rispetto a C99, lo standard precedente, e significa che anche i valori non sono dettati dallo standard: dipende interamente dall'implementazione.
Ciò significa che il nome del programma può essere vuoto se l'ambiente host non lo fornisce e qualsiasi altra cosa se l'ambiente host lo fa fornisce, a condizione che "qualsiasi altra cosa" rappresenti in qualche modo il nome del programma. Nei miei momenti più sadici, prenderei in considerazione la possibilità di tradurlo in swahili, eseguirlo attraverso un cifrario di sostituzione e quindi memorizzarlo in ordine inverso dei byte :-).
Tuttavia, l'attuazione definiti ha un significato specifico nelle norme ISO - il documento di attuazione must come funziona. Quindi anche UNIX, che può mettere tutto ciò che vuole argv[0]
con la exec
famiglia di chiamate, deve (e lo fa) documentarlo.
argv[0]
sia relativa alla programmazione nel mondo reale.
In *nix
sistemi di tipo con exec*()
chiamate, argv[0]
sarà tutto ciò che il chiamante mette nel argv0
posto nella exec*()
chiamata.
La shell utilizza la convenzione secondo cui questo è il nome del programma e la maggior parte degli altri programmi segue la stessa convenzione, quindi di argv[0]
solito il nome del programma.
Ma un programma Unix canaglia può chiamare exec()
e fare argv[0]
tutto ciò che vuole, quindi non importa cosa dice lo standard C, non puoi contare su questo il 100% delle volte.
Secondo lo standard C ++, sezione 3.6.1:
argv [0] deve essere il puntatore al carattere iniziale di un NTMBS che rappresenta il nome utilizzato per invocare il programma o ""
Quindi no, non è garantito, almeno dallo Standard.
ISO-IEC 9899 afferma:
5.1.2.2.1 Avvio del programma
Se il valore di
argc
è maggiore di zero, la stringa puntata daargv[0]
rappresenta il nome del programma;argv[0][0]
deve essere il carattere null se il nome del programma non è disponibile dall'ambiente host. Se il valore diargc
è maggiore di uno, le stringhe puntate daargv[1]
tramiteargv[argc-1]
rappresentano i parametri del programma .
Ho anche usato:
#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) */
E poi devi solo analizzare la stringa per estrarre il nome dell'eseguibile dal percorso.
/proc/self/path/a.out
collegamento simbolico può essere utilizzabile su Solaris 10 e versioni successive.
GetModuleFileNameW
dovrebbe essere usato per poter recuperare qualsiasi percorso, ma solo la presenza del codice costituisce una buona guida).
Questa pagina afferma:
L'elemento argv [0] normalmente contiene il nome del programma, ma non ci si dovrebbe fare affidamento su questo - comunque è insolito che un programma non conosca il proprio nome!
Tuttavia, altre pagine sembrano confermare il fatto che è sempre il nome dell'eseguibile. Questo afferma:
Noterai che argv [0] è il percorso e il nome del programma stesso. Ciò consente al programma di scoprire informazioni su se stesso. Ne aggiunge anche uno in più all'array di argomenti del programma, quindi un errore comune quando si recuperano gli argomenti della riga di comando è quello di prendere argv [0] quando si vuole argv [1].
argv[0]="-/bin/sh"
? Questo è il caso di tutte le macchine che ho usato, comunque.
Applicazioni di avere un argv[0] !=
nome eseguibile
molte shell determinano se sono una shell di login controllando argv[0][0] == '-'
. Le shell di accesso hanno proprietà diverse, in particolare che forniscono alcuni file predefiniti come/etc/profile
.
In genere è l'init stesso o getty
che aggiunge l'interlinea -
, vedere anche: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152 # 300152
binari multi-chiamata, forse in particolare Busybox . Questi link simbolici più nomi, ad esempio /bin/sh
ea /bin/ls
un singolo eseguibile /bin/busybox
, che riconosce da quale strumento utilizzareargv[0]
.
Ciò rende possibile avere un unico piccolo eseguibile collegato staticamente che rappresenta più strumenti e funzionerà praticamente su qualsiasi ambiente Linux.
Vedi anche: /unix/315812/why-does-argv-include-the-program-name/315817
execve
Esempio POSIX argv[0] !=
eseguibile in cui nome eseguibile
Altri menzionati exec
, ma ecco un esempio eseguibile.
AC
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *argv[] = {"yada yada", NULL};
char *envp[] = {NULL};
execve("b.out", argv, envp);
}
avanti Cristo
#include <stdio.h>
int main(int argc, char **argv) {
puts(argv[0]);
}
Poi:
gcc a.c -o a.out
gcc b.c -o b.out
./a.out
Dà:
yada yada
Sì, argv[0]
potrebbe anche essere:
Testato su Ubuntu 16.10.
Non sono sicuro che sia una convenzione o uno standard quasi universale, ma in entrambi i casi dovresti rispettarlo. Tuttavia, non l'ho mai visto sfruttato al di fuori di Unix e sistemi simili a Unix. Negli ambienti Unix - e forse in particolare ai vecchi tempi - i programmi potevano avere comportamenti significativamente diversi a seconda del nome con cui vengono invocati.
MODIFICATO: Vedo da altri post contemporaneamente al mio che qualcuno l'ha identificato come proveniente da uno standard particolare, ma sono sicuro che la convenzione è antecedente allo standard.
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
. Il nome dell'eseguibile non ha alcuna relazione con il valore inargv[0]
.