Mengapa perintah `which` tidak berfungsi untuk` cd`? Saya tidak dapat menemukan executable untuk `cd`!

30

Saya mencoba which cddan tidak memberikan jalan tetapi mengembalikan kode keluar 1 (diperiksa dengan echo $?). Coreutil cditu sendiri berfungsi, jadi executable harus ada di sana, kan? Saya juga menjalankan finduntuk cd, tetapi tidak ada file yang dapat dieksekusi ditampilkan. Bagaimana penerapannya?

Memperbarui:

Saya tidak tahu apakah saya harus menanyakan ini di pos lain tetapi karena saya pikir ini bagus di sini, saya memperluas (?) Pos ... Jadi jawabannya sebenarnya cukup sederhana, tidak ada yang dapat dieksekusi untuk itu - karena itu builtin - Tapi saya telah menemukan beberapa builtin (bash shell di Fedora) memiliki file yang dapat dieksekusi! Jadi builtin -> tidak ada yang bisa dieksekusi tidak benar saya kira? Mungkin jawaban yang menjelaskan apa sebenarnya builtin (perintah builtin?), Yang sebenarnya merupakan masalah di sini, daripada lebih fokus pada cd... Beberapa tautan bagus yang diposting sebelumnya menunjukkan bahwa builtin bukan program ... jadi apa itu? Bagaimana mereka bekerja? Apakah itu hanya fungsi atau utas shell?

tepat
sumber
1
Baca jawaban ini . Disarankan untuk menggunakan typeperintah
c0rp
7
Lihat T&J ini tentang mengapa cdharus dibangun: Mengapa cd bukan program? dan yang ini mengapa typelebih baik dari which: Mengapa tidak menggunakan "yang"? Lalu apa yang harus digunakan?
terdon
Pertanyaan serupa di sini: askubuntu.com/q/613470/178596
Wilf

Jawaban:

46

Perintah cdtidak bisa dieksekusi

Dalam sebuah shell, cddigunakan untuk "masuk ke direktori lain", atau lebih secara formal, untuk mengubah direktori kerja saat ini (CWD). Tidak mungkin menerapkan itu sebagai perintah eksternal:

Direktori milik suatu proses

Direktori kerja saat ini adalah direktori yang digunakan untuk menginterpretasikan jalur relatif untuk mendapatkan jalur lengkap yang dapat digunakan untuk mengakses file. Jalur relatif digunakan di banyak tempat, dan interpretasi dalam satu proses seharusnya tidak mempengaruhi proses lain.
Untuk alasan ini, setiap proses memiliki direktori kerjanya sendiri saat ini.

cdadalah tentang mengubah direktori kerja saat ini dari proses shell, misalnya bash.

Jika itu adalah perintah eksternal, yang dapat dieksekusi di jalur, menjalankan yang dapat dieksekusi akan membuat proses dengan direktori kerjanya sendiri, tanpa mempengaruhi bahwa dari shell saat ini. Bahkan jika perintah eksternal akan mengubah direktori itu, perubahan itu hilang ketika proses eksternal keluar.

Perintah builtin shell

Jadi tidak masuk akal untuk menjalankan perintah eksternal untuk tugas cd. Perintah cdperlu menerapkan perubahan pada proses shell yang sedang berjalan.

Untuk melakukan itu, itu adalah "perintah builtin" dari shell.

Perintah builtin adalah perintah yang berperilaku mirip dengan perintah eksternal, tetapi diimplementasikan dalam shell (jadi cdbukan bagian dari coreutils). Ini memungkinkan perintah untuk mengubah keadaan shell itu sendiri, dalam hal ini untuk memanggil chdir()lihat (lihat man 2 chdir);

Tentang which

Sekarang, jawaban atas pertanyaan judul itu mudah:
Perintah yang whichdapat dieksekusi tidak dapat memberi tahu kami bahwa cd adalah perintah bawaan karena perintah yang dapat dieksekusi tidak tahu apa-apa tentang bawaan.

Alternatif type -a

Sebagai alternatif which, Anda bisa menggunakan type -a; Itu dapat melihat perintah dan builtin yang dapat dieksekusi; Selain itu, ia melihat alias dan fungsi - juga diimplementasikan di shell:

$ 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
sumber
1
Penjelasan hebat!
SaltyNuts
3
Jauh lebih baik daripada jawaban yang diterima saat ini - ini menjelaskan mengapa cd shell dibangun.
Lily Chung
28

