Чому моя програма під назвою "set" не виконується?


10

Я створив просту програму на C:

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

    if (argc != 5) {
       fputs("Not enough arguments!\n", stderr);
       exit(EXIT_FAILURE);
    }

І я змінив PATH в etc / bash.bashrc так:

PATH=.:$PATH

Я зберегла цю програму як set.c і збираю її

gcc -o set set.c

у папці

~/Programming/so

Однак, коли я дзвоню

set 2 3

нічого не відбувається. Немає тексту, який з’являється.

Дзвінок

./set 2 3

дає очікуваний результат

Я ніколи не мав проблем з PATH раніше і

which set

повертає ./set. Тож, здається, ПАТ є правильним. Що відбувається?


10
Відносно небезпечно додати "." до вашої ПАТИ. Краще просто використовувати ./, виконуючи щось із локального каталогу, або переміщуючи виконуваний файл у добре відомий каталог, наприклад ~ / bin /
— TREE

7
Це також погана ідея викликати свою програму тестування testз тієї ж причини; testтакож вбудована оболонка.
— Джонатан Леффлер

@JonathanLeffler І все ж для швидких тестів виклик програми, testмабуть, має сенс. Звичайно, до моменту, коли ви вкладете його у себе, PATHви дійсно повинні були придумати іншу назву. І поки ви не введете програму, PATHвам доведеться викликати її як завгодно ./test. Тому начебто нормально використовувати назву testдля програми, якщо це швидкий тест, який ви збираєтесь видалити до кінця дня.
— kasperd

1
@kasperd: Наскільки мені відомо, умовна назва програми для швидкого тестування foo.
— hmakholm залишився над Монікою

Якщо ви дасте ім'я, lsто, коли ви переходите, щоб побачити, чи він існує, він запуститься (але тільки якщо ви змінили свій шлях, як ви робили в питанні).
— ctrl-alt-delor

Відповіді:


24

Замість використання which, яке не працює, коли вам це найбільше потрібно , використовуйте typeдля визначення того, що буде працювати під час введення команди:

$ which set
./set
$ type set
set is a shell builtin

Оболонка завжди шукає вбудовані елементи, перш ніж шукати $PATH, тому налаштування $PATHтут не допомагає.

Найкраще було б перейменувати свій виконуваний файл на щось інше, але якщо ваше завдання вимагає назви програми set, ви можете використовувати функцію оболонки:

$ function set { ./set; }
$ type set
set is a function
set ()
{
    ./set
}

(Це працює bash, але інші оболонки на кшталт цього kshможуть не допустити. Дивіться відповідь mikeserv для більш портативного рішення.)

Тепер введення setзапустить функцію з назвою "set", яка виконується ./set. GNU bashшукає функції, перш ніж шукати вбудовані, і шукає вбудовані, перш ніж шукати $PATH. Розділ з назвою "КОМАНДА ВИКОНАННЯ" на сторінці bash man дає більше інформації про це.

Дивіться також документацію щодо builtinта command: help builtinта help command.


3
Ви рекомендуєте typeбільше which, але не вказуйте причин. ( Я знаю чому , але хтось, хто потребує рекомендації, не хотів.)
— cjm

1
@cjm Ось цілий трактат про те, чому б не : unix.stackexchange.com/questions/85249/…
— Ентоні

Дуже інформативні. Ви ніколи не здогадаєтесь, що існує стільки суперечок щодо такої, здавалося б, простої команди, яка виконує просте завдання
— Ганея Дан Андрей

4
@GaneaDanAndrei В основному використовуйте typeзамість which, не називайте свою програму "встановленою", і розумійте, що function set { ./set; }це некрасивий хакер, якого ви, ймовірно, повинні уникати.
— Yellowantphil

11

setце вбудований в баш (і, мабуть, більшість інших раковин). Це означає, що bash навіть не шукатиме шлях при пошуку функції.

