เมื่อส่งอาร์กิวเมนต์ไปยังmain()
แอปพลิเคชัน C หรือ C ++ จะargv[0]
เป็นชื่อของไฟล์ปฏิบัติการเสมอ? หรือนี่เป็นเพียงอนุสัญญาทั่วไปและไม่รับประกันว่าจะเป็นจริง 100% ของเวลา?
เมื่อส่งอาร์กิวเมนต์ไปยังmain()
แอปพลิเคชัน C หรือ C ++ จะargv[0]
เป็นชื่อของไฟล์ปฏิบัติการเสมอ? หรือนี่เป็นเพียงอนุสัญญาทั่วไปและไม่รับประกันว่าจะเป็นจริง 100% ของเวลา?
คำตอบ:
การเดา (แม้กระทั่งการคาดเดาที่มีการศึกษา) เป็นเรื่องสนุก แต่คุณต้องไปที่เอกสารมาตรฐานเพื่อให้แน่ใจ ตัวอย่างเช่นสถานะ ISO C11 (ความสำคัญของฉัน):
ถ้าค่าของ
argc
มากกว่าศูนย์สตริงที่ชี้ไปโดยargv[0]
แสดงถึงชื่อโปรแกรมargv[0][0]
จะต้องเป็นอักขระ null หากชื่อโปรแกรมไม่พร้อมใช้งานจากสภาพแวดล้อมโฮสต์
จึงไม่เป็นเพียงชื่อโปรแกรมถ้าชื่อที่มีความพร้อมใช้งาน และ"แสดง"ชื่อโปรแกรมไม่จำเป็นต้องเป็นชื่อโปรแกรมเสมอไป ส่วนก่อนหน้านั้นระบุว่า:
ถ้าค่าของ
argc
มากกว่าศูนย์สมาชิกอาร์เรย์ที่argv[0]
ผ่านการargv[argc-1]
รวมจะต้องมีตัวชี้ไปยังสตริงซึ่งได้รับค่าที่กำหนดการนำไปใช้งานโดยสภาพแวดล้อมโฮสต์ก่อนที่จะเริ่มต้นโปรแกรม
สิ่งนี้ไม่เปลี่ยนแปลงจาก C99 ซึ่งเป็นมาตรฐานก่อนหน้านี้และหมายความว่าแม้แต่ค่าต่างๆก็ไม่ได้ถูกกำหนดโดยมาตรฐาน แต่ก็ขึ้นอยู่กับการนำไปใช้งานทั้งหมด
ซึ่งหมายความว่าชื่อโปรแกรมที่สามารถจะว่างเปล่าถ้าสภาพแวดล้อมโฮสต์ไม่ให้มันและสิ่งอื่น ๆ ถ้าสภาพแวดล้อมโฮสต์ไม่ให้มันมีเงื่อนไขว่า "สิ่งอื่น" อย่างใดแสดงชื่อโปรแกรม ในช่วงเวลาซาดิสม์ของฉันฉันจะพิจารณาแปลเป็นภาษาสวาฮิลีโดยใช้รหัสแทนจากนั้นจัดเก็บตามลำดับไบต์ย้อนกลับ :-)
อย่างไรก็ตามการดำเนินการตามที่กำหนดไว้จะมีความหมายที่เฉพาะเจาะจงในมาตรฐาน ISO - เอกสารการดำเนินการต้องวิธีการทำงาน ดังนั้นแม้ UNIX ซึ่งสามารถใส่อะไรที่มันชอบเข้าargv[0]
กับexec
ครอบครัวของสายมี (และไม่) เอกสารมัน
argv[0]
เป็นสิ่งที่สำคัญต่อการเขียนโปรแกรมในโลกแห่งความเป็นจริง
ภายใต้*nix
ระบบประเภทที่มีการexec*()
โทรargv[0]
จะเป็นอะไรก็ตามที่ผู้โทรเข้ามาในargv0
จุดที่exec*()
โทร
เชลล์ใช้หลักการว่านี่คือชื่อโปรแกรมและโปรแกรมอื่น ๆ ส่วนใหญ่จะเป็นไปตามแบบแผนเดียวกันดังนั้นargv[0]
โดยปกติจะเป็นชื่อโปรแกรม
แต่โปรแกรม Unix โกงสามารถโทรexec()
และทำargv[0]
อะไรก็ได้ที่ชอบดังนั้นไม่ว่ามาตรฐาน C จะพูดอะไรคุณก็ไม่สามารถนับได้ 100% ของเวลานี้
ตามมาตรฐาน C ++ ส่วน 3.6.1:
argv [0] จะเป็นตัวชี้ไปยังอักขระเริ่มต้นของ NTMBS ที่แสดงถึงชื่อที่ใช้เรียกโปรแกรมหรือ ""
ดังนั้นจึงไม่รับประกันอย่างน้อยตามมาตรฐาน
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) */
จากนั้นคุณต้องแยกวิเคราะห์สตริงเพื่อแยกชื่อปฏิบัติการออกจากพา ธ
/proc/self/path/a.out
symlink อาจจะสามารถใช้งานได้บน Solaris 10 ขึ้นไป
GetModuleFileNameW
ควรใช้เพื่อให้สามารถดึงเส้นทางใด ๆ แต่เพียงแค่การมีอยู่ของรหัสเท่านั้นที่ถือเป็นแนวทางที่ดี)
แอปพลิเคชั่นที่มี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/ls
exebutable เดียว/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
หน้านี้ระบุ:
โดยปกติองค์ประกอบ argv [0] จะมีชื่อของโปรแกรม แต่ก็ไม่ควรพึ่งพา - อย่างไรก็ตามมันเป็นเรื่องผิดปกติที่โปรแกรมจะไม่รู้จักชื่อของตัวเอง!
อย่างไรก็ตามหน้าอื่น ๆ ดูเหมือนจะสำรองข้อมูลไว้ว่าเป็นชื่อของไฟล์ปฏิบัติการเสมอ สิ่งนี้ระบุ:
คุณจะสังเกตเห็นว่า argv [0] คือพา ธ และชื่อของโปรแกรมเอง สิ่งนี้ช่วยให้โปรแกรมสามารถค้นพบข้อมูลเกี่ยวกับตัวเอง นอกจากนี้ยังเพิ่มอีกหนึ่งรายการในอาร์เรย์ของอาร์กิวเมนต์โปรแกรมดังนั้นข้อผิดพลาดทั่วไปเมื่อดึงอาร์กิวเมนต์บรรทัดคำสั่งคือการคว้า argv [0] เมื่อคุณต้องการ argv [1]
argv[0]="-/bin/sh"
? นั่นเป็นกรณีของเครื่องทั้งหมดที่ฉันใช้ แต่อย่างใด
ฉันไม่แน่ใจว่าเป็นอนุสัญญาที่เกือบจะเป็นสากลหรือเป็นมาตรฐาน แต่คุณควรปฏิบัติตามวิธีใด ฉันไม่เคยเห็นว่ามันถูกใช้ประโยชน์นอกระบบ Unix และ Unix เหมือนกัน ในสภาพแวดล้อม Unix - และโดยเฉพาะอย่างยิ่งในสมัยก่อนโปรแกรมอาจมีพฤติกรรมที่แตกต่างกันอย่างมากขึ้นอยู่กับชื่อที่เรียกใช้
แก้ไข: ฉันเห็นจากโพสต์อื่น ๆ ในเวลาเดียวกันกับของฉันว่ามีคนระบุว่ามาจากมาตรฐานเฉพาะ แต่ฉันแน่ใจว่าการประชุมนั้นมีมานานแล้วมาตรฐาน
หากคุณเริ่มโปรแกรม Amiga โดย Workbench argv [0] จะไม่ถูกตั้งค่าโดย CLI เท่านั้น
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
. ชื่อของไฟล์ปฏิบัติการไม่มีความสัมพันธ์กับค่าในargv[0]
.