"Argv [0] = yürütülebilir dosyanın adı" kabul edilen bir standart mı yoksa yalnızca genel bir kural mı?


104

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?


20
Unix üzerinde, düşünün: 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 yoktur argv[0].
Jonathan Leffler

Yanıtlar:


122

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 argcsı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 argcsıfırdan büyükse, kapsayıcı argv[0]aracılığıyla dizi üyeleri argv[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 execbelgelemek zorundadır (ve yapar).


3
Bu standart olabilir, ancak unix bunu basitçe uygulamaz ve buna güvenemezsiniz.
dmckee --- eski moderatör kedicik

4
Soru UNIX söz etmedi hiç . Basit ve basit bir C sorusuydu, dolayısıyla ISO C referans belgesidir. Program adı standartta tanımlanmış bir uygulama olduğundan, bir uygulama istediğini yapmakta özgürdür, gerçek adı olmayan bir şeye izin vermek de dahil - bunu sondan bir önceki cümlede açıklığa kavuşturduğumu düşündüm.
paxdiablo

2
Pax, seni hayal oy vermedi ve bu cevabım kadar yetkili olduğundan yapanları tasvip etmiyoruz edebilirsiniz olsun. Ama değerinin güvenilmezliğinin argv[0]gerçek dünyadaki programlamaya yakın olduğunu düşünüyorum .
dmckee --- eski moderatör kedi

4
@caf, bu doğru. Programın tam yolu ('/ progpath / prog'), sadece dosya adı ('prog'), hafifçe değiştirilmiş bir ad ('-prog'), açıklayıcı bir ad (' prog - progging için bir program ') ve hiçbir şey (' '). Uygulama, neyi içerdiğini tanımlamalıdır, ancak tüm standart gerektirir.
paxdiablo

3
Herkese teşekkürler! (Görünüşte) basit bir sorudan harika tartışma. Richard'ın cevabı * nix işletim sistemleri için geçerli olsa da, paxdiablo'nun cevabını seçtim çünkü belirli bir işletim sisteminin davranışıyla daha az ilgileniyorum ve öncelikle kabul edilmiş bir standardın varlığıyla (veya yokluğuyla) ilgileniyorum. (Merak ediyorsanız: Orijinal soru bağlamında - işletim sistemim yok. Gömülü bir cihaza yüklenen bir yürütülebilir dosya için ham argc / argv arabelleğini oluşturmak için kod yazıyorum ve ne yapmam gerektiğini bilmem gerekiyor argv [0] ile). Harika olduğu için StackOverflow'a +1!
Mike Willekes

51

Altında *nixtip sistemlerde exec*()çağrılar, argv[0]içine ne olursa olsun arayan koyar olacak argv0iç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.


4
Bu paxdiablo'nun yukarısından daha iyi bir cevap. Standart buna sadece "program adı" diyor, ancak bu benim bildiğim hiçbir yerde uygulanmıyor. Unix çekirdekleri, execve () işlevine aktarılan dizeyi değişmeden çocuk sürece eşit olarak iletir.
Andy Ross

4
C standart vb POSIX standardı ( ') (execve' bu konuda bilmiyor çünkü ne diyebilirim sınırlıdır opengroup.org/onlinepubs/9699919799/functions/execve.html ) daha söylemek - bu temizlemek yapma argv [0] 'da olan, sürecin kaprisinde' execve () '(veya ilgili) sistem çağrısını yürütür.
Jonathan Leffler

1
@Andy, fikirlerini almakta özgürsün :-) Ama yaptırım konusunda yanılıyorsun. Bir uygulama standarda uymuyorsa, uygun değildir. Ve aslında, çünkü en uygulanması tanımlı "programı adı" ne, UNIX gibi bir OS olarak olduğu sürece belirtir olarak adının ne uygun. Bu, argv [0] 'ı çalıştırma ailesinde istediğiniz herhangi bir şeyle yükleyerek bir program adını bariz bir şekilde taklit edebilmeyi içerir.
paxdiablo

Standartta argv [0] ("program adını temsil eder") ve argv [1..N] ("program argümanlarını temsil ederler") anlamına gelen "temsil eder" kelimesinin güzelliği budur. "yüksüz yutma" geçerli bir program adıdır.
Richard Pennington

9

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.


5
Bunun boş sonlandırılmış çok baytlı dizge olduğunu varsayıyorum?
paxdiablo

6

ISO-IEC 9899 şunları belirtir:

5.1.2.2.1 Program başlatma

Değeri argcsıfırdan büyükse, ile gösterilen dizge argv[0]program adını temsil eder; argv[0][0]program adı ana bilgisayar ortamında mevcut değilse boş karakter olacaktır. Değeri ise argcbirden büyük olduğu, dizeleri tarafından işaret argv[1]yoluyla argv[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.


2
/proc/self/path/a.outSembolik bağ Solaris 10 ve yukarı kullanılabilirler.
ephemient

Kod için oy verildi (ideal veya doğru olduğunu söylemiyoruz, örneğin Windows'ta GetModuleFileNameWherhangi bir yolu elde etmek için kullanılmalıdır, ancak yalnızca kodun varlığı iyi bir rehberlik teşkil eder).
Şerefe ve hth. - Alf

4

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 gettyekler, -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/shve /bin/lstek exebutable için /bin/busyboxgelen 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 execvebulunduğ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.


3

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.


11
Bazı programlar, onları çağırmak için kullanılan adı bilmedikleri gerçeğinden yararlanır. BusyBox'ın ( busybox.net/about.html ) bu şekilde çalıştığına inanıyorum . Yalnızca bir yürütülebilir dosya vardır ve birçok farklı komut satırı yardımcı programını uygular. Hangi komut satırı aracının çalıştırılması gerektiğini belirlemek için bir grup sembolik bağlantı ve argv [0] kullanır
Trent

Evet, "gunzip" in "gzip" e sembolik bir bağlantı olduğunu fark ettiğimi ve bir an için bunun nasıl çalıştığını merak ettiğimi hatırlıyorum.
David Thornley

2
Birçok program bilgi için argv [0] 'a bakar; örneğin, ismin son bileşeni bir tire (örneğin '/ bin / -sh') ile başlıyorsa, kabuk profili ve diğer şeyleri bir oturum açma kabuğunda olduğu gibi çalıştıracaktır.
Jonathan Leffler

2
@Jon: Giriş kabuklarının başladığını sanıyordum argv[0]="-/bin/sh"? Nasılsa kullandığım tüm makinelerde durum bu.
ephemient

3

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.


1
Eminim ki eğer insanlar cevabımı "işaretlerlerse", neyi sevmediklerini belli etselerdi.
Joe Mabel

0

Workbench ile bir Amiga programı başlatırsanız argv [0] sadece CLI tarafından ayarlanmayacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.