cdadalah built-in shell yang diamanatkan POSIX :

Jika perintah sederhana menghasilkan nama perintah dan daftar argumen opsional, tindakan berikut harus dilakukan:

  1. Jika nama perintah tidak mengandung garis miring, langkah pertama yang berhasil dalam urutan berikut akan terjadi:
    ...
    • Jika nama perintah cocok dengan nama utilitas yang tercantum dalam tabel berikut, utilitas itu harus dipanggil.
      ...
      cd
      ...
    • Kalau tidak, perintah harus dicari untuk menggunakan PATH ...

Meskipun ini tidak secara eksplisit mengatakan itu harus built-in, spesifikasi selanjutnya mengatakan, dalam deskripsicd :

Karena cd mempengaruhi lingkungan eksekusi shell saat ini, ia selalu disediakan sebagai built-in shell biasa.

Dari bashmanual :

Perintah built-in shell berikut ini diwarisi dari Bourne Shell. Perintah-perintah ini diimplementasikan sebagaimana ditentukan oleh standar POSIX.
...

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

Saya kira Anda bisa memikirkan arsitektur di mana cdtidak harus menjadi builtin. Namun, Anda harus melihat apa yang tersirat built-in. Jika Anda menulis kode khusus di shell untuk melakukan sesuatu untuk beberapa perintah, Anda sudah hampir memiliki builtin. Semakin banyak yang Anda lakukan, semakin baik hanya memiliki builtin.

Sebagai contoh, Anda dapat meminta shell memiliki IPC untuk berkomunikasi dengan subproses, dan akan ada cdprogram yang akan memeriksa keberadaan direktori dan apakah Anda memiliki izin untuk mengaksesnya dan kemudian berkomunikasi dengan shell untuk memerintahkannya mengubah direktori. Namun, Anda harus kemudian memeriksa apakah proses berkomunikasi dengan Anda adalah anak-anak (atau membuat alat komunikasi khusus hanya dengan anak-anak, seperti deskriptor file khusus, memori bersama, dll.), Dan jika prosesnya memang benar menjalankan cdprogram tepercaya atau yang lainnya. Itu adalah sekaleng cacing.

Atau Anda bisa memiliki cdprogram yang membuat chdirsystem call, dan memulai shell baru dengan semua variabel lingkungan saat ini diterapkan ke shell baru, dan kemudian membunuh shell induknya (entah bagaimana) ketika dilakukan. 1

Lebih buruk lagi, Anda bahkan dapat memiliki sistem di mana suatu proses dapat mengubah lingkungan proses lain (saya pikir secara teknis Anda dapat melakukan ini dengan para penentang). Namun sistem seperti itu akan sangat, sangat rentan.

Anda akan menemukan diri Anda menambahkan kode lebih banyak dan lebih banyak untuk mengamankan metode seperti itu, dan itu jauh lebih mudah untuk membuatnya menjadi builtin.


Bahwa sesuatu itu executable tidak mencegahnya menjadi builtin. Inti masalah:

echo dan test

echodan testmerupakan utilitas yang diamanatkan POSIX ( /bin/echodan /bin/test). Namun hampir setiap shell populer memiliki builtin echodan test. Demikian pula, killbuiltin yang tersedia sebagai program. Lainnya termasuk:

  • sleep (tidak seperti biasa)
  • time
  • false
  • true
  • printf

