Tại sao lệnh `which` không hoạt động cho` cd`? Tôi cũng không thể tìm thấy tệp thực thi cho `cd`!


30

Tôi đã thử which cdvà nó không đưa ra một đường dẫn mà thay vào đó trả về mã thoát 1 (được kiểm tra bằng echo $?). Bản cdthân coreutil đang hoạt động, vì vậy tệp thực thi nên ở đó, phải không? Tôi cũng chạy một findcho cd, nhưng không có tập tin thực thi được hiển thị. Làm thế nào nó được thực hiện sau đó?

Cập nhật:

Tôi không biết tôi có nên hỏi điều này trong một bài đăng khác không nhưng vì tôi nghĩ nó tốt ở đây, nên tôi đang mở rộng (?) Bài viết ... Vì vậy, câu trả lời thực sự khá đơn giản, không có gì có thể thực hiện được - bởi vì nó một nội trang - Nhưng tôi đã tìm thấy một số nội dung (bash shell trong Fedora) có các tệp thi hành! Vì vậy, dựng sẵn -> tôi không thể thực thi được phải không? Có lẽ một câu trả lời giải thích các nội dung thực sự là gì (các lệnh dựng sẵn?), Đây thực sự là vấn đề ở đây, thay vì tập trung nhiều hơn vào cd... Một số liên kết tốt được đăng trước đây chỉ ra rằng các nội dung không phải là chương trình ... vậy chúng là gì? Họ làm việc như thế nào? Có phải chúng chỉ là chức năng hoặc chủ đề của vỏ?


1
Đọc câu trả lời này . Bạn nên sử dụng typelệnh
c0rp

7
Xem phần hỏi đáp này về lý do tại sao cdcần phải là một nội dung: Tại sao cd không phải là một chương trình? và cái này tại sao typetốt hơn which: Tại sao không sử dụng "cái nào"? Dùng gì rồi?
terdon

Câu hỏi tương tự ở đây: askubfox.com/q/613470/178596
Wilf

Câu trả lời:


46

Lệnh cdkhông thể thực thi được

Trong shell, cdđược sử dụng để "đi vào thư mục khác", hoặc chính thức hơn, để thay đổi thư mục làm việc hiện tại (CWD). Không thể thực hiện điều đó như một lệnh bên ngoài:

Thư mục thuộc về một quá trình

Thư mục làm việc curent là thư mục được sử dụng để giải thích các đường dẫn tương đối để có được một đường dẫn đầy đủ có thể được sử dụng để truy cập các tệp. Các đường dẫn tương đối được sử dụng ở nhiều nơi và việc giải thích trong một quy trình sẽ không ảnh hưởng đến quá trình khác.
Vì lý do này, mọi quá trình đều có thư mục làm việc hiện tại của riêng nó.

cdlà về việc thay đổi thư mục làm việc hiện tại của quá trình shell, ví dụ bash.

Nếu đó là một lệnh bên ngoài, một tệp thực thi trong đường dẫn, chạy lệnh thực thi đó sẽ tạo ra một quy trình với thư mục làm việc của chính nó, mà không ảnh hưởng đến lớp vỏ hiện tại. Ngay cả khi lệnh bên ngoài sẽ thay đổi thư mục của nó, thay đổi đó sẽ biến mất khi quá trình bên ngoài thoát.

Các lệnh dựng sẵn của Shell

Vì vậy, nó không có ý nghĩa để chạy một lệnh bên ngoài cho nhiệm vụ cd. Lệnh cdcần áp dụng thay đổi cho quy trình shell hiện đang chạy.

Để làm được điều đó, nó là một lệnh được xây dựng sẵn của vùng vỏ sò.

Các lệnh dựng sẵn là các lệnh hoạt động tương tự như các lệnh bên ngoài, nhưng được triển khai trong trình bao (vì vậy cdkhông phải là một phần của lõi). Điều này cho phép lệnh thay đổi trạng thái của chính shell, trong trường hợp này để gọi chdir()see (see man 2 chdir);

Trong khoảng which

Bây giờ, câu trả lời cho câu hỏi tiêu đề rất dễ:
Lệnh thực thi whichkhông thể cho chúng ta biết rằng cd là lệnh dựng sẵn vì lệnh thực thi không biết gì về nội dung.

Thay thế type -a

Thay thế cho which, bạn có thể sử dụng type -a; Nó có thể thấy các lệnh thực thi và nội trang; Ngoài ra, nó còn thấy các bí danh và chức năng - cũng được triển khai trong trình bao:

$ 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
Giải thích tuyệt vời!
SaltyNuts

3
Tốt hơn nhiều so với câu trả lời hiện được chấp nhận - điều này giải thích tại sao cd vỏ được tích hợp sẵn.
Lily Chung

