“ argv [0] = name-of-executable” เป็นมาตรฐานที่ยอมรับหรือเป็นเพียงแบบแผนทั่วไป


104

เมื่อส่งอาร์กิวเมนต์ไปยังmain()แอปพลิเคชัน C หรือ C ++ จะargv[0]เป็นชื่อของไฟล์ปฏิบัติการเสมอ? หรือนี่เป็นเพียงอนุสัญญาทั่วไปและไม่รับประกันว่าจะเป็นจริง 100% ของเวลา?


20
ใน Unix ให้พิจารณา: execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);. ชื่อของไฟล์ปฏิบัติการไม่มีความสัมพันธ์กับค่าในargv[0].
Jonathan Leffler

คำตอบ:


122

การเดา (แม้กระทั่งการคาดเดาที่มีการศึกษา) เป็นเรื่องสนุก แต่คุณต้องไปที่เอกสารมาตรฐานเพื่อให้แน่ใจ ตัวอย่างเช่นสถานะ ISO C11 (ความสำคัญของฉัน):

ถ้าค่าของargcมากกว่าศูนย์สตริงที่ชี้ไปโดยargv[0] แสดงถึงชื่อโปรแกรม argv[0][0]จะต้องเป็นอักขระ null หากชื่อโปรแกรมไม่พร้อมใช้งานจากสภาพแวดล้อมโฮสต์

จึงไม่เป็นเพียงชื่อโปรแกรมถ้าชื่อที่มีความพร้อมใช้งาน และ"แสดง"ชื่อโปรแกรมไม่จำเป็นต้องเป็นชื่อโปรแกรมเสมอไป ส่วนก่อนหน้านั้นระบุว่า:

ถ้าค่าของargcมากกว่าศูนย์สมาชิกอาร์เรย์ที่argv[0]ผ่านการargv[argc-1]รวมจะต้องมีตัวชี้ไปยังสตริงซึ่งได้รับค่าที่กำหนดการนำไปใช้งานโดยสภาพแวดล้อมโฮสต์ก่อนที่จะเริ่มต้นโปรแกรม

สิ่งนี้ไม่เปลี่ยนแปลงจาก C99 ซึ่งเป็นมาตรฐานก่อนหน้านี้และหมายความว่าแม้แต่ค่าต่างๆก็ไม่ได้ถูกกำหนดโดยมาตรฐาน แต่ก็ขึ้นอยู่กับการนำไปใช้งานทั้งหมด

ซึ่งหมายความว่าชื่อโปรแกรมที่สามารถจะว่างเปล่าถ้าสภาพแวดล้อมโฮสต์ไม่ให้มันและสิ่งอื่น ๆ ถ้าสภาพแวดล้อมโฮสต์ไม่ให้มันมีเงื่อนไขว่า "สิ่งอื่น" อย่างใดแสดงชื่อโปรแกรม ในช่วงเวลาซาดิสม์ของฉันฉันจะพิจารณาแปลเป็นภาษาสวาฮิลีโดยใช้รหัสแทนจากนั้นจัดเก็บตามลำดับไบต์ย้อนกลับ :-)

อย่างไรก็ตามการดำเนินการตามที่กำหนดไว้จะมีความหมายที่เฉพาะเจาะจงในมาตรฐาน ISO - เอกสารการดำเนินการต้องวิธีการทำงาน ดังนั้นแม้ UNIX ซึ่งสามารถใส่อะไรที่มันชอบเข้าargv[0]กับexecครอบครัวของสายมี (และไม่) เอกสารมัน


3
นั่นอาจเป็นมาตรฐาน แต่ unix ไม่ได้บังคับใช้และคุณไม่สามารถวางใจได้
dmckee --- อดีตผู้ดูแลลูกแมว

4
คำถามที่ไม่ได้กล่าวถึง UNIX ที่ทั้งหมด มันเป็นคำถาม C ธรรมดาและเรียบง่ายดังนั้น ISO C จึงเป็นเอกสารอ้างอิง ชื่อโปรแกรมเป็นการใช้งานที่กำหนดไว้ในมาตรฐานดังนั้นการใช้งานจึงมีอิสระที่จะทำในสิ่งที่ต้องการรวมถึงการอนุญาตบางสิ่งในนั้นที่ไม่ใช่ชื่อจริง - ฉันคิดว่าฉันจะทำให้ชัดเจนในประโยคสุดท้าย
paxdiablo

