cd
є вбудованою оболонкою оболонки POSIX :
Якщо проста команда призводить до імені команди та необов'язкового списку аргументів, слід виконати такі дії:
- Якщо ім'я команди не містить косої риски, повинен відбутися перший успішний крок у наступній послідовності:
...
- Якщо ім'я команди відповідає імені утиліти, зазначеному в наступній таблиці, ця утиліта буде викликана.
...
cd
...
- В іншому випадку команду слід шукати за допомогою PATH ...
Хоча це прямо не говорить про те, що він повинен бути вбудованим, специфікація продовжує говорити в описіcd
:
Оскільки cd впливає на поточне середовище виконання оболонки, він завжди надається як регулярний вбудований оболонку.
З bash
посібника :
Наступні вбудовані команди оболонки успадковуються від оболонки Борна. Ці команди реалізуються, як визначено стандартом POSIX.
...
cd
cd [-L|[-P [-e]]] [directory]
Я гадаю, ви можете придумати архітектуру, де cd
не повинно бути вбудованим. Однак ви повинні побачити, що означає вбудований модуль. Якщо ви пишете спеціальний код в оболонці, щоб зробити щось для якоїсь команди, ви наближаєтесь до вбудованого. Чим більше ви робите, тим краще просто мати вбудований.
Наприклад, у вас може бути оболонка, яка має IPC для зв'язку з підпроцесами, і там буде cd
програма, яка перевірятиме наявність каталогу і чи є у вас дозвіл на доступ до нього, а потім спілкується з оболонкою, щоб сказати їй змінити її каталог. Однак вам доведеться перевірити, чи процес спілкування з вами є дитиною (або зробити спеціальні засоби спілкування лише з дітьми, наприклад, спеціальний дескриптор файлів, спільна пам'ять тощо), і чи процес насправді є запуск довіреної cd
програми чи чогось іншого. Ось і ціла банка глистів.
Або у вас може бути cd
програма, яка здійснює chdir
системний виклик, і запускає нову оболонку зі всіма поточними змінними середовища, застосовані до нової оболонки, а потім вбиває її батьківську оболонку (якось), коли це завершено. 1
Гірше, що ви навіть можете мати систему, де процес може змінювати середовище інших процесів (я думаю, що технічно ви можете це зробити за допомогою налагоджувачів). Однак така система була б дуже, дуже вразливою.
Ви зможете додавати все більше коду для захисту таких методів, і значно простіше просто зробити його вбудованим.
Те, що щось є виконуваним, не заважає йому бути вбудованим. Справа в точці:
echo
і test
echo
і test
є утилітами ( /bin/echo
і /bin/test
), призначеними POSIX . Але майже кожна популярна оболонка має вбудований echo
і test
. Аналогічно, kill
також вбудований модуль, який доступний як програма. До інших належать:
sleep
(не так часто)
time
false
true
printf
Однак є деякі випадки, коли команда не може бути нічого, крім вбудованого. Одне з таких є cd
. Як правило, якщо повний шлях не вказаний, а ім'я команди збігається з вбудованим, викликається функція, що відповідає цій команді. Залежно від оболонки, поведінка вбудованого та поведінки виконуваного файлу може відрізнятися (особливо це проблемаecho
, яка має диво-різну поведінку . Якщо ви хочете бути певними щодо поведінки, бажано викликати виконуваний файл за допомогою повний шлях і встановити змінні типу POSIXLY_CORRECT
(навіть тоді реальної гарантії немає).
Технічно нічого не заважає вам забезпечити ОС, яка також є оболонкою і має кожну команду як вбудований. Близьким до цього крайнього кінця є монолітний BusyBox . BusyBox - це єдиний двійковий файл, який (залежно від імені, з яким він називається) може вести себе як будь-яка з понад 240 програм , включаючи Almquist Shell ( ash
). Якщо ви скасуєте PATH
під час роботи BusyBox ash
, програми, доступні в BusyBox, все ще доступні для вас, не вказуючи a PATH
. Вони наближаються до вбудованих оболонок, за винятком того, що сама оболонка є свого роду вбудованою в BusyBox.
Якщо ви подивитесь на dash
джерело, потік виконання виглядає приблизно так (звичайно, за допомогою додаткових функцій, коли використовуються труби та інші речі):
main
→ cmdloop
→ evaltree
→evalcommand
evalcommand
потім використовує findcommand
для визначення, що таке команда. Якщо це вбудований, то :
case CMDBUILTIN:
if (spclbltin > 0 || argc == 0) {
poplocalvars(1);
if (execcmd && argc > 1)
listsetvar(varlist.list, VEXPORT);
}
if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) {
if (exception == EXERROR && spclbltin <= 0) {
FORCEINTON;
break;
cmdentry.u.cmd
є struct
( struct builtincmd
), один з членів якої є покажчиком на функцію, з підписом типовою main
: (int, char **)
. Ці evalbltin
виклики функцій ( в залежності від вбудованої , чи є eval
команда чи ні) або evalcmd
, або ця функція покажчик. Фактичні функції визначаються у різних вихідних файлах. echo
Наприклад, це :
int
echocmd(int argc, char **argv)
{
int nonl;
nonl = *++argv ? equal(*argv, "-n") : 0;
argv += nonl;
do {
int c;
if (likely(*argv))
nonl += print_escape_str("%s", NULL, NULL, *argv++);
if (nonl > 0)
break;
c = *argv ? ' ' : '\n';
out1c(c);
} while (*argv);
return 0;
}
Усі посилання на вихідний код у цьому розділі базуються на номері рядка, тому вони можуть змінюватися без попереднього повідомлення.
1 POSIX-системи мають cd
виконуваний файл .
Бічна примітка:
У Unix та Linux є багато чудових публікацій, які стосуються поведінки оболонок. Зокрема:
Якщо ви ще не помітили схему в перелічених питаннях, майже всі вони стосуються Стефана Шазела .
type
командою