28

cdlà một vỏ được ủy quyền POSIX :

Nếu một lệnh đơn giản dẫn đến một tên lệnh và một danh sách các đối số tùy chọn, các hành động sau sẽ được thực hiện:

  1. Nếu tên lệnh không chứa bất kỳ dấu gạch chéo nào, bước thành công đầu tiên trong chuỗi sau sẽ xảy ra:
    ...
    • Nếu tên lệnh khớp với tên của một tiện ích được liệt kê trong bảng sau, tiện ích đó sẽ được gọi.
      ...
      cd
      ...
    • Nếu không, lệnh sẽ được tìm kiếm bằng cách sử dụng ...

Mặc dù điều này không nói rõ ràng rằng nó phải được tích hợp sẵn, nhưng thông số kỹ thuật tiếp tục nói, trong phần mô tả vềcd :

Vì cd ảnh hưởng đến môi trường thực thi shell hiện tại, nó luôn được cung cấp dưới dạng shell được tích hợp thường xuyên.

Từ bashhướng dẫn :

Các lệnh dựng sẵn shell sau đây được kế thừa từ Bourne Shell. Các lệnh này được thực hiện theo quy định của tiêu chuẩn POSIX.
...

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

Tôi cho rằng bạn có thể nghĩ về một kiến ​​trúc cdkhông cần phải là một kiến trúc . Tuy nhiên, bạn phải xem những gì tích hợp ngụ ý. Nếu bạn viết mã đặc biệt vào trình bao để thực hiện điều gì đó cho một lệnh nào đó, bạn sắp hoàn thành việc dựng sẵn. Bạn càng làm, tốt hơn là chỉ cần có một nội dung.

Ví dụ, bạn có thể có shell có IPC để giao tiếp với các quy trình con và sẽ có một cdchương trình kiểm tra sự tồn tại của thư mục và liệu bạn có được phép truy cập hay không và sau đó giao tiếp với shell để bảo nó thay đổi danh mục. Tuy nhiên, sau đó bạn sẽ phải kiểm tra xem quy trình giao tiếp với bạn có phải là trẻ em không (hoặc chỉ thực hiện các phương tiện giao tiếp đặc biệt với trẻ em, như mô tả tệp đặc biệt, bộ nhớ dùng chung, v.v.) và nếu thực tế quá trình này chạy cdchương trình đáng tin cậy hoặc một cái gì đó khác. Đó là cả một con giun.

Hoặc bạn có thể có một cdchương trình thực hiện chdircuộc gọi hệ thống và khởi động một shell mới với tất cả các biến môi trường hiện tại được áp dụng cho shell mới, và sau đó giết shell cha của nó (bằng cách nào đó) khi hoàn thành. 1

Tồi tệ hơn, bạn thậm chí có thể có một hệ thống trong đó một quy trình có thể thay đổi môi trường của các quy trình khác (tôi nghĩ về mặt kỹ thuật bạn có thể làm điều này với trình gỡ lỗi). Tuy nhiên, một hệ thống như vậy sẽ rất, rất dễ bị tổn thương.

Bạn sẽ thấy mình thêm ngày càng nhiều mã để bảo mật các phương thức như vậy và đơn giản hơn đáng kể để đơn giản hóa nó thành một nội dung.


Rằng một cái gì đó là một thực thi không ngăn nó trở thành một nội dung. Trường hợp tại điểm:

echotest

echotestlà các tiện ích bắt buộc POSIX ( /bin/echo/bin/test). Tuy nhiên, gần như mọi vỏ phổ biến đều có tích hợp echotest. Tương tự, killcũng được tích hợp sẵn như một chương trình. Những người khác bao gồm:

  • sleep (không phổ biến)
  • time
  • false
  • true
  • printf

