Dlaczego polecenie `które` nie działa dla` cd`? Nie mogę też znaleźć pliku wykonywalnego dla `cd`!

30

Próbowałem which cdi nie podałem ścieżki, ale zamiast tego zwróciłem kod wyjścia 1 (zaznaczony za pomocą echo $?). Sam coreutil cddziała, więc plik wykonywalny powinien tam być, prawda? Uruchomiłem również findfor cd, ale nie pokazano pliku wykonywalnego. Jak to jest wtedy realizowane?

Aktualizacja:

Nie wiem, czy powinienem o to zapytać w innym poście, ale ponieważ myślę, że tutaj jest dobrze, rozwijam (?) Ten post ... Więc odpowiedź była w rzeczywistości dość prosta, nie można tego wykonać - ponieważ to jest Wbudowane - Ale znalazłem, że niektóre wbudowane (powłoka bash w Fedorze) mają pliki wykonywalne! Więc wbudowane -> żaden plik wykonywalny nie jest poprawny, jak sądzę? Być może odpowiedź wyjaśniająca, czym właściwie są wbudowane polecenia (wbudowane polecenia?), O co tu chodzi, zamiast skupiać się bardziej na cd... Niektóre dobre linki zamieszczone wcześniej wskazują, że wbudowane programy nie są programami ... więc czym one są? Jak oni pracują? Czy to tylko funkcje czy wątki powłoki?

precyzyjny
źródło
1
Przeczytaj odpowiedź. Zaleca się użycie typepolecenia
c0rp
7
Zapoznaj się z pytaniami i odpowiedziami na pytanie, dlaczego cdmusi być wbudowany: dlaczego cd nie jest programem? a ten, dlaczego typejest lepszy niż which: Dlaczego nie użyć „który”? Czego więc użyć?
terdon
Podobne pytanie tutaj: askubuntu.com/q/613470/178596
Wilf

Odpowiedzi:

46

Polecenie cdnie może być plikiem wykonywalnym

W powłoce cdsłuży do „przejścia do innego katalogu” lub, bardziej formalnie, do zmiany bieżącego katalogu roboczego (CWD). Niemożliwe jest zaimplementowanie tego jako zewnętrznego polecenia:

Katalog należy do procesu

Obecny katalog roboczy to katalog służący do interpretacji ścieżek względnych w celu uzyskania pełnej ścieżki dostępu do plików. W wielu miejscach stosowane są ścieżki względne, a interpretacja w jednym procesie nie powinna wpływać na inny proces.
Z tego powodu każdy proces ma własny bieżący katalog roboczy.

cddotyczy na przykład zmiany bieżącego katalogu roboczego procesu powłoki bash.

Gdyby było to polecenie zewnętrzne, plik wykonywalny na ścieżce, uruchomienie tego pliku stworzyłoby proces z własnym katalogiem roboczym, bez wpływu na proces bieżącej powłoki. Nawet jeśli polecenie zewnętrzne zmieniłoby katalog, zmiana ta zniknie po zakończeniu procesu zewnętrznego.

Polecenia wbudowane w powłokę

Dlatego nie ma sensu uruchamiać zewnętrznego polecenia dla zadania cd. Komendacd musi zastosować zmianę do aktualnie uruchomionego procesu powłoki.

Aby to zrobić, jest to „wbudowane polecenie” powłoki.

Wbudowane polecenia to polecenia, które zachowują się podobnie do poleceń zewnętrznych, ale są zaimplementowane w powłoce (więc cdnie są częścią coreutils). Umożliwia to komendzie zmianę stanu samej powłoki, w tym przypadku wywołanie chdir()zobacz (patrz man 2 chdir);

O which

Teraz odpowiedź na pytanie tytułowe jest łatwa:
komenda wykonywalna whichnie może nam powiedzieć, że cd jest komendą wbudowaną, ponieważ komenda wykonywalna nie wie nic o kompilacjach.

Alternatywny type -a

Alternatywnie whichmożesz użyć type -a; Może zobaczyć wykonywalne polecenia i wbudowane; Dodatkowo widzi aliasy i funkcje - również zaimplementowane w powłoce:

$ 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
źródło
1
Świetne wyjaśnienie!
SaltyNuts,
3
Znacznie lepiej niż obecnie akceptowana odpowiedź - to wyjaśnia, dlaczego cd wbudowana jest powłoka.
Lily Chung,
28

