cd
это встроенная в POSIX оболочка:
Если простая команда приводит к имени команды и необязательному списку аргументов, должны быть выполнены следующие действия:
- Если имя команды не содержит косых черт, должен произойти первый успешный шаг в следующей последовательности:
...
- Если имя команды совпадает с именем утилиты, указанной в следующей таблице, эта утилита должна быть вызвана.
...
cd
...
- В противном случае команда должна быть найдена с использованием PATH ...
Хотя это явно не говорит о том, что оно должно быть встроенным, спецификация продолжает в описанииcd
:
Поскольку cd влияет на текущую среду выполнения оболочки, он всегда предоставляется как обычная встроенная оболочка.
Из bash
руководства :
Следующие встроенные команды оболочки унаследованы от Bourne Shell. Эти команды реализованы в соответствии со стандартом POSIX.
...
cd
cd [-L|[-P [-e]]] [directory]
Полагаю, вы могли бы подумать об архитектуре, в которой cd
не обязательно быть встроенной. Тем не менее, вы должны увидеть, что означает встроенный. Если вы напишите специальный код в оболочке, чтобы сделать что-то для какой-то команды, вы почти наверняка станете встроенным. Чем больше вы делаете, тем лучше иметь встроенную функцию.
Например, у вас может быть оболочка с IPC для связи с подпроцессами, и будет cd
программа, которая проверит наличие каталога, есть ли у вас разрешение на доступ и он, а затем свяжется с оболочкой, чтобы сообщить ей об изменении ее каталог. Однако вам нужно будет проверить, является ли процесс, общающийся с вами, дочерним (или создать специальные средства связи только с дочерними элементами, такие как специальный файловый дескриптор, разделяемая память и т. Д.), И действительно ли процесс запуск доверенной cd
программы или что-то еще. Это целая банка червей.
Или у вас может быть cd
программа, которая выполняет chdir
системный вызов, и запускает новую оболочку со всеми текущими переменными среды, примененными к новой оболочке, а затем убивает свою родительскую оболочку (каким-то образом), когда это делается. 1
Хуже того, у вас может быть даже система, в которой процесс может изменять окружение других процессов (я думаю, что технически вы можете сделать это с помощью отладчиков). Однако такая система будет очень, очень уязвимой.
Вы обнаружите, что добавляете все больше и больше кода для защиты таких методов, и значительно проще просто сделать его встроенным.
То, что что-то является исполняемым файлом, не мешает ему быть встроенным. Дело в точке:
echo
а также test
echo
и test
являются утвержденными POSIX утилитами ( /bin/echo
и /bin/test
). Тем не менее , почти каждый популярный корпус имеет встроенную команду echo
и test
. Точно так kill
же встроен в программу. Другие включают в себя:
sleep
(не так часто)
time
false
true
printf
Однако в некоторых случаях команда не может быть чем-либо, кроме встроенного. Одним из них является cd
. Как правило, если полный путь не указан, а имя команды совпадает с именем встроенной, вызывается функция, подходящая для этой команды. В зависимости от оболочки поведение встроенной функции и исполняемого файла может различаться (это особенно проблема, дляecho
которой характерно дикое поведение . Если вы хотите быть уверены в поведении, предпочтительно вызывать исполняемый файл с использованием полный путь, и установить переменные, как POSIXLY_CORRECT
(даже тогда нет никакой реальной гарантии).
Технически ничто не мешает вам предоставить ОС, которая также является оболочкой и имеет каждую команду как встроенную. Близко к этому крайнему концу монолитный BusyBox . BusyBox - это отдельный двоичный файл, который (в зависимости от имени, с которым он вызывается) может вести себя как любая из более чем 240 программ , включая Almquist Shell ( ash
). Если вы отключили PATH
во время работы BusyBox ash
, программы, доступные в BusyBox, по-прежнему доступны без указания 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
команду