2
ท่านผมไม่ได้ลงคะแนนคุณลงและไม่เห็นด้วยกับผู้ที่ไม่เพราะคำตอบนี้เป็นเผด็จการในขณะที่มันสามารถจะได้รับ แต่ฉันคิดว่าความไม่น่าเชื่อถือของมูลค่าargv[0]เป็นสิ่งที่สำคัญต่อการเขียนโปรแกรมในโลกแห่งความเป็นจริง
dmckee --- อดีตผู้ดูแลลูกแมว

4
@caf ถูกต้อง ฉันเคยเห็นมันมีสิ่งที่หลากหลายเช่นเส้นทางแบบเต็มของโปรแกรม ('/ progpath / prog') เพียงแค่ชื่อไฟล์ ('prog'), ชื่อที่แก้ไขเล็กน้อย ('-prog'), ชื่อที่สื่อความหมาย (' prog - โปรแกรมสำหรับ progging ') และ nothing (' ') การนำไปใช้ต้องกำหนดสิ่งที่ถือไว้ แต่นั่นคือมาตรฐานทั้งหมดที่ต้องการ
paxdiablo

3
ขอบคุณทุกคน! การสนทนาที่ยอดเยี่ยมจากคำถามง่ายๆ (ดูเหมือน) แม้ว่าคำตอบของ Richard จะใช้ได้กับระบบปฏิบัติการ * nix แต่ฉันก็เลือกคำตอบของ paxdiablo เพราะไม่ค่อยสนใจพฤติกรรมของระบบปฏิบัติการที่เฉพาะเจาะจงและสนใจเป็นหลักในการมีอยู่ (หรือไม่มี) มาตรฐานที่ยอมรับ (หากคุณสงสัย: ในบริบทของคำถามเดิม - ฉันไม่มีระบบปฏิบัติการฉันกำลังเขียนโค้ดเพื่อสร้างบัฟเฟอร์ raw argc / argv สำหรับไฟล์ปฏิบัติการที่โหลดลงในอุปกรณ์ฝังตัวและจำเป็นต้องรู้ว่าฉันควรทำอย่างไร ด้วย argv [0]) +1 ถึง StackOverflow เพื่อความยอดเยี่ยม!
Mike Willekes

51

ภายใต้*nixระบบประเภทที่มีการexec*()โทรargv[0]จะเป็นอะไรก็ตามที่ผู้โทรเข้ามาในargv0จุดที่exec*()โทร

เชลล์ใช้หลักการว่านี่คือชื่อโปรแกรมและโปรแกรมอื่น ๆ ส่วนใหญ่จะเป็นไปตามแบบแผนเดียวกันดังนั้นargv[0]โดยปกติจะเป็นชื่อโปรแกรม

แต่โปรแกรม Unix โกงสามารถโทรexec()และทำargv[0]อะไรก็ได้ที่ชอบดังนั้นไม่ว่ามาตรฐาน C จะพูดอะไรคุณก็ไม่สามารถนับได้ 100% ของเวลานี้


4
นี่เป็นคำตอบที่ดีกว่าของ paxdiablo ข้างต้น มาตรฐานเรียกมันว่า "ชื่อโปรแกรม" แต่สิ่งนี้ไม่ได้บังคับใช้กับความรู้ของฉัน เมล็ด Unix ส่งสตริงที่ส่งผ่านไปยัง execve () อย่างสม่ำเสมอโดยไม่เปลี่ยนแปลงไปยังกระบวนการย่อย
Andy Ross

4
มาตรฐาน C มีข้อ จำกัด ในสิ่งที่สามารถพูดได้เนื่องจากไม่รู้เกี่ยวกับ 'execve ()' เป็นต้นมาตรฐาน POSIX ( opengroup.org/onlinepubs/9699919799/functions/execve.html ) มีมากกว่าที่จะพูด - ทำให้ชัดเจน สิ่งที่อยู่ใน argv [0] เป็นไปตามความตั้งใจของกระบวนการที่เรียกใช้การเรียกระบบ 'execve ()' (หรือที่เกี่ยวข้อง)
Jonathan Leffler

1
@ แอนดี้คุณมีอิสระที่จะแสดงความคิดเห็นของคุณ :-) แต่คุณคิดผิดเกี่ยวกับการบังคับใช้ หากการนำไปใช้งานไม่เป็นไปตามมาตรฐานแสดงว่าไม่เป็นไปตามมาตรฐาน และในความเป็นจริงเนื่องจากมีการกำหนดการนำไปใช้งานว่า "ชื่อโปรแกรม" คืออะไรระบบปฏิบัติการอย่าง UNIX จึงสอดคล้องตราบเท่าที่ระบุว่าชื่อคืออะไร ซึ่งรวมถึงความสามารถในการปลอมชื่อโปรแกรมอย่างโจ่งแจ้งโดยการโหลด argv [0] ด้วยสิ่งที่คุณต้องการในกลุ่มการโทรของผู้บริหาร
paxdiablo