cdjest wbudowaną powłoką z mandatem POSIX :

Jeśli proste polecenie daje nazwę polecenia i opcjonalną listę argumentów, należy wykonać następujące czynności:

  1. Jeśli nazwa polecenia nie zawiera żadnych ukośników, nastąpi pierwszy udany krok w następującej kolejności:
    ...
    • Jeśli nazwa polecenia jest zgodna z nazwą narzędzia wymienionego w poniższej tabeli, narzędzie to należy wywołać.
      ...
      cd
      ...
    • W przeciwnym razie polecenie należy wyszukać za pomocą ŚCIEŻKI ...

Chociaż nie mówi to wyraźnie, że musi to być wbudowany, specyfikacja mówi dalej w opisiecd :

Ponieważ cd wpływa na bieżące środowisko wykonywania powłoki, zawsze jest udostępniany jako zwykła wbudowana powłoka.

Z bashinstrukcji :

Następujące polecenia wbudowane w powłokę są dziedziczone z powłoki Bourne'a. Te polecenia są implementowane zgodnie ze standardem POSIX.
...

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

Podejrzewam, że można pomyśleć o architekturze, w której cdnie musi być wbudowany. Musisz jednak zobaczyć, co oznacza wbudowany. Jeśli napiszesz specjalny kod w powłoce, aby zrobić coś dla jakiegoś polecenia, zbliżasz się do wbudowania. Im więcej robisz, tym lepiej jest mieć wbudowaną funkcję.

Na przykład, możesz mieć powłokę z IPC do komunikowania się z podprocesami, i byłoby cd program, który sprawdzałby istnienie katalogu i czy masz uprawnienia dostępu i to, a następnie komunikowałby się z powłoką, aby nakazał jej zmianę informator. Będziesz jednak musiał sprawdzić, czy proces komunikowania się z tobą jest dzieckiem (lub zastosuj specjalne środki komunikacji tylko z dziećmi, takie jak specjalny deskryptor pliku, pamięć współdzielona itp.) I czy proces jest w rzeczywistości uruchamianie zaufanego cdprogramu lub czegoś innego. To cała puszka robaków.

Lub możesz mieć cdprogram, który wywołuje chdirsystem, i uruchamia nową powłokę ze wszystkimi bieżącymi zmiennymi środowiskowymi zastosowanymi do nowej powłoki, a następnie zabija swoją powłokę macierzystą (jakoś) po zakończeniu.1

Co gorsza, możesz nawet mieć system, w którym proces może zmieniać środowiska innych procesów (myślę, że technicznie można to zrobić za pomocą debuggerów). Taki system byłby jednak bardzo, bardzo wrażliwy.

Przekonasz się, że dodajesz coraz więcej kodu, aby zabezpieczyć takie metody, i znacznie łatwiej jest po prostu zrobić to wbudowanym.


To, że coś jest plikiem wykonywalnym, nie uniemożliwia jego wbudowania. Przykładem:

echo i test

echoi testsą narzędziami wymaganymi przez POSIX ( /bin/echoi /bin/test). Jednak prawie każda popularna powłoka ma wbudowane echoi test. Podobnie killjest również wbudowany, który jest dostępny jako program. Inne obejmują:

  • sleep (nie tak często)
  • time
  • false
  • true
  • printf

