Чому аргумент включає назву програми?


106

Типові програми Unix / Linux приймають введення командного рядка як кількість аргументів ( int argc) та вектор аргументу ( char *argv[]). Перший елемент програми argv- це назва програми, а потім - фактичні аргументи.

Чому назва програми передається виконуваному файлу як аргумент? Чи є приклади програм, що використовують своє ім’я (можливо, якась execситуація)?


6
як mv та cp?
Архемар

9
У Debian shє посилання на dash. Вони поводяться по-різному, коли їх називають як shабо якdash
Motte001

21
@AlexejMagura Якщо ви використовуєте щось подібне busybox(звичайне на рятувальних дисках тощо), то майже все (cp, mv, rm, ls, ...) є символічним посиланням на busbox.
Баард Копперуд

11
Я знаходжу це дуже важко ігнорувати, так що я скажу: ви , ймовірно , мали в виду програму «GNU» ( gcc, bash, gunzip, більшу частину решти ОС ...), так як Linux це тільки ядро.
wizzwizz4

10
@ wizzwizz4 Що не так із "Типовими програмами Unix / Linux"? Я читав це як "Типові програми, що працюють на Unix / Linux". Це набагато краще, ніж обмеження певних програм GNU. Денніс Річі, звичайно, не використовував жодної програми GNU. BTW ядро ​​Hurd - приклад програми GNU, яка не має основної функції ...
rudimeier

Відповіді:


122

Для початку зауважте, що argv[0]це не обов'язково назва програми. Це те , що абонент вводить в argv[0]частину execveсистемного виклику (наприклад , див це питання на переповнення стека ). (Усі інші варіанти execне системні виклики, а інтерфейси до execve.)

Припустимо, наприклад, наступне (за допомогою execl):

execl("/var/tmp/mybackdoor", "top", NULL);

/var/tmp/mybackdoorце те, що виконується, але argv[0]встановлено top, і це те, що psабо (справжнє) topвідображатиметься. Дивіться цю відповідь на U&L SE для отримання додаткової інформації про це.

Відклавши все це в сторону: До появи фантазійних файлових систем, як-от /proc, це argv[0]був єдиний спосіб, щоб процес дізнався про власне ім'я. Для чого це було б добре?

  • Кілька програм налаштовують свою поведінку залежно від імені, яким вони були викликані (як правило, символічними або жорсткими посиланнями, наприклад , утиліти BusyBox ; ще кілька прикладів наведено в інших відповідях на це питання).
  • Більше того, сервіси, демони та інші програми, що входять в систему через системний журнал, часто додають своє ім'я до записів журналу; без цього відстеження подій стало б поруч із нездійсненним.

18
Прикладами таких програм є bunzip2, bzcatі bzip2для яких перші дві є посиланнями на третю.
Руслан

5
@Ruslan Цікаво, що zcatце не посилання. Здається, вони уникають недоліків цієї методики, використовуючи сценарій оболонки. Але вони не можуть надрукувати повний --helpвихід, тому що хтось, хто додав параметри до gzip, забув також підтримувати zcat.
rudimeier

1
Наскільки я пам'ятаю, стандарти кодування GNU відмовляють від використання argv [0] для зміни поведінки програми ( розділ "Стандартні інтерфейси взагалі" в поточній версії ). gunzipє історичним винятком.

19
busybox - ще один чудовий приклад. Можна викликати 308 різних імен, щоб викликати різні команди: busybox.net/downloads/BusyBox.html#commands
Pepijn Schmitz

2
Багато, багато інших програм також вводять їх argv[0]у вихідні дані про використання / допомогу замість жорсткого кодування свого імені. Деякі в повному обсязі, деякі лише базовою назвою.
спектри

62

Багато:

  • Bash працює в режимі POSIX, коли argv[0]є sh. Вона працює як оболонка входу, коли argv[0]починається з -.
  • Vim поводиться по- різному при запуску , як vi, view, evim, eview, ex, vimdiffі т.д.
  • Busybox, як уже згадувалося.
  • У системах з Systemd як первонач shutdown, rebootі т.д. є символічними посиланнями наsystemctl .
  • і так далі.

7
Ще один є sendmailі mail. Кожна окрема unix MTA оснащена символьним посиланням для цих двох команд і призначена для імітації поведінки оригіналу при виклику як такої, що означає, що будь-яка програма Unix, якій потрібно відправляти пошту, точно знає, як це зробити.
Шадур

4
інший поширений випадок: testі [: коли ви викликаєте перший, він обробляє помилку, якщо останній аргумент є ]. (на фактичній стабільній Debian ці команди є двома різними програмами, але попередні версії та MacO все ще використовують ту саму програму). І tex, latexі так далі: бінарна те ж саме, але , дивлячись , як його називали, це вибрати правильний конфігураційний файл. initподібний.
Джакомо Катенацці

4
Пов'язане, [вважає помилкою, якщо останній аргумент - ні ] .
чепнер

