Чому б команда `what` не працювала для` cd`? Я не можу знайти виконувану програму `cd`!


30

Я спробував, which cdі він не дав шлях, але натомість повернув вихідний код 1 (перевірено echo $?). Сам Coreutil cdпрацює, тому виконуваний файл повинен бути там, правда? Я також працював findза cd, але не було показано виконуваного файлу. Як це реалізується тоді?

Оновлення:

Я не знаю, чи варто запитати це в іншій посаді, але оскільки я вважаю, що це добре тут, я розширюю (?) Посаду ... Тож відповідь насправді була досить простою, для цього немає виконуваного - тому що a вбудований - Але я знайшов деякі вбудовані файли (bash shell у Fedora) мають виконувані файли! Отже, вбудований -> жоден виконуваний файл не прав? Можливо, відповідь, що пояснює, що насправді є вбудованими (вбудовані команди?), Що насправді тут питання, а не зосереджуватися більше на cd... Деякі хороші посилання, розміщені раніше, вказують на те, що вбудовані програми не є програмами ... так що ж вони? Як вони працюють? Це просто функції або нитки оболонки?


1
Прочитайте цю відповідь. Пропонується скористатись typeкомандою
c0rp

7
Дивіться це питання і запитання про те, чому cdпотрібно бути вбудованим: Чому CD не є програмою? і цей на чому typeкраще, ніж which: Чому б не використовувати "який"? Що тоді використовувати?
тердон

Подібне запитання тут: askubuntu.com/q/613470/178596
Вільф

Відповіді:


46

Команда cdне може бути виконуваною

В оболонці cdвикористовується для "переходу в інший каталог", або більш формально, для зміни робочого каталогу каталізаторів (CWD). Реалізувати це як зовнішню команду неможливо:

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

Довідник, який працює, - це каталог, який використовується для інтерпретації відносних шляхів для отримання повного шляху, який можна використовувати для доступу до файлів. Відносні шляхи використовуються в багатьох місцях, і інтерпретація в одному процесі не повинна впливати на інший процес.
З цієї причини кожен процес має власний поточний робочий каталог.

cdстосується зміни, наприклад, поточної робочої директорії процесу оболонки bash.

Якби це була зовнішня команда, виконуваний у конвеєрі виконаний виконуваний файл, створив би процес із власною робочою директорією, не впливаючи на поточну оболонку. Навіть якщо зовнішня команда змінила б її каталог, ця зміна відпадає, коли зовнішній процес закінчується.

Shell вбудовані команди

Тому немає сенсу запускати зовнішню команду для виконання завдання cd. Команді cdпотрібно застосувати зміну до поточно запущеного процесу оболонки.

Для цього це "вбудована команда" оболонки.

Команди Builtin - це команди, які поводяться аналогічно зовнішнім командам, але реалізуються в оболонці (тому 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

1
Чудове пояснення!
SaltyNuts

3
Набагато краще, ніж зараз прийнята відповідь - це пояснює, чому cd вбудована оболонка.
Лілі Чунг

28

cdє вбудованою оболонкою оболонки POSIX :

Якщо проста команда призводить до імені команди та необов'язкового списку аргументів, слід виконати такі дії:

  1. Якщо ім'я команди не містить косої риски, повинен відбутися перший успішний крок у наступній послідовності:
    ...
    • Якщо ім'я команди відповідає імені утиліти, зазначеному в наступній таблиці, ця утиліта буде викликана.
      ...
      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.


Тематичний випадок: оболонка алхівіста Debian ( 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(те саме для всіх команд, вбудованих в оболонки)
Sylvain Pineau

@SylvainPineau, незважаючи на те, що я посилався на посібник з bash, ці поради, як правило, не застосовуються до інших оболонок, наприклад, zsh.
муру

Дійсно help, це вбудований баш (для zsh, це run-help cd)
Sylvain Pineau

Пов'язаний опис із специфікації POSIX прямо не говорить про те, що він cdповинен бути вбудованим у оболонку ..., але виходячи з того, як властивості процесу та їх передача працюють у UNIX cdяк вбудована оболонка, є єдиною реальною реалізацією. Дивіться відповідь Волкера Зігеля .
пабук

@pabouk дійсно (це називає це утиліта), а потім продовжує говорити: "Оскільки CD впливає на поточне середовище виконання оболонки, він завжди надається як регулярний вбудований оболонку".
муру

8

Ви не можете знайти виконуваний файл, cdоскільки його немає.

cd- це внутрішня команда вашої оболонки (наприклад bash).


7

від man which:

який повертає назви файлів (або посилань), які були б виконані в поточному середовищі, якби його аргументи були задані як команди в строго відповідній оболонці POSIX. Це робиться шляхом пошуку в PATH виконуваних файлів, що відповідають назвам аргументів. Він не слідує символічних посилань.

Як ми бачимо з опису which, це лише перевірка PATH. Тож якщо ви їх реалізували bash function, це нічого не покаже. Краще використовувати typeкоманду разом з which.

Наприклад, в lsкоманді Ubuntu, псевдонімом до 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. Всі вбудовані баші, описані 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.


1
Можливо, варто підкреслити більше: Не використовуйте which, не використовуйте type.
tripleee
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.