นั่นคือความสวยงามของคำว่า "แสดง" ในมาตรฐานเมื่ออ้างถึง argv [0] ("แทนชื่อโปรแกรม") และ argv [1..N] ("แสดงถึงอาร์กิวเมนต์ของโปรแกรม") "unsaden swallow" เป็นชื่อโปรแกรมที่ถูกต้อง
Richard Pennington

9

ตามมาตรฐาน C ++ ส่วน 3.6.1:

argv [0] จะเป็นตัวชี้ไปยังอักขระเริ่มต้นของ NTMBS ที่แสดงถึงชื่อที่ใช้เรียกโปรแกรมหรือ ""

ดังนั้นจึงไม่รับประกันอย่างน้อยตามมาตรฐาน


5
ฉันคิดว่าเป็นสตริงหลายไบต์ที่ถูกยกเลิกด้วย null?
paxdiablo

6

ISO-IEC 9899 สถานะ:

5.1.2.2.1 การเริ่มต้นโปรแกรม

ถ้าค่าของargcมากกว่าศูนย์สตริงที่ชี้โดยargv[0]แสดงถึงชื่อโปรแกรม argv[0][0]จะต้องเป็นอักขระ null หากชื่อโปรแกรมไม่พร้อมใช้งานจากสภาพแวดล้อมโฮสต์ ถ้าค่าของargcค่ามากกว่าหนึ่งสายที่ชี้ไปตามargv[1]ผ่านargv[argc-1]ตัวแทนของพารามิเตอร์โปรแกรม

ฉันยังใช้:

#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) */

จากนั้นคุณต้องแยกวิเคราะห์สตริงเพื่อแยกชื่อปฏิบัติการออกจากพา ธ


2
/proc/self/path/a.outsymlink อาจจะสามารถใช้งานได้บน Solaris 10 ขึ้นไป
ephemient

เพิ่มคะแนนให้กับรหัส (ไม่ได้บอกว่าเหมาะหรือถูกต้องเช่นใน Windows GetModuleFileNameWควรใช้เพื่อให้สามารถดึงเส้นทางใด ๆ แต่เพียงแค่การมีอยู่ของรหัสเท่านั้นที่ถือเป็นแนวทางที่ดี)
ไชโยและ hth - Alf

4

แอปพลิเคชั่นที่มีargv[0] !=ชื่อปฏิบัติการ

  • argv[0][0] == '-'เปลือกหอยจำนวนมากตรวจสอบว่าพวกเขาจะเป็นเปลือกเข้าสู่ระบบโดยการตรวจสอบ ล็อกอินเชลล์มีคุณสมบัติที่แตกต่างกันโดยเฉพาะอย่างยิ่งที่มาไฟล์เริ่มต้นบางไฟล์เช่น/etc/profile.

    โดยทั่วไปจะเป็นตัวเริ่มต้นเองหรือgettyเพิ่มส่วนนำ-ให้ดูเพิ่มเติมที่: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in สร้าง / 300152 # 300152

  • ไบนารีหลายสายอาจจะเป็นที่สะดุดตาที่สุดBusybox symlink หลายชื่อเช่น/bin/shและ/bin/lsexebutable เดียว/bin/busyboxซึ่งรับรู้ว่าจะใช้เครื่องมือargv[0]ใด

    สิ่งนี้ทำให้เป็นไปได้ที่จะมีไฟล์ปฏิบัติการที่เชื่อมโยงแบบคงที่ขนาดเล็กเพียงตัวเดียวซึ่งแสดงถึงเครื่องมือหลายตัวและจะทำงานได้บนทุกสภาพแวดล้อมของ Linux

ดูเพิ่มเติม: /unix/315812/why-does-argv-include-the-program-name/315817

execveตัวอย่างPOSIX ที่argv[0] !=รันได้โดยที่ชื่อปฏิบัติการ

อื่น ๆ ที่กล่าวถึง execแต่นี่เป็นตัวอย่างที่รันได้

ac

#define _XOPEN_SOURCE 700
#include <unistd.h>