Tuy nhiên, có một số trường hợp lệnh không thể là gì ngoài lệnh dựng sẵn. Một trong số đó là cd. Thông thường, nếu đường dẫn đầy đủ không được chỉ định và tên lệnh khớp với tên của nội trang, một hàm phù hợp với lệnh đó được gọi. Tùy thuộc vào vỏ, hành vi của các BUILTIN và của thực thi có thể khác nhau (điều này đặc biệt là vấn đề đốiecho , trong đó có hành vi cực kỳ khác nhau . Nếu bạn muốn chắc chắn về hành vi, nó là thích hợp hơn để gọi thực thi bằng cách sử dụng đường dẫn đầy đủ và đặt các biến như POSIXLY_CORRECT(ngay cả khi đó không có đảm bảo thực sự).

Về mặt kỹ thuật, không có gì ngăn cản bạn cung cấp HĐH cũng là một hệ vỏ và có mọi lệnh dưới dạng dựng sẵn. Gần cuối cùng là BusyBox nguyên khối . BusyBox là một nhị phân duy nhất, mà (tùy thuộc vào tên mà nó được gọi) có thể hoạt động như bất kỳ trong số hơn 240 chương trình , bao gồm cả Almquist Shell ( ash). Nếu bạn không đặt PATHtrong khi chạy BusyBox ash, các chương trình có sẵn trong BusyBox vẫn có thể truy cập được mà bạn không chỉ định a PATH. Chúng gần giống với các phần tử dựng vỏ, ngoại trừ bản thân phần vỏ đó là một dạng dựng sẵn của BusyBox.


Nghiên cứu điển hình: Shell Almquist Shell ( dash)

Nếu bạn nhìn vào dashnguồn, luồng thực thi là như thế này (tất nhiên, với các chức năng bổ sung có liên quan khi đường ống và những thứ khác được sử dụng):

maincmdloopevaltreeevalcommand

evalcommandsau đó sử dụng findcommandđể xác định lệnh là gì Nếu nó là một nội trang, thì :

 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.cmdlà một struct( struct builtincmd), một trong số các thành viên của nó là một con trỏ hàm, với một chữ ký điển hình là main: (int, char **). Các evalbltincuộc gọi chức năng (tùy thuộc vào việc dựng sẵn là evallệnh hay không) hoặc là evalcmd, hoặc con trỏ chức năng này. Các chức năng thực tế được xác định trong các tập tin nguồn khác nhau. echo, ví dụ, là :

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

Tất cả các liên kết đến mã nguồn trong phần này là dựa trên số dòng, vì vậy chúng có thể thay đổi mà không cần thông báo trước.


1 hệ thống POSIX có thể cdthực thi được .


Lưu ý bên:

Có rất nhiều bài viết xuất sắc trên Unix & Linux liên quan đến hành vi shell. Đặc biệt:

Nếu bạn chưa nhận thấy một mô hình trong các câu hỏi được liệt kê cho đến nay, gần như tất cả chúng đều liên quan đến Stéphane Chazelas .


4
Lưu ý rằng bạn có thể nhận được cdvăn bản trợ giúp với help cd(điều tương tự cho tất cả các lệnh dựng sẵn shell)
Sylvain Pineau

@SylvainPineau mặc dù tôi đã liên kết với hướng dẫn bash, nhưng lời khuyên đó thường không áp dụng cho các shell khác, chẳng hạn như zsh.
muru

Quả thực helplà một bash dựng sẵn (đối với zsh, nó run-help cd)
Sylvain Pineau

Mô tả được liên kết từ đặc tả POSIX không nói rõ ràng rằng nó cdphải được tích hợp sẵn trong vỏ ... nhưng dựa trên cách các thuộc tính quy trình và chuyển giao của chúng hoạt động trong UNIX cdnhư một vỏ được tích hợp là cách thực hiện đơn giản. Xem câu trả lời của Volker Siegel .
pabouk

@pabouk thực sự (nó gọi nó là một tiện ích), và sau đó tiếp tục nói: "Vì cd ảnh hưởng đến môi trường thực thi shell hiện tại, nên nó luôn được cung cấp dưới dạng shell được tích hợp thường xuyên."
muru

8

Bạn không thể tìm thấy một thực thi cho cdvì không có.

cdlà một lệnh nội bộ của shell của bạn (ví dụ bash).


7

từ man which:

trong đó trả về tên đường dẫn của các tệp (hoặc liên kết) sẽ được thực thi trong môi trường hiện tại, có các đối số của nó được đưa ra dưới dạng các lệnh trong trình bao tuân thủ POSIX. Nó thực hiện điều này bằng cách tìm kiếm PATH cho các tệp thực thi khớp với tên của các đối số. Nó không theo các liên kết tượng trưng.

Như chúng ta có thể thấy từ mô tả which, nó chỉ đang kiểm tra PATH. Vì vậy, nếu bạn thực hiện một số bash function, nó sẽ cho bạn thấy không có gì. Nó là tốt hơn để sử dụng typelệnh cùng với which.

Ví dụ trong lslệnh Ubuntu có bí danh ls --color=auto.

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

$ which ls
/bin/ls

Và nếu bạn thực hiện chức năng kiểm tra hello:

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

whichkhông cho thấy gì Nhưng type:

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

Trong trường hợp của bạn:

$ type cd
cd is a shell builtin

Điều này có nghĩa là đó cdlà một vỏ dựng sẵn , nó ở bên trong bash. Tất cả các nội dung bash được mô tả trong man bash, trong phần SHELL BUILTIN THÔNG TIN

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
Có lẽ nên nhấn mạnh hơn: Không sử dụng which, sử dụng type.
tripleee
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.