Почему команда `which` не работает для`cd`? Я также не могу найти исполняемый файл для `cd`!
Я старался which cd
и он не дал путь, но вместо этого возвратил код выхода 1 (проверено с echo $?
). Coreutil cd
сам работает, поэтому исполняемый файл должен быть там, верно? Я также побежал find
за cd
, но не был показан исполняемый файл. Как это реализовано тогда?
Обновить:
Я не знаю, стоит ли спрашивать об этом в другом посте, но так как я думаю, что это хорошо, я расширяю (?) Пост... Так что ответ был на самом деле довольно простым, для этого не существует исполняемого файла - потому что он встроенный - Но я обнаружил, что некоторые встроенные функции (оболочка bash в Fedora) имеют исполняемые файлы! Итак, встроенный -> нет исполняемого файла не правильно, я полагаю? Может быть, ответ, объясняющий, что на самом деле представляют собой встроенные функции (встроенные команды?), А на самом деле здесь дело не в том, чтобы сосредоточиться на cd
... Некоторые хорошие ссылки, опубликованные ранее, указывают, что встроенные программы не являются программами... так что же это? Как они работают? Это просто функции или темы оболочки?
4 ответа
команда cd
не может быть исполняемым
В оболочке, cd
используется для "перехода в другой каталог" или, более формально, для изменения текущего рабочего каталога (CWD). Это невозможно реализовать как внешнюю команду:
Каталог принадлежит процессу
Текущий рабочий каталог - это каталог, который используется для интерпретации относительных путей для получения полного пути, который можно использовать для доступа к файлам. Относительные пути используются во многих местах, и интерпретация в одном процессе не должна влиять на другой процесс.
По этой причине каждый процесс имеет свой текущий рабочий каталог.
cd
например, об изменении текущего рабочего каталога процесса оболочки bash
,
Если бы это была внешняя команда, исполняемый файл в пути, выполняющий этот исполняемый файл, создал бы процесс со своим собственным рабочим каталогом, не влияя на тот из текущей оболочки. Даже если внешняя команда изменит свой каталог, это изменение исчезнет при выходе из внешнего процесса.
Встроенные команды оболочки
Так что нет смысла запускать внешнюю команду для задачи cd
, Команда cd
необходимо применить изменение к текущему запущенному процессу оболочки.
Для этого это "встроенная команда" оболочки.
Встроенные команды - это команды, которые ведут себя подобно внешним командам, но реализованы в оболочке (поэтому cd
не является частью coreutils). Это позволяет команде изменить состояние самой оболочки, в этом случае вызвать chdir()
вижу-вижу man 2 chdir
);
Около which
Теперь, ответ на заглавный вопрос прост:
Исполняемая команда which
не может сказать нам, что cd является встроенной командой, потому что исполняемая команда ничего не знает о встроенных командах.
альтернатива type -a
В качестве альтернативы which
, ты можешь использовать type -a
; Он может видеть исполняемые команды и встроенные функции; Кроме того, он видит псевдонимы и функции, также реализованные в оболочке:
$ type -a cd
cd is a shell builtin
$ type -a type
type is a shell builtin
$ type -a which
which is /usr/bin/which
which is /bin/which
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.
Пример из практики: Оболочка Debian Almquist ( dash
)
Если вы посмотрите на 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 есть много отличных постов, посвященных поведению оболочки. Особенно:
- Почему printf лучше, чем echo?
- Почему бы не использовать "который"? Что использовать тогда?
- Почему я не могу перенаправить вывод имени пути из одной команды в "cd"?
- Какой смысл
cd
внешняя команда? - Есть ли разница между добавлением пары имя-значение к команде и использованием env в bash?
Если вы не заметили закономерности в перечисленных вопросах, почти все из них касаются Стефана Шазеля.
От man which
:
который возвращает пути к файлам (или ссылкам), которые будут выполняться в текущей среде, если бы его аргументы были заданы в качестве команд в строго POSIX-совместимой оболочке. Это делается путем поиска в PATH исполняемых файлов, соответствующих именам аргументов. Он не следует по символическим ссылкам.
Как мы видим из описания which
это только проверка PATH
, Так что, если вы реализовали некоторые bash function
, это ничего вам не покажет. Лучше использовать type
командовать вместе с which
,
Например в Ubuntu ls
команда с псевдонимом ls --color=auto
,
$ type ls
ls is aliased to `ls --color=auto'
$ which ls
/bin/ls
И если вы реализуете тестовую функцию hello
:
$ function hello() { for i in {1,2,3}; do echo Hello $i;done }
$ which hello
which
ничего не показывает Но type
:
$ type hello
hello is a function
hello ()
{
for i in {1,2,3};
do
echo Hello $i;
done
}
В твоем случае:
$ type cd
cd is a shell builtin
Это означает, что cd
это встроенная оболочка, она внутри bash
, Все встроенные bash описаны в man bash
, в разделе SHELL BUILTIN COMMANDS
SHELL BUILTIN COMMANDS
Unless otherwise noted, each builtin command documented in this section
as accepting options preceded by - accepts -- to signify the end of the
options. The :, true, false, and test builtins do not accept options
and do not treat -- specially. The exit, logout, break, continue, let,
and shift builtins accept and process arguments beginning with - with‐
out requiring --. Other builtins that accept arguments but are not
specified as accepting options interpret arguments beginning with - as
invalid options and require -- to prevent this interpretation.
Вы не можете найти исполняемый файл для cd
потому что нет.
cd
это внутренняя команда вашей оболочки (например, bash
).