Я думаю, це відповідає на друге питання, але не на перше. Я дуже сумніваюся, що хтось із дизайнерів ОС сів і сказав: «Ей, було б здорово, якби я одна і та ж програма робила різні речі просто на основі її виконавчого імені. Я думаю, тоді я включу це ім'я у свій масив аргументів. «
Joey

@Joey Так, формулювання покликане донести, що (Q: "Чи є ...? A:" Багато: ... ")
муру

34

Історично - argvце лише масив покажчиків на "слова" командного рядка, тому є сенс починати з першого "слова", яке, як правило, називається програмою.

І є досить багато програм, які поводяться по-різному, відповідно до того, яке ім’я використовується для їх виклику, тож ви можете просто створити різні посилання на них і отримати різні "команди". Найбільш крайній приклад, який я можу придумати, - це зайнятий ящик , який діє як кілька десятків різних "команд", залежно від того, як він викликається .

Редагувати : Посилання на перше видання Unix, як вимагається

Можна бачити , наприклад , з основною функції , ccщо argcі argvвже використовувалися. В оболонках копіюють аргументи parbufвсередині newargчастини петлі, в той час як лікуючі сама команда таким же чином , як аргументи. (Зрозуміло, пізніше він виконує лише перший аргумент, який є назвою команди). Схоже, execvродичів тоді не було.


1
будь ласка, додайте посилання, що підтверджують це.
lesmana

Зі швидкого скимінгу, execприймає назву команди для виконання та нульовий завершений масив покажчиків char (найкраще видно на minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s , де execбереться посилання на мітку 2 і мітку 1, і на позначці 2:з'являється etc/init\0, а на мітці 1:з'являється посилання на мітку 2 і кінцевий нуль), що в основному є execveсьогоднішнім мінусом envp.
ninjalj

1
execvі execlіснували "назавжди" (тобто з початку до середини 1970-х) - це execvбув системний виклик і execlбув функцією бібліотеки, яка називала його.   execveтоді не існувало, оскільки тоді не існувало середовища. Інші члени родини були додані пізніше.
G-Man

@ G-Man Чи можете ви вказати мене на execvджерело v1, з яким я пов’язаний? Просто цікаво.
dirkt

22

Користувачі:

Ви можете використовувати ім'я програми для зміни поведінки програми .

Наприклад, ви можете створити кілька посилань на фактичний бінарний файл.

Один з відомих прикладів, коли використовується ця методика, - проект зайнятої скриньки, який встановлює лише одне єдине бінарне і безліч посилань на нього. (ls, cp, mv тощо). Вони роблять це для економії місця для зберігання, оскільки їх цілі - це невеликі вбудовані пристрої.

Це також використовується в setarchutil-linux:

$ ls -l /usr/bin/ | grep setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 i386 -> setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 linux32 -> setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 linux64 -> setarch
-rwxr-xr-x 1 root root       14680 2015-10-22 16:54 setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 x86_64 -> setarch

Тут вони використовують цю техніку, щоб уникнути багатьох повторюваних вихідних файлів або просто зберегти джерела більш читабельними.

Іншим випадком використання буде програма, яка потребує завантаження деяких модулів або даних під час виконання. Наявність програмного шляху дозволяє вам завантажувати модулі з контуру відносно місця розташування програми .

Крім того, багато програм друкують повідомлення про помилки, включаючи назву програми .

Чому :

  1. Оскільки це POSIX-конвенція ( man 3p execve):

argv - це масив аргументів рядків, переданих новій програмі. За умовою, перший із цих рядків повинен містити ім'я файлу, пов'язане з файлом, який виконується.

  1. Це стандарт C (принаймні C99 і C11):

Якщо значення argc більше нуля, рядок, на який вказується argv [0], представляє назву програми; argv [0] [0] має бути нульовим символом, якщо ім'я програми недоступне в середовищі хоста.

Зверніть увагу, що в стандарті C написано "ім'я програми", а не "ім'я файлу".


3
Хіба це не перерветься, якщо ви перейдете до симпосилання з іншого символьного посилання?
Мехрдад

3
@Mehrdad, так, це недолік і може заплутати користувача.
rudimeier

@rudimeier: Ваші пункти "Чому" насправді не є причинами, вони просто "гомункулус", тобто просто виникає питання, чому стандарт вимагає, щоб це було так.
einpoklum

Питання ОП @einpoklum було: Чому назва програми передається виконуваному файлу? Я відповів: Тому що стандарт POSIX і C говорить нам про це. Як ти думаєш, це насправді не причина ? Якби документи, які я цитував, не існували, ймовірно, багато програм не перейшли б до назви програми.
rudimeier

ОП ефективно запитує "ЧОМУ говорять стандарти POSIX та C?" Формулювання було на абстрагованому рівні, але це здається зрозумілим. Реально єдиний спосіб знати - це запитати джерел.
користувач2338816

21

На додаток до програм, що змінюють їх поведінку залежно від того, як їх викликали, я вважаю argv[0]корисним для друку використання програми, наприклад:

printf("Usage: %s [arguments]\n", argv[0]);

Це призводить до того, що повідомлення про використання завжди використовує ім’я, через яке воно було викликане. Якщо програма перейменована, повідомлення про її використання змінюється разом із нею. Він навіть включає ім'я шляху, з якого він називався:

# cat foo.c 
#include <stdio.h>
int main(int argc, char **argv) { printf("Usage: %s [arguments]\n", argv[0]); }
# gcc -Wall -o foo foo.c
# mv foo /usr/bin 
# cd /usr/bin 
# ln -s foo bar
# foo
Usage: foo [arguments]
# bar
Usage: bar [arguments]
# ./foo
Usage: ./foo [arguments]
# /usr/bin/foo
Usage: /usr/bin/foo [arguments]

Це приємне дотик, особливо для невеликих інструментів / сценаріїв спеціального призначення, які можуть існувати всюди.

Це здається звичайною практикою і в інструментах GNU, див. lsНаприклад:

% ls --qq
ls: unrecognized option '--qq'
Try 'ls --help' for more information.
% /bin/ls --qq
/bin/ls: unrecognized option '--qq'
Try '/bin/ls --help' for more information.

3
+1. Я збирався запропонувати те саме. Дивно, що так багато людей зосереджуються на зміні поведінки і не можуть згадати, мабуть, найбільш очевидне та набагато більш розповсюджене використання.
The Vee

5

Один виконує програму набравши: program_name0 arg1 arg2 arg3 ....

Отже оболонка повинна вже ділити маркер, а перший маркер - це вже назва програми. І BTW, тому є однакові показники і на стороні програми, і на оболонці.

Я думаю, що це був просто прийом зручності (на самому початку), і, як ви бачите в інших відповідях, це було також дуже зручно, тому цю традицію продовжували і визначали як API.


4

В основному, argv включає в себе назву програми, щоб ви могли писати повідомлення про помилки, як-от prgm: file: No such file or directory, що було б реалізовано приблизно так:

    fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[1] );