int main(void) {
    char *argv[] = {"yada yada", NULL};
    char *envp[] = {NULL};
    execve("b.out", argv, envp);
}

bc

#include <stdio.h>

int main(int argc, char **argv) {
    puts(argv[0]);
}

จากนั้น:

gcc a.c -o a.out
gcc b.c -o b.out
./a.out

ให้:

yada yada

ใช่argv[0]อาจเป็น:

ทดสอบบน Ubuntu 16.10


3

หน้านี้ระบุ:

โดยปกติองค์ประกอบ argv [0] จะมีชื่อของโปรแกรม แต่ก็ไม่ควรพึ่งพา - อย่างไรก็ตามมันเป็นเรื่องผิดปกติที่โปรแกรมจะไม่รู้จักชื่อของตัวเอง!

อย่างไรก็ตามหน้าอื่น ๆ ดูเหมือนจะสำรองข้อมูลไว้ว่าเป็นชื่อของไฟล์ปฏิบัติการเสมอ สิ่งนี้ระบุ:

คุณจะสังเกตเห็นว่า argv [0] คือพา ธ และชื่อของโปรแกรมเอง สิ่งนี้ช่วยให้โปรแกรมสามารถค้นพบข้อมูลเกี่ยวกับตัวเอง นอกจากนี้ยังเพิ่มอีกหนึ่งรายการในอาร์เรย์ของอาร์กิวเมนต์โปรแกรมดังนั้นข้อผิดพลาดทั่วไปเมื่อดึงอาร์กิวเมนต์บรรทัดคำสั่งคือการคว้า argv [0] เมื่อคุณต้องการ argv [1]


11
บางโปรแกรมใช้ประโยชน์จากการที่พวกเขาไม่รู้จักชื่อที่ใช้เรียกใช้ ฉันเชื่อว่า BusyBox ( busybox.net/about.html ) ทำงานในลักษณะนี้ มีเพียงโปรแกรมเดียวที่สามารถใช้งานยูทิลิตี้บรรทัดคำสั่งต่างๆได้ มันใช้ลิงก์สัญลักษณ์จำนวนมากและ argv [0] เพื่อกำหนดว่าควรเรียกใช้เครื่องมือบรรทัดคำสั่งใด
เทรนต์

ใช่ฉันจำได้ว่าสังเกตว่า "gunzip" เป็นลิงก์สัญลักษณ์ไปยัง "gzip" และสงสัยอยู่ครู่หนึ่งว่ามันทำงานอย่างไร
David Thornley

2
หลายโปรแกรมดูที่ argv [0] สำหรับข้อมูล; ตัวอย่างเช่นหากส่วนประกอบสุดท้ายของชื่อขึ้นต้นด้วยเครื่องหมายขีด ('/ bin / -sh' เป็นต้น) เชลล์จะเรียกใช้โปรไฟล์และสิ่งอื่น ๆ สำหรับเชลล์ล็อกอิน
Jonathan Leffler

2
@ จอน: ฉันคิดว่าล็อกอินเชลล์เริ่มต้นด้วยargv[0]="-/bin/sh"? นั่นเป็นกรณีของเครื่องทั้งหมดที่ฉันใช้ แต่อย่างใด
ephemient

3

ฉันไม่แน่ใจว่าเป็นอนุสัญญาที่เกือบจะเป็นสากลหรือเป็นมาตรฐาน แต่คุณควรปฏิบัติตามวิธีใด ฉันไม่เคยเห็นว่ามันถูกใช้ประโยชน์นอกระบบ Unix และ Unix เหมือนกัน ในสภาพแวดล้อม Unix - และโดยเฉพาะอย่างยิ่งในสมัยก่อนโปรแกรมอาจมีพฤติกรรมที่แตกต่างกันอย่างมากขึ้นอยู่กับชื่อที่เรียกใช้

แก้ไข: ฉันเห็นจากโพสต์อื่น ๆ ในเวลาเดียวกันกับของฉันว่ามีคนระบุว่ามาจากมาตรฐานเฉพาะ แต่ฉันแน่ใจว่าการประชุมนั้นมีมานานแล้วมาตรฐาน


1
ฉันหวังว่าหากผู้คนจะ "ทำเครื่องหมาย" คำตอบของฉันพวกเขาจะให้สิ่งที่พวกเขาไม่ชอบเกี่ยวกับเรื่องนี้
Joe Mabel

0

หากคุณเริ่มโปรแกรม Amiga โดย Workbench argv [0] จะไม่ถูกตั้งค่าโดย CLI เท่านั้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.