Namun, ada beberapa kasus di mana perintah tidak bisa apa-apa selain builtin. Salah satunya adalah cd. Biasanya, jika path lengkap tidak ditentukan, dan nama perintah cocok dengan yang builtin, fungsi yang cocok dengan perintah itu disebut. Bergantung pada shell, perilaku bawaan dan perilaku yang dapat dieksekusi mungkin berbeda (ini khususnya masalahecho , yang memiliki perilaku yang sangat berbeda . Jika Anda ingin memastikan perilaku tersebut, lebih baik untuk memanggil yang dapat dieksekusi menggunakan path lengkap, dan mengatur variabel seperti POSIXLY_CORRECT(bahkan saat itu tidak ada jaminan nyata).

Secara teknis tidak ada yang mencegah Anda menyediakan OS yang juga sebuah shell dan memiliki setiap perintah sebagai builtin. Dekat dengan ujung ekstrem ini adalah BusyBox monolitik . BusyBox adalah biner tunggal, yang (tergantung pada namanya) dapat berperilaku sebagai salah satu dari lebih dari 240 program , termasuk Shell Almquist ( ash). Jika Anda membatalkan PATHpengaturan saat menjalankan BusyBox ash, program yang tersedia di BusyBox masih dapat diakses oleh Anda tanpa menentukan a PATH. Mereka hampir menjadi builtin shell, kecuali bahwa shell itu sendiri adalah semacam builtin ke BusyBox.


Studi kasus: Shell Debian Almquist ( dash)

Jika Anda melihat dashsumbernya, utas eksekusi adalah sesuatu seperti ini (tentu saja, dengan fungsi tambahan yang terlibat ketika pipa dan hal-hal lain digunakan):

maincmdloopevaltreeevalcommand

evalcommandlalu gunakan findcommanduntuk menentukan apa perintahnya. Jika itu adalah builtin, maka :

 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.cmdadalah struct( struct builtincmd), yang salah satu anggotanya adalah pointer fungsi, dengan tanda tangan khas main: (int, char **). The evalbltinfungsi panggilan (tergantung pada apakah builtin adalah evalperintah atau tidak) baik evalcmd, atau fungsi pointer ini. Fungsi sebenarnya didefinisikan dalam berbagai file sumber. echo, misalnya, adalah :

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

Semua tautan ke kode sumber di bagian ini berbasis nomor baris, sehingga dapat berubah tanpa pemberitahuan.


1 sistem POSIX memang cddapat dieksekusi .


Catatan:

Ada banyak posting bagus di Unix & Linux yang berhubungan dengan perilaku shell. Khususnya:

Jika Anda belum melihat pola dalam pertanyaan yang terdaftar sejauh ini, hampir semuanya melibatkan Stéphane Chazelas .

muru
sumber
4
Perhatikan bahwa Anda bisa mendapatkan cdteks bantuan dengan help cd(hal yang sama untuk semua perintah shell builtin)
Sylvain Pineau
@SylvainPineau meskipun saya telah ditautkan ke manual bash, saran itu umumnya tidak berlaku untuk shell lain, seperti zsh.
muru
Memang helpbash builtin (untuk zsh, itu run-help cd)
Sylvain Pineau
Deskripsi yang ditautkan dari spesifikasi POSIX tidak secara eksplisit mengatakan bahwa itu cdharus sebagai built-in shell ... tetapi berdasarkan pada bagaimana properti proses dan transfernya bekerja di UNIX cdsebagai shell built-in adalah satu-satunya implementasi langsung. Lihat balasan dari Volker Siegel .
pabouk
@ poabuk memang (itu menyebutnya utilitas), dan kemudian berkata: "Karena cd mempengaruhi lingkungan eksekusi shell saat ini, selalu diberikan sebagai shell built-in reguler."
muru
8

Anda tidak dapat menemukan executable cdkarena tidak ada.

cdadalah perintah internal shell Anda (misalnya bash).

Uwe Plonus
sumber
7

dari man which:

yang mengembalikan nama path file (atau tautan) yang akan dieksekusi di lingkungan saat ini, seandainya argumennya diberikan sebagai perintah dalam shell yang benar-benar sesuai dengan POSIX. Ini dilakukan dengan mencari PATH untuk file yang dapat dieksekusi yang cocok dengan nama argumen. Itu tidak mengikuti tautan simbolis.

Seperti yang bisa kita lihat dari deskripsi which, itu hanya memeriksa PATH. Jadi, jika Anda menerapkan beberapa bash function, itu tidak akan menunjukkan apa-apa kepada Anda. Lebih baik menggunakan typeperintah bersama which.

Misalnya dalam lsperintah Ubuntu alias ls --color=auto.

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

$ which ls
/bin/ls

Dan jika Anda menerapkan fungsi tes hello:

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

whichtidak menunjukkan apa-apa. Tapi type:

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

Dalam kasus Anda:

$ type cd
cd is a shell builtin

Ini berarti bahwa cdadalah builtin shell , itu adalah di dalam bash. Semua bawaan bash dijelaskan dalam man bash, di bagian SHELL BUILTIN PERINTAH

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
sumber
2
Mmmmmanwhich ,.
IQAndreas
1
Mungkin harus lebih ditekankan: Jangan gunakan which, gunakan type.
tripleee