Почему команда `which` не работает для` cd`? Я также не могу найти исполняемый файл для `cd`!

30

Я попытался, which cdи он не дал путь, но вместо этого возвратил код выхода 1 (проверено с echo $?). Сам coreutil cdработает, поэтому исполняемый файл должен быть там, верно? Я также запустил findдля cd, но не было показано исполняемый файл. Как это реализовано тогда?

Обновить:

Я не знаю, стоит ли спрашивать об этом в другом посте, но так как я думаю, что это хорошо, я расширяю (?) Пост ... Так что ответ был на самом деле довольно простым, для этого не существует исполняемого файла - потому что он встроенный - Но я обнаружил, что некоторые встроенные функции (оболочка bash в Fedora) имеют исполняемые файлы! Итак, встроенный -> нет исполняемого файла не правильно, я полагаю? Может быть, ответ, объясняющий, что на самом деле представляют собой встроенные функции (встроенные команды?), А здесь дело не в том, чтобы сосредоточиться больше на cd... Некоторые опубликованные ранее хорошие ссылки указывают на то, что встроенные функции не являются программами ... так что же это? Как они работают? Это просто функции или темы оболочки?

точный
источник
1
Прочитайте этот ответ. Предлагается использовать typeкоманду
c0rp
7
Посмотрите эти вопросы и ответы на вопрос, почему cdнужно быть встроенным: почему компакт-диск не является программой? и этот, почему typeлучше, чем which: почему бы не использовать "который"? Что использовать тогда?
Тердон
Подобный вопрос здесь: askubuntu.com/q/613470/178596
Уилф

Ответы:

46

Команда cdне может быть исполняемой

В оболочке cdиспользуется для «перехода в другой каталог» или, более формально, для изменения текущего рабочего каталога (CWD). Это невозможно реализовать как внешнюю команду:

Каталог принадлежит процессу

Текущий рабочий каталог - это каталог, который используется для интерпретации относительных путей для получения полного пути, который можно использовать для доступа к файлам. Относительные пути используются во многих местах, и интерпретация в одном процессе не должна влиять на другой процесс.
По этой причине каждый процесс имеет свой текущий рабочий каталог.

cdнапример, об изменении текущего рабочего каталога процесса оболочки bash.

Если бы это была внешняя команда, исполняемый файл в пути, выполняющий этот исполняемый файл, создал бы процесс со своим собственным рабочим каталогом, не влияя на тот из текущей оболочки. Даже если внешняя команда изменит свой каталог, это изменение исчезнет при выходе из внешнего процесса.

Встроенные команды оболочки

Поэтому нет смысла запускать внешнюю команду для задачи cd. Команде cdнеобходимо применить изменения к текущему процессу оболочки.

Для этого это «встроенная команда» оболочки.

Встроенные команды - это команды, которые ведут себя подобно внешним командам, но реализованы в оболочке (поэтому они cdне являются частью coreutils). Это позволяет команде изменить состояние самой оболочки, в этом случае вызвать chdir()see (см. 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
Volker Siegel
источник
1
Отличное объяснение!
SaltyNuts
3
Намного лучше, чем принятый в настоящее время ответ - это объясняет, почему cd встроена оболочка.
Лили Чунг
28

cdэто встроенная в POSIX оболочка:

Если простая команда приводит к имени команды и необязательному списку аргументов, должны быть выполнены следующие действия:

  1. Если имя команды не содержит косых черт, должен произойти первый успешный шаг в следующей последовательности:
    ...
    • Если имя команды совпадает с именем утилиты, указанной в следующей таблице, эта утилита должна быть вызвана.
      ...
      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источник, поток выполнения выглядит примерно так (конечно, с дополнительными функциями, которые используются при использовании каналов и других вещей):

maincmdloopevaltreeevalcommand

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 есть много отличных постов, посвященных поведению оболочки. Особенно:

Если вы не заметили закономерности в перечисленных вопросах, почти все из них касаются Стефана Шазеля .

Мур
источник
4
Обратите внимание, что вы можете получить cdтекст справки с помощью help cd(то же самое для всех встроенных команд оболочки)
Сильвен Пино,
@SylvainPineau, хотя я и ссылаюсь на руководство по bash, этот совет обычно не применим к другим оболочкам, таким как zsh.
Муру
Действительно help, это встроенный bash (для zsh, это run-help cd)
Сильвен Пино,
Связанное описание из спецификации POSIX явно не говорит о том, что оно cdдолжно быть встроено в оболочку ... но основано на том, как свойства процесса и их передача работают в UNIX cdкак встроенная оболочка, является единственной простой реализацией. Смотрите ответ Volker Siegel .
Пабук
@pabouk действительно (он называет это утилитой), а затем продолжает: «Поскольку cd влияет на текущую среду выполнения оболочки, он всегда предоставляется как обычная встроенная оболочка».
Муру
8

Вы не можете найти исполняемый файл для, cdпотому что нет ни одного.

cdэто внутренняя команда вашей оболочки (например bash).

Уве Плонус
источник
7

от 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
       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.
c0rp
источник
2
Ммммmanwhich .
IQAndreas
1
Возможно, следует подчеркнуть больше: не используйте which, используйте type.
tripleee