2

Інший приклад застосування цієї програми - це ця програма, яка замінює себе на ... саму себе, поки ви не введете щось, що не є y.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char** argv) {

  (void) argc;

  printf("arg: %s\n", argv[1]);
  int count = atoi(argv[1]);

  if ( getchar() == 'y' ) {

    ++count;

    char buf[20];
    sprintf(buf, "%d", count);

    char* newargv[3];
    newargv[0] = argv[0];
    newargv[1] = buf;
    newargv[2] = NULL;

    execve(argv[0], newargv, NULL);
  }

  return count;
}

Очевидно, начебто надуманий, якщо цікавий приклад, але я думаю, що це може мати реальні користі - наприклад, бінарний файл, що оновлюється самостійно, який переписує власний простір пам’яті новою версією самого себе, яку він завантажив або змінив.

Приклад:

$ ./res 1
arg: 1
y
arg: 2
y
arg: 3
y
arg: 4
y
arg: 5
y
arg: 6
y
arg: 7
n

7 | $

Джерело та трохи більше інформації .


Вітаємо з досягненням 1000.
G-Man

0

Шлях до програми полягає в argv[0]тому, щоб програма могла отримати файли конфігурації тощо зі свого каталогу інсталяції.
Без цього було б неможливо argv[0].


2
Це не особливо вдале пояснення - немає причин, щоб ми не могли стандартизувати щось на зразок, (char *path_to_program, char **argv, int argc)наприклад,
moopet

AFAIK, більшість програм тягнути конфігурації з стандартного розташування ( ~/.<program>, /etc/<program, $XDG_CONFIG_HOME) і або прийняти параметр , щоб змінити його або мати опцію під час компіляції , що пече в постійній до двійковій системі .
Xiong Chiamiov

0

ccache поводиться таким чином, щоб імітувати різні виклики до бінарних файлів компілятора. ccache - це кеш-компіляція - вся справа в тому, щоб ніколи не збирати один і той же вихідний код двічі, а замість цього повертати об’єктний код з кешу.

На сторінці man ccache "є два способи використання ccache. Ви можете або префіксувати свої команди компіляції ccache, або ви можете дозволити ccache маскуватися як компілятор, створивши символічне посилання (назване як компілятор) до cacache. Перший метод найзручніший, якщо ви просто хочете спробувати ccache або хочете використовувати його для деяких конкретних проектів. Другий метод найбільш корисний, коли ви хочете використовувати ccache для всіх своїх компіляцій. "

Метод символьних посилань включає виконання цих команд:

cp ccache /usr/local/bin/
ln -s ccache /usr/local/bin/gcc
ln -s ccache /usr/local/bin/g++
ln -s ccache /usr/local/bin/cc
ln -s ccache /usr/local/bin/c++
... etc ...

... ефект якого полягає в тому, щоб дозволити ccache обробляти будь-які команди, які інакше перейшли б до компіляторів, таким чином дозволяючи ccache повернути кешований файл або передати команду фактичному компілятору.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.