Bagaimana cara mencari di $ PATH berfungsi di bawah tenda?

8

Ada terlalu banyak artikel / sumber daya di web yang mengajarkan orang-orang BAGAIMANA mengatur variabel lingkungan PATHsehingga mereka dapat menggunakan tulisan tangan pendek javadan pythonlain - lain alih-alih jalur absolut dalam antarmuka baris perintah.

Yang saya tertarik ketahui adalah apa yang ada di balik layar ketika kita mengetikkan perintah dan tekan enter (mirip dengan apa yang terjadi ketika Anda mengetikkan URL di browser ).

Ini tebakan saya:

  1. baca perintah (parse / preprocess stdin untuk mendapatkan argumen yang benar $@)
  2. pencarian perintah
  3. eksekusi perintah (program dimulai, konsumsi memori, stdout / stderr ke shell)
  4. render ulang emulator oleh variabel lingkungan yang relevan (mis $PS#. $PROMPT, dll)

Bagian yang paling ingin saya pikirkan adalah pencarian perintah. Jelas, $PATHdikonsumsi oleh beberapa fungsi latar belakang dan dipisahkan oleh :/ ;sebagai pembatas, lalu apa yang terjadi? Apakah kita menggunakan tabel hash (kunci: nama file, nilai: dirname absolut file) untuk menyimpan file biner di bawah PATH tersebut, atau beberapa kait lainnya?

CATATAN: Saya awalnya mengira itu adalah tabel hash yang dapat saya gunakan [ -z hash [command] ]untuk memeriksa apakah perintah tersedia di env saat ini, tetapi ketika saya menggunakan hash | grep pythonsaya tidak mendapatkan apa-apa dari output sambil which pythonbekerja seperti yang diharapkan. (Saya pikir mekanismenya mungkin spesifik untuk shell, tapi saya ingin mendapatkan lebih banyak wawasan.)

Xlee
sumber

Jawaban:

11

Seperti yang Anda duga, perilaku pasti tergantung pada shell, tetapi tingkat fungsi dasar ditentukan oleh POSIX.

Pencarian perintah dan eksekusi untuk bahasa perintah shell standar (yang sebagian besar shell mengimplementasikan superset) memiliki banyak kasus, tetapi kami hanya tertarik untuk saat ini dalam kasus di mana PATHdigunakan. Dalam hal itu:

perintah harus dicari untuk menggunakan variabel lingkungan PATH seperti yang dijelaskan dalam Variabel Lingkungan XBD

dan

Jika pencarian berhasil:

[...]

shell mengeksekusi utilitas dalam lingkungan utilitas terpisah dengan tindakan yang setara dengan memanggil execl()fungsi [...] dengan argumen path diatur ke pathname yang dihasilkan dari pencarian.

Dalam kasus yang tidak berhasil, eksekusi gagal dan kode keluar 127 dikembalikan dengan pesan kesalahan.

Perilaku ini konsisten dengan execvpfungsi, khususnya. Semua exec*fungsi menerima nama file dari program yang akan dijalankan, urutan argumen (yang akan menjadi argvprogram), dan mungkin satu set variabel lingkungan. Untuk versi yang menggunakan PATHpencarian, POSIX mendefinisikan bahwa :

Argumen File digunakan untuk membangun pathname yang mengidentifikasi file gambar proses baru [...] awalan path untuk file ini diperoleh dengan pencarian direktori lulus sebagai variabel lingkungan PATH


The perilaku PATH didefinisikan di tempat lain sebagai:

Variabel ini harus mewakili urutan awalan jalur yang berlaku fungsi dan utilitas tertentu dalam mencari file yang dapat dieksekusi yang hanya diketahui oleh nama file. Awalan harus dipisahkan oleh <colon> (':'). Ketika awalan yang tidak nol panjang diterapkan ke nama file ini, <slash> harus dimasukkan antara awalan dan nama file jika awalan tidak berakhir. Awalan nol panjang adalah fitur lawas yang menunjukkan direktori kerja saat ini. Itu muncul sebagai dua karakter yang berdekatan ("::"), sebagai inisial <colon> sebelum daftar lainnya, atau sebagai trailing <colon> mengikuti sisa dari daftar. Aplikasi yang benar-benar sesuai harus menggunakan pathname aktual (seperti.) Untuk mewakili direktori kerja saat ini di PATH.Daftar harus dicari dari awal hingga akhir, menerapkan nama file untuk setiap awalan, sampai file yang dapat dieksekusi dengan nama yang ditentukan dan izin eksekusi yang sesuai ditemukan . Jika nama path yang dicari mengandung <slash>, pencarian melalui awalan path tidak akan dilakukan. Jika pathname dimulai dengan <slash>, path yang ditentukan diselesaikan (lihat Resolusi Pathname ). Jika PATH tidak disetel atau disetel ke nol, pencarian jalur ditentukan oleh implementasi.

Itu agak padat, jadi ringkasannya:

  1. Jika nama program memiliki /(slash, U + 002F SOLIDUS) di dalamnya, perlakukan sebagai jalur dengan cara biasa , dan lewati sisa proses ini. Untuk shell, kasus ini secara teknis tidak muncul (karena aturan shell akan sudah menanganinya).
  2. Nilai PATHdibagi menjadi beberapa bagian di setiap titik dua, dan kemudian setiap komponen diproses dari kiri ke kanan. Sebagai kasus khusus (historis), komponen kosong dari variabel tidak kosong diperlakukan sebagai .(direktori saat ini).
  3. Untuk setiap komponen, nama program ditambahkan ke bagian akhir dengan penggabungan /dan keberadaan file dengan nama tersebut dicentang, dan jika ada, maka izin eksekusi (+ x) yang valid juga akan diperiksa. Jika salah satu dari pemeriksaan tersebut gagal, proses beralih ke komponen berikutnya. Jika tidak, perintah akan memutuskan jalur ini dan pencarian dilakukan.
  4. Jika Anda kehabisan komponen, pencarian gagal.
  5. Jika tidak ada apa-apa di PATHdalamnya, atau tidak ada, lakukan apa pun yang Anda inginkan.

Kerang asli akan memiliki perintah bawaan, yang ditemukan sebelum pencarian ini, dan sering juga alias dan fungsi. Mereka tidak berinteraksi PATH. POSIX mendefinisikan beberapa perilaku di sekitar itu , dan shell Anda mungkin memiliki lebih banyak.


Meskipun dimungkinkan untuk exec*melakukan sebagian besar dari ini untuk Anda, shell dalam praktiknya mungkin mengimplementasikan pencarian ini sendiri, terutama untuk tujuan caching, tetapi perilaku cache kosong harus serupa. Kerang memiliki garis lintang yang cukup lebar di sini dan memiliki perilaku yang agak berbeda dalam kasing sudut.

Seperti yang Anda temukan, Bash menggunakan tabel hash untuk mengingat jalur penuh perintah yang dilihat sebelumnya, dan tabel itu dapat diakses dengan hashfungsi. Pertama kali Anda menjalankan perintah yang dicari, dan ketika hasilnya ditemukan itu akan ditambahkan ke tabel sehingga tidak perlu repot mencari saat berikutnya Anda mencobanya.

Di zsh, di sisi lain, penuh PATHumumnya dicari ketika shell dimulai. Tabel pencarian diisi terlebih dahulu dengan semua nama perintah yang ditemukan sehingga pencarian runtime biasanya tidak diperlukan (kecuali jika perintah baru ditambahkan). Anda bisa melihat itu terjadi ketika Anda mencoba untuk melengkapi-perintah yang tidak ada sebelumnya.

Kerang yang sangat ringan, seperti dash, cenderung mendelegasikan perilaku sebanyak mungkin ke pustaka sistem dan tidak repot-repot mengingat jalur perintah masa lalu.

Michael Homer
sumber
Terima kasih banyak atas penjelasan terperinci, ini benar-benar memberikan wawasan yang mendalam. Perbandingan Anda tentang PATHantara bashdan zshmembantu saya menyelesaikan kebingungan saya!
Xlee