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

27

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

Update:

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

    
задан precise 06.10.2014 в 13:23
источник

4 ответа

43

Команда 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
    
ответ дан Volker Siegel 06.10.2014 в 14:11
28

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

  

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

     
  1. Если имя команды не содержит косых черт, произойдет первый успешный шаг в следующей последовательности:
      ...      
    • Если имя команды совпадает с именем утилиты, указанной в следующей таблице, эта утилита должна быть вызвана.   ...
      cd
        ...
    •   
    • В противном случае необходимо выполнить поиск команды с помощью PATH ...
    •   
  2.   

В то время как это явно не говорит о том, что он должен быть встроенным, в описании указывается, что в описание cd :

  

Так как cd влияет на текущую среду выполнения оболочки, она всегда предоставляется как встроенная оболочка.

Из руководства bash :

  

Следующие команды, встроенные в оболочку, наследуются от оболочки Bourne. Эти команды реализованы, как указано в стандарте POSIX.
  ...

cd
       cd [-L|[-P [-e]]] [directory]

Я полагаю, вы могли бы подумать о архитектуре, где cd не должно быть встроенным. Однако вы должны видеть, что подразумевает встроенный. Если вы пишете специальный код в оболочке, чтобы что-то сделать для какой-либо команды, вы приближаетесь к встроенному. Чем больше вы делаете, тем лучше просто иметь встроенный интерфейс.

Например, вы можете заставить оболочку IPC взаимодействовать с подпроцессами, и будет существовать программа cd , которая будет проверять наличие каталога и имеет ли у вас разрешение на доступ, а затем, а затем общается с оболочкой чтобы он изменил свой каталог. Тем не менее, вам нужно будет проверить, является ли процесс, связанный с вами, дочерним (или использовать специальные средства связи только с детьми, такие как специальный файловый дескриптор, разделяемая память и т. Д.), И если процесс на самом деле запуск доверенной программы cd или что-то еще. Это целая банда червей.

Или у вас может быть программа cd , которая делает chdir , и запускает новую оболочку со всеми текущими переменными среды, применяемыми к новой оболочке, а затем завершает свою родительскую оболочку (когда-то). 1

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

Вы обнаружите, что добавляете все больше и больше кода для защиты таких методов, и значительно проще просто сделать его встроенным.

Что-то исполняемый файл не помешает ему быть встроенным. Пример:

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 Shell ( 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 & amp; Linux, которые имеют дело с поведением оболочки. В частности:

  

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

    
ответ дан muru 06.10.2014 в 13:30
8

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

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

    
ответ дан Uwe Plonus 06.10.2014 в 13:26
7

из man which :

  

, который возвращает пути к файлам (или ссылкам), которые будут   выполненных в текущей среде, были приведены аргументы   как команды в строго POSIX-совместимой оболочке. Это делает   поиск PATH для исполняемых файлов, соответствующих именам   аргументы. Это не соответствует символическим ссылкам.

Как видно из описания which , он проверяет только PATH . Поэтому, если вы внедрили некоторый bash function , он ничего не покажет. Лучше использовать команду type вместе с which .

Например, в команде Ubuntu ls aliased на 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.
    
ответ дан c0rp 06.10.2014 в 13:48