Чому моя програма під назвою "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.