Są jednak przypadki, w których polecenie nie może być niczym innym jak wbudowanym. Jednym z nich jest cd. Zazwyczaj, jeśli pełna ścieżka nie jest określona, ​​a nazwa polecenia odpowiada nazwie wbudowanego, wywoływana jest funkcja dostosowana do tego polecenia. W zależności od powłoki zachowanie wbudowanego i pliku wykonywalnego może się różnić (jest to szczególnie problemecho , ponieważ ma on bardzo różne zachowania . Jeśli chcesz być pewien zachowania, lepiej jest wywołać plik wykonywalny przy użyciu pełną ścieżkę i ustaw zmienne takie jakPOSIXLY_CORRECT (nawet wtedy nie ma prawdziwej gwarancji).

Technicznie nic nie stoi na przeszkodzie, aby zapewnić system operacyjny, który jest również powłoką i ma wszystkie polecenia jako wbudowane. Blisko tego skrajnego końca jest monolityczny BusyBox . BusyBox to pojedynczy plik binarny, który (w zależności od nazwy, z którą jest wywoływany) może zachowywać się jak dowolny z ponad 240 programów , w tym Almquist Shell ( ash). Jeśli rozłączysz się PATHpodczas uruchamiania BusyBox ash, programy dostępne w BusyBox są nadal dostępne bez określania PATH. Zbliżają się do wbudowania powłoki, z wyjątkiem tego, że sama powłoka jest rodzajem wbudowania w BusyBox.


Studium przypadku: The Debian Almquist Shell ( dash)

Jeśli spojrzysz na dashźródło, wątek wykonania jest mniej więcej taki (oczywiście z dodatkowymi funkcjami związanymi z użyciem potoków i innych rzeczy):

maincmdloopevaltreeevalcommand

evalcommandnastępnie używa findcommanddo ustalenia, czym jest polecenie. Jeśli jest wbudowany, to :

 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.cmdjest struct( struct builtincmd), którego jednym z członków jest wskaźnik funkcji, z podpisem typowy main: (int, char **). Te evalbltinwywołania funkcji (w zależności od tego, czy wbudowany jest evalkomenda lub nie) albo evalcmd, czy funkcja ta wskazówka. Rzeczywiste funkcje są zdefiniowane w różnych plikach źródłowych. echo, na przykład jest :

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;
}

Wszystkie łącza do kodu źródłowego w tej sekcji są oparte na numerach wierszy, więc mogą ulec zmianie bez powiadomienia.


1 Systemy POSIX mają cdplik wykonywalny .


Dygresja:

Istnieje wiele doskonałych postów w systemach Unix i Linux, które dotyczą zachowania powłoki. W szczególności:

Jeśli do tej pory nie zauważyłeś wzoru, prawie wszystkie z nich dotyczą Stéphane Chazelas .

muru
źródło
4
Zauważ, że możesz otrzymać cdtekst pomocy help cd(to samo dla wszystkich poleceń wbudowanych w powłokę)
Sylvain Pineau
@SylvainPineau, mimo że podłączyłem się do podręcznika bash, ta rada nie ma ogólnie zastosowania do innych powłok, takich jak zsh.
muru
Rzeczywiście helpjest to wbudowane bash (dla zsh, to run-help cd)
Sylvain Pineau
Połączony opis ze specyfikacji POSIX nie mówi wprost, że cdmusi być tak wbudowany w powłokę ... ale oparty na tym, jak właściwości procesu i ich przesyłanie działają w systemie UNIX cdjako wbudowana powłoka, jest jedyną prostą implementacją. Zobacz odpowiedź Volkera Siegela .
pabouk
@ pabouk rzeczywiście (nazywa to narzędziem), a następnie mówi: „Ponieważ cd wpływa na bieżące środowisko wykonywania powłoki, zawsze jest udostępniany jako zwykła wbudowana powłoka”.
muru
8

Nie można znaleźć pliku wykonywalnego, cdponieważ go nie ma.

cdjest wewnętrznym poleceniem twojej powłoki (np bash.).

Uwe Plonus
źródło
7

z man which:

który zwraca ścieżki do plików (lub linków), które byłyby wykonywane w bieżącym środowisku, gdyby jego argumenty zostały podane jako polecenia w powłoce ściśle zgodnej z POSIX. Robi to, przeszukując PATH w poszukiwaniu plików wykonywalnych pasujących do nazw argumentów. Nie podąża za symbolicznymi linkami.

Jak widać z opisu which, to tylko sprawdzenie PATH. Więc jeśli wdrożysz jakieś bash function, nic ci to nie pokaże. Lepiej używać typepolecenia razem z which.

Na przykład w lspoleceniu Ubuntu aliasowanym do ls --color=auto.

$ type ls
ls is aliased to `ls --color=auto'

$ which ls
/bin/ls

A jeśli zaimplementujesz funkcję testową hello:

$ function hello() { for i in {1,2,3}; do echo Hello $i;done }
$ which hello

whichnic nie pokazuje. Ale type:

$ type hello
hello is a function
hello () 
{ 
    for i in {1,2,3};
    do
        echo Hello $i;
    done
}

W Twoim przypadku:

$ type cd
cd is a shell builtin

Oznacza to, że cdjest wbudowaną powłoką , jest w środku bash. Wszystkie wbudowane bash opisane man bashw sekcji KOMENDY WBUDOWANE SHELL

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
źródło
2
Mmmm,manwhich .
IQAndreas
1
Być może należy to bardziej podkreślić: nie używaj which, używaj type.
tripleee