В якості побічного зауваження я б настійно радив не додавати .на шлях з міркувань безпеки. Уявіть, наприклад, cdколись /tmpпісля того, як будь-який інший користувач додав виконуваний файл /tmp/cd.


2
Так, це дурна ідея, що мій вчитель нав'язує нас, щоб "не виглядати непрофесійно, представляючи програму". Він оцінює вас, якщо цього не зробити.
— Ганея Ден Андрій


4
cdце вбудована оболонка, тому приклад не працюватиме з тієї ж причини, що і з set.
— Еміль Єржабек

15
Використання ./fooдля виклику програми є професійним; це показує, що ви розумієте, чому .не слід бути в $ PATH. Ваш вчитель помиляється, і ви можете сказати йому, що я це сказав.
— zwol

10

setце не просто вбудований, а спеціальний вбудований POSIX . Існує кілька вбудованих команд, які визначені стандартами, щоб їх можна було знайти в пошуку команд перед чим-небудь іншим - $PATHне виконується пошук, імена функцій не шукаються тощо. Більшість вбудованих, які не є спеціальними , фактично вимагаються стандартом POSIX для знайдений у вашому $PATH до того, як оболонка запустить будь-яку власну вбудовану процедуру. Це відноситься echoі більшість інших (хоча чи стандарт шануються в цьому відношенні був предметом спору у відкритих списках розсилки в минулому) , але НЕ з set, trap, break,return, continue, ., :, times, eval, exit, export, readonly, unset, Або exec.

Усі вони є зарезервованими іменами оболонки, і вони мають спеціальні атрибути, окрім їх порядку уподобань для пошуку команд. Наприклад, ви не можете визначити функцію оболонки з будь-яким із цих імен в оболонці, сумісної зі стандартами. Це гарна річ - вона дає можливість людям безпечно писати портативні сценарії . Це базові команди, з яких досвідчений сценарист може встановити надійне та надійне опору у своєму оточенні. Вторгаючись це простір імен є НЕ рекомендується.

Однак якщо ви хочете вторгнутись у нього, ви можете зробити це портативно alias. Порядок розширення оболонки дозволяє це вирішення. Оскільки aliasрозширено, коли команда читається, те, що ви заміните setім'я у своєму визначенні, розшириться правильно, воно, ймовірно, не повинно поширюватися на одне з цих імен.

Отже, ви можете зробити:

alias set=./set

... що буде добре працювати.


3

Проблема полягає в тому set, що вбудована оболонка, і найкращим рішенням було б використовувати іншу назву для вашої виконуваної програми.

Між іншим, минулого тижня я поставив запитання про те, як запускати системні команди замість вбудованих оболонок з однойменною назвою, і рішення, яке я прийняв, було запустити команду через env:

env set 2 3

У цьому конкретному випадку, коли ви вже знаєте, що команда, яку ви хочете використовувати, знаходиться у вашому поточному каталозі, було б краще безпосередньо запустити виконуваний файл, ввівши його шлях (використовуючи .для представлення поточної робочої директорії):

./set 2 3

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

Пропозиції, такі як використання commandвбудованого, не працюватимуть у Bash: це лише запобігає запуску функцій оболонки . Хоча це не задокументовано, я також помітив, що використання commandтакож пригнічує ключові слова оболонки . Однак це не зробить те ж саме для вбудованих оболонок , як set. Як я розумію, commandможе працювати з іншими оболонками, такими як zsh.

Крім того , прийоми , такі як \setабо "set"чи 'set'не працювати на Bash вбудованих команд - хоча вони корисні для запуску виконуваних файлів замість псевдонімів або оболонки ключових слів .

Примітка. Спочатку ця відповідь почалася як коментар до відповіді Еріка, але стала занадто великою, щоб вписатись у коментар. Інші відповіді, що рекомендують використовувати typeта не додавати .до PATH, є хорошими.

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