Bir main()
C veya C ++ uygulamasında argümanı iletirken, her argv[0]
zaman yürütülebilir dosyanın adı mı olur? Yoksa bu sadece yaygın bir kongre midir ve her zaman doğru olması garanti edilmez mi?
Bir main()
C veya C ++ uygulamasında argümanı iletirken, her argv[0]
zaman yürütülebilir dosyanın adı mı olur? Yoksa bu sadece yaygın bir kongre midir ve her zaman doğru olması garanti edilmez mi?
Yanıtlar:
Tahmin çalışması (hatta eğitimli tahmin çalışması) eğlencelidir, ancak emin olmak için gerçekten standart belgelerine gitmeniz gerekir. Örneğin, ISO C11 şunu belirtir (vurgu):
Değeri
argc
sıfırdan büyükse, ile gösterilen dize program adınıargv[0]
temsil eder ;argv[0][0]
program adı ana bilgisayar ortamında mevcut değilse boş karakter olacaktır.
Yani hayır, bu ad mevcutsa yalnızca program adıdır . Ve bu "temsil" program adı, mutlaka bir program adı. Bundan önceki bölüm şunları belirtir:
Değeri
argc
sıfırdan büyükse, kapsayıcıargv[0]
aracılığıyla dizi üyeleriargv[argc-1]
, program başlangıcından önce ana bilgisayar ortamı tarafından uygulama tanımlı değerler verilen dizelere işaretçiler içermelidir.
Bu, önceki standart olan C99'dan değişmez ve değerlerin bile standart tarafından dikte edilmediği anlamına gelir - tamamen uygulamaya bağlıdır.
Bu, ana bilgisayar ortamı bunu sağlamazsa program adının boş olabileceği anlamına gelir ve ana bilgisayar ortamı bunu sağlarsa, "başka herhangi bir şey" bir şekilde program adını temsil ettiği sürece, başka herhangi bir şey olabilir. Daha sadist anlarımda, onu Swahili'ye çevirmeyi, bir ikame şifresinden geçirip sonra ters bayt sırasına göre depolamayı düşünürdüm :-).
Bununla birlikte, uygulama tanımlı yapar nasıl çalıştığını uygulama zorunluluk belgesi - ISO standartlarına belli bir anlamı vardır. Bu nedenle , çağrı ailesine argv[0]
istediği her şeyi koyabilen UNIX bile bunu exec
belgelemek zorundadır (ve yapar).
argv[0]
gerçek dünyadaki programlamaya yakın olduğunu düşünüyorum .
Altında *nix
tip sistemlerde exec*()
çağrılar, argv[0]
içine ne olursa olsun arayan koyar olacak argv0
içinde yerinde exec*()
çağrı.
Kabuk, bunun program adı olduğu kuralını kullanır ve diğer programların çoğu aynı kuralı izler, yani argv[0]
genellikle program adı.
Ancak haydut bir Unix programı arayabilir exec()
ve istediği argv[0]
her şeyi yapabilir , bu nedenle C standardı ne derse desin, bu% 100'üne güvenemezsiniz.
C ++ Standardına göre bölüm 3.6.1:
argv [0], programı çağırmak için kullanılan adı temsil eden bir NTMBS'nin ilk karakterine gösterici olacaktır veya ""
Yani hayır, en azından Standart tarafından garanti edilmez.
ISO-IEC 9899 şunları belirtir:
5.1.2.2.1 Program başlatma
Değeri
argc
sıfırdan büyükse, ile gösterilen dizgeargv[0]
program adını temsil eder;argv[0][0]
program adı ana bilgisayar ortamında mevcut değilse boş karakter olacaktır. Değeri iseargc
birden büyük olduğu, dizeleri tarafından işaretargv[1]
yoluylaargv[argc-1]
temsil program parametrelerini .
Ben de kullandım:
#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) */
Ve sonra, çalıştırılabilir adı yoldan çıkarmak için dizeyi ayrıştırmanız yeterlidir.
/proc/self/path/a.out
Sembolik bağ Solaris 10 ve yukarı kullanılabilirler.
GetModuleFileNameW
herhangi bir yolu elde etmek için kullanılmalıdır, ancak yalnızca kodun varlığı iyi bir rehberlik teşkil eder).
argv[0] !=
Yürütülebilir ada sahip uygulamalar
birçok kabuk kontrol ederek bunların bir giriş kabuğu olup olmadığını belirler argv[0][0] == '-'
. Oturum açma kabuklarının farklı özellikleri vardır, özellikle /etc/profile
.
Tipik olarak initin kendisidir veya baştaki kelimeyi getty
ekler, -
ayrıca bkz .: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-yapı / 300152 # 300152
çoklu arama ikili dosyaları, belki de en önemlisi Busybox . Bunlar sembolik bağı birden isimler örneğin /bin/sh
ve /bin/ls
tek exebutable için /bin/busybox
gelen kullanımı için hangi aracı olarak tanır argv[0]
.
Bu, birden çok aracı temsil eden ve temelde herhangi bir Linux ortamında çalışacak olan tek bir küçük statik olarak bağlantılı yürütülebilir dosyaya sahip olmayı mümkün kılar.
Ayrıca bkz .: /unix/315812/why-does-argv-include-the-program-name/315817
Çalıştırılabilir adın execve
bulunduğu argv[0] !=
çalıştırılabilir POSIX örneği
Diğerleri bahsetti exec
, ancak burada çalıştırılabilir bir örnek var.
AC
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *argv[] = {"yada yada", NULL};
char *envp[] = {NULL};
execve("b.out", argv, envp);
}
M.Ö
#include <stdio.h>
int main(int argc, char **argv) {
puts(argv[0]);
}
Sonra:
gcc a.c -o a.out
gcc b.c -o b.out
./a.out
Verir:
yada yada
Evet, argv[0]
şunlar da olabilir:
Ubuntu 16.10'da test edilmiştir.
Bu sayfa şunları belirtir:
Argv [0] öğesi normalde programın adını içerir, ancak buna güvenilmemelidir - zaten bir programın kendi adını bilmemesi alışılmadık bir durumdur!
Ancak, diğer sayfalar bunun her zaman yürütülebilir dosyanın adı olduğu gerçeğini destekliyor gibi görünüyor. Bu, şunu belirtir:
Argv [0] 'ın programın yolu ve adı olduğunu fark edeceksiniz. Bu, programın kendisi hakkında bilgi keşfetmesini sağlar. Ayrıca, program argümanları dizisine bir tane daha ekler, bu nedenle komut satırı argümanlarını alırken yaygın bir hata, argv [1] 'i istediğinizde argv [0]' ı almaktır.
argv[0]="-/bin/sh"
? Nasılsa kullandığım tüm makinelerde durum bu.
Neredeyse evrensel bir sözleşme mi yoksa bir standart mı olduğundan emin değilim, ancak her iki durumda da ona uymalısınız. Yine de Unix ve Unix benzeri sistemler dışında kullanıldığını hiç görmedim. Unix ortamlarında - ve belki de özellikle eski günlerde - programlar, çağrıldıkları ada bağlı olarak önemli ölçüde farklı davranışlara sahip olabilir.
DÜZENLENMİŞ: Benimki ile aynı zamanda diğer gönderilerden birinin bunun belirli bir standarttan geldiğini belirlediğini görüyorum, ancak konvansiyonun uzun süredir standarttan önce geldiğinden eminim.
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
. Yürütülebilir dosyanın adının içindeki değerle hiçbir ilişkisi yokturargv[0]
.