GNU/Linux >> Belajar Linux >  >> Linux

Mengapa Tidak Menggunakan "yang"? Apa yang Harus Digunakan Lalu?

Saat mencari jalur ke executable atau memeriksa apa yang akan terjadi jika Anda memasukkan nama perintah di shell Unix, ada banyak sekali utilitas yang berbeda (which , type , command , whence , where , where , whatis , hash , dll).

Kita sering mendengar bahwa which harus dihindari. Mengapa? Apa yang harus kita gunakan sebagai gantinya?

Jawaban yang Diterima:

Inilah semua yang Anda tidak akan pernah ingin tahu tentangnya:

Ringkasan

Untuk mendapatkan nama path dari executable dalam skrip shell mirip Bourne (ada beberapa peringatan; lihat di bawah):

ls=$(command -v ls)

Untuk mengetahui apakah ada perintah yang diberikan:

if command -v given-command > /dev/null 2>&1; then
  echo given-command is available
else
  echo given-command is not available
fi

Pada prompt shell interaktif seperti Bourne:

type ls

which command adalah warisan rusak dari C-Shell dan lebih baik dibiarkan sendiri di shell seperti Bourne.

Kasus Penggunaan

Ada perbedaan antara mencari informasi tersebut sebagai bagian dari skrip atau secara interaktif di prompt shell.

Pada prompt shell, kasus penggunaan yang umum adalah:perintah ini berperilaku aneh, apakah saya menggunakan yang benar? Apa yang sebenarnya terjadi ketika saya mengetik mycmd ? Bisakah saya melihat lebih jauh apa itu?

Dalam hal ini, Anda ingin mengetahui apa yang dilakukan shell saat Anda menjalankan perintah tanpa benar-benar menjalankan perintah tersebut.

Dalam skrip shell, itu cenderung sangat berbeda. Dalam skrip shell tidak ada alasan mengapa Anda ingin tahu di mana atau apa perintah itu jika yang ingin Anda lakukan hanyalah menjalankannya. Secara umum, yang ingin Anda ketahui adalah jalur yang dapat dieksekusi, sehingga Anda bisa mendapatkan lebih banyak informasi darinya (seperti jalur ke file lain yang relatif terhadap itu, atau membaca informasi dari konten file yang dapat dieksekusi di jalur itu).

Secara interaktif, Anda mungkin ingin tahu tentang semua my-cmd perintah yang tersedia di sistem, dalam skrip, jarang begitu.

Sebagian besar alat yang tersedia (seperti yang sering terjadi) telah dirancang untuk digunakan secara interaktif.

Sejarah

Sedikit sejarah dulu.

Shell Unix awal hingga akhir 70-an tidak memiliki fungsi atau alias. Hanya pencarian tradisional dari executable di $PATH . csh memperkenalkan alias sekitar tahun 1978 (meskipun csh pertama kali dirilis di 2BSD , pada Mei 1979), dan juga pemrosesan .cshrc bagi pengguna untuk menyesuaikan shell (setiap shell, sebagai csh , membaca .cshrc bahkan ketika tidak interaktif seperti dalam skrip).

Sementara shell Bourne pertama kali dirilis di Unix V7 sebelumnya pada tahun 1979, dukungan fungsi hanya ditambahkan kemudian (1984 di SVR2), dan bagaimanapun, tidak pernah memiliki beberapa rc file (.profile adalah mengonfigurasi lingkungan Anda, bukan shell per se ).

csh menjadi jauh lebih populer daripada shell Bourne karena (meskipun sintaksnya jauh lebih buruk daripada shell Bourne) ia menambahkan banyak fitur yang lebih nyaman dan bagus untuk penggunaan interaktif.

Di 3BSD (1980), sebuah which skrip csh telah ditambahkan untuk csh pengguna untuk membantu mengidentifikasi yang dapat dieksekusi, dan ini adalah skrip yang hampir berbeda yang dapat Anda temukan sebagai which di banyak Unice komersial saat ini (seperti Solaris, HP/UX, AIX atau Tru64).

Script itu membaca ~/.cshrc pengguna (seperti semua csh skrip lakukan kecuali dipanggil dengan csh -f ), dan mencari nama perintah yang disediakan dalam daftar alias dan di $path (array yang csh mempertahankan berdasarkan $PATH ).

Ini dia:which datang pertama untuk shell paling populer saat itu (dan csh masih populer hingga pertengahan 90-an), yang merupakan alasan utama mengapa ia didokumentasikan dalam buku dan masih digunakan secara luas.

Perhatikan bahwa, bahkan untuk csh pengguna, itu which skrip csh tidak selalu memberi Anda informasi yang benar. Itu mendapatkan alias yang didefinisikan di ~/.cshrc , bukan yang mungkin Anda tetapkan nanti saat diminta atau misalnya dengan source menggunakan csh lain file, dan (meskipun itu bukan ide yang baik), PATH mungkin didefinisikan ulang dalam ~/.cshrc .

Menjalankan which perintah dari shell Bourne akan tetap mencari alias yang ditentukan dalam ~/.cshrc Anda , tetapi jika Anda tidak memilikinya karena Anda tidak menggunakan csh , itu mungkin masih memberi Anda jawaban yang benar.

Fungsi serupa tidak ditambahkan ke shell Bourne hingga 1984 di SVR2 dengan type perintah bawaan. Fakta bahwa itu adalah bawaan (sebagai lawan dari skrip eksternal) berarti dapat memberi Anda informasi yang benar (sampai batas tertentu) karena memiliki akses ke bagian dalam shell.

type awal perintah mengalami masalah yang sama dengan which skrip yang tidak mengembalikan status kegagalan keluar jika perintah tidak ditemukan. Juga, untuk executable, bertentangan dengan which , itu menghasilkan sesuatu seperti ls is /bin/ls bukan hanya /bin/ls yang membuatnya kurang mudah digunakan dalam skrip.

Shell Bourne Unix Versi 8 (tidak dirilis di alam liar) memiliki type bawaan diubah namanya menjadi whatis dan diperluas untuk juga melaporkan tentang parameter dan definisi fungsi cetak. Itu juga memperbaiki type masalah tidak mengembalikan kegagalan ketika gagal menemukan nama.

rc , cangkang Plan9 (yang akan menjadi penerus Unix) (dan turunannya seperti akanga dan es ) memiliki whatis juga.

Shell Korn (bagian dari POSIX sh definisi didasarkan pada), dikembangkan pada pertengahan 80-an tetapi tidak tersedia secara luas sebelum 1988, menambahkan banyak csh fitur (editor baris, alias…) di atas shell Bourne. Ia menambahkan whence-nya sendiri bawaan (selain type ) yang mengambil beberapa opsi (-v untuk memberikan type -seperti keluaran verbose, dan -p untuk hanya mencari executable (bukan alias/fungsi…)).

Terkait:Debian – Bagaimana cara menggunakan driver nirkabel berpemilik selama instalasi USB Debian?

Bertepatan dengan gejolak yang berkaitan dengan masalah hak cipta antara AT&T dan Berkeley, beberapa perangkat lunak gratis implementasi shell keluar pada akhir 80-an awal 90-an. Semua cangkang Almquist (ash , untuk menggantikan shell Bourne di BSD), implementasi domain publik ksh (pdksh ), bash (disponsori oleh FSF), zsh keluar di antara tahun 1989 dan 1991.

Ash, meskipun dimaksudkan sebagai pengganti cangkang Bourne, tidak memiliki type builtin sampai lama kemudian (dalam NetBSD 1.3 dan FreeBSD 2.3), meskipun memiliki hash -v . OSF/1 /bin/sh memiliki type builtin yang selalu mengembalikan 0 hingga OSF/1 v3.x. bash tidak menambahkan whence tetapi menambahkan -p pilihan untuk type untuk mencetak jalur (type -p akan seperti whence -p ) dan -a untuk melaporkan semua perintah-perintah yang cocok. tcsh membuat which builtin dan menambahkan where perintah bertindak seperti bash type -a . zsh memiliki semuanya.

fish shell (2005) memiliki type perintah diimplementasikan sebagai fungsi.

which sementara skrip csh telah dihapus dari NetBSD (karena dibuat di tcsh dan tidak banyak digunakan di shell lain), dan fungsionalitas ditambahkan ke whereis (ketika dipanggil sebagai which , where berperilaku seperti which kecuali bahwa itu hanya mencari executable di $PATH ). Di OpenBSD dan FreeBSD, which juga diubah menjadi yang ditulis dalam C yang mencari perintah di $PATH saja.

Implementasi

Ada lusinan implementasi which perintah pada berbagai Unice dengan sintaks dan perilaku yang berbeda.

Di Linux (di samping yang ada di tcsh dan zsh ) kami menemukan beberapa implementasi. Pada sistem Debian terbaru misalnya, ini adalah skrip shell POSIX sederhana yang mencari perintah di $PATH .

busybox juga memiliki which perintah.

Ada GNU which yang mungkin paling boros. Ia mencoba untuk memperluas apa which skrip csh lakukan pada shell lain:Anda dapat memberi tahu apa alias dan fungsinya sehingga dapat memberi Anda jawaban yang lebih baik (dan saya yakin beberapa distribusi Linux mengatur beberapa alias global untuk bash untuk melakukan itu).

zsh memiliki beberapa operator untuk memperluas ke jalur yang dapat dieksekusi:= ekspansi nama file operator dan :c pengubah perluasan riwayat (di sini diterapkan pada perluasan parameter ):

$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls

zsh , di zsh/parameters module juga membuat tabel hash perintah sebagai commands larik asosiatif:

$ print -r -- $commands[ls]
/bin/ls

whatis utilitas (kecuali yang ada di Unix V8 Bourne shell atau Plan 9 rc /es ) sebenarnya tidak terkait karena hanya untuk dokumentasi (mengambil database whatis, yaitu sinopsis halaman manual).

where juga ditambahkan di 3BSD bersamaan dengan which meskipun ditulis dalam C , bukan csh dan digunakan untuk mencari pada saat yang sama, halaman manual yang dapat dieksekusi, dan sumber tetapi tidak berdasarkan lingkungan saat ini. Jadi sekali lagi, itu menjawab kebutuhan yang berbeda.

Sekarang, di bagian depan standar, POSIX menentukan command -v dan -V perintah (yang dulunya opsional hingga POSIX.2008). UNIX menentukan type perintah (tidak ada opsi). Itu saja (where , which , whence tidak ditentukan dalam standar apa pun).

Hingga beberapa versi, type dan command -v adalah opsional dalam spesifikasi Basis Standar Linux yang menjelaskan mengapa misalnya beberapa versi lama posh (meskipun berdasarkan pdksh yang memiliki keduanya) tidak memiliki keduanya. command -v juga ditambahkan ke beberapa implementasi shell Bourne (seperti pada Solaris).

Status Hari Ini

Status saat ini adalah type dan command -v ada di mana-mana di semua cangkang mirip Bourne (meskipun, seperti yang dicatat oleh @jarno, perhatikan peringatan/bug di bash ketika tidak dalam mode POSIX atau beberapa turunan dari shell Almquist di bawah dalam komentar). tcsh adalah satu-satunya shell di mana Anda ingin menggunakan which (karena tidak ada type di sana dan which sudah terpasang).

Di shell selain tcsh dan zsh , which mungkin memberi tahu Anda jalur executable yang diberikan selama tidak ada alias atau fungsi dengan nama yang sama di salah satu ~/.cshrc kami , ~/.bashrc atau file startup shell apa pun dan Anda tidak mendefinisikan $PATH di ~/.cshrc . Anda . Jika Anda memiliki alias atau fungsi yang ditentukan untuknya, itu mungkin atau mungkin tidak memberi tahu Anda tentangnya, atau memberi tahu Anda hal yang salah.

Jika Anda ingin tahu tentang semua perintah dengan nama tertentu, tidak ada yang portabel. Anda akan menggunakan where di tcsh atau zsh , type -a di bash atau zsh , whence -a di ksh93 dan di shell lain, Anda dapat menggunakan type dalam kombinasi dengan which -a yang mungkin berhasil.

Rekomendasi

Mendapatkan nama path ke executable

Sekarang, untuk mendapatkan nama path dari executable dalam skrip, ada beberapa peringatan:

ls=$(command -v ls)

akan menjadi cara standar untuk melakukannya.

Namun ada beberapa masalah:

  • Tidak mungkin mengetahui jalur yang dapat dieksekusi tanpa menjalankannya. Semua type , which , command -v … semua menggunakan heuristik untuk mengetahui jalurnya. Mereka mengulang melalui $PATH komponen dan temukan file non-direktori pertama yang izinnya Anda jalankan. Namun, tergantung pada shell, ketika datang untuk mengeksekusi perintah, banyak dari mereka (Bourne, AT&T ksh, zsh, ash…) hanya akan menjalankannya dalam urutan $PATH sampai execve panggilan sistem tidak kembali dengan kesalahan. Misalnya jika $PATH berisi /foo:/bar dan Anda ingin menjalankan ls , pertama-tama mereka akan mencoba mengeksekusi /foo/ls atau jika gagal /bar/ls . Sekarang eksekusi /foo/ls mungkin gagal karena Anda tidak memiliki izin eksekusi tetapi juga karena banyak alasan lain, seperti itu bukan executable yang valid. command -v ls akan melaporkan /foo/ls jika Anda memiliki izin eksekusi untuk /foo/ls , tetapi menjalankan ls mungkin benar-benar menjalankan /bar/ls jika /foo/ls bukan executable yang valid.
  • jika foo adalah builtin atau fungsi atau alias, command -v foo mengembalikan foo . Dengan beberapa shell seperti ash , pdksh atau zsh , itu juga dapat mengembalikan foo jika $PATH termasuk string kosong dan ada foo yang dapat dieksekusi file di direktori saat ini. Ada beberapa keadaan di mana Anda mungkin perlu mempertimbangkannya. Ingatlah misalnya bahwa daftar bawaan bervariasi dengan implementasi shell (misalnya, mount terkadang builtin untuk busybox sh ), dan misalnya bash bisa mendapatkan fungsi dari lingkungan.
  • jika $PATH berisi komponen jalur relatif (biasanya . atau string kosong yang keduanya merujuk ke direktori saat ini tetapi bisa berupa apa saja), tergantung pada shell, command -v cmd mungkin tidak menampilkan jalur absolut. Jadi jalur yang Anda peroleh pada saat Anda menjalankan command -v tidak akan berlaku lagi setelah Anda cd di tempat lain.
  • Anekdot:dengan shell ksh93, jika /opt/ast/bin (meskipun jalur yang tepat dapat bervariasi pada sistem yang berbeda, saya percaya) ada di dalam Anda $PATH , ksh93 akan menyediakan beberapa bawaan tambahan (chmod , cmp , cat …), tetapi command -v chmod akan mengembalikan /opt/ast/bin/chmod bahkan jika jalur itu tidak ada.
Terkait:Dd vs cat — apakah dd masih relevan hari ini?

Menentukan apakah ada perintah

Untuk mengetahui apakah perintah yang diberikan ada secara standar, Anda dapat melakukan:

if command -v given-command > /dev/null 2>&1; then
  echo given-command is available
else
  echo given-command is not available
fi

Di mana seseorang mungkin ingin menggunakan which

(t)csh

Di csh dan tcsh , Anda tidak punya banyak pilihan. Dalam tcsh , tidak apa-apa sebagai which sudah terpasang. Di csh , itu akan menjadi sistem which perintah, yang mungkin tidak melakukan apa yang Anda inginkan dalam beberapa kasus.

Temukan perintah hanya di beberapa shell

Kasus di mana mungkin masuk akal untuk menggunakan which adalah jika Anda ingin mengetahui jalur suatu perintah, mengabaikan potensi bawaan atau fungsi shell di bash , csh (bukan tcsh ), dash , atau Bourne skrip shell, yaitu shell yang tidak memiliki whence -p (seperti ksh atau zsh ), command -ev (seperti yash ), whatis -p (rc , akanga ) atau which . bawaan (seperti tcsh atau zsh ) pada sistem di mana which tersedia dan bukan csh naskah.

Jika kondisi tersebut terpenuhi, maka:

echo=$(which echo)

akan memberi Anda jalur echo first pertama di $PATH (kecuali dalam kasus sudut), terlepas dari apakah echo juga merupakan shell builtin/alias/function atau tidak.

Di shell lain, Anda lebih suka:

  • zsh :echo==echo atau echo=$commands[echo] atau echo=${${:-echo}:c}
  • ksh , zsh :echo=$(whence -p echo)
  • yash :echo=$(command -ev echo)
  • rc , akanga :echo=`whatis -p echo` (hati-hati dengan jalur dengan spasi)
  • ikan :set echo (type -fp echo)

Perhatikan bahwa jika semua yang ingin Anda lakukan adalah lari echo perintah, Anda tidak harus mendapatkan jalurnya, Anda cukup melakukan:

env echo this is not echoed by the builtin echo

Misalnya, dengan tcsh , untuk mencegah which . bawaan dari digunakan:

set Echo = "`env which echo`"

Bila Anda membutuhkan perintah eksternal

Kasus lain di mana Anda mungkin ingin menggunakan which adalah saat Anda benar-benar membutuhkan sebuah perintah eksternal. POSIX mengharuskan semua bawaan shell (seperti command ) juga tersedia sebagai perintah eksternal, tetapi sayangnya, itu tidak berlaku untuk command pada banyak sistem. Misalnya, jarang menemukan command perintah pada sistem operasi berbasis Linux sementara kebanyakan dari mereka memiliki which perintah (meskipun berbeda dengan opsi dan perilaku yang berbeda).

Kasus di mana Anda mungkin menginginkan perintah eksternal adalah di mana pun Anda akan menjalankan perintah tanpa menggunakan shell POSIX.

system("some command line") , popen() … fungsi C atau berbagai bahasa memanggil shell untuk mengurai baris perintah itu, jadi system("command -v my-cmd") melakukan pekerjaan di dalamnya. Pengecualian untuk itu adalah perl yang mengoptimalkan shell jika tidak melihat karakter khusus shell (selain spasi). Itu juga berlaku untuk operator backticknya:

$ perl -le 'print system "command -v emacs"'
-1
$ perl -le 'print system ":;command -v emacs"'
/usr/bin/emacs
0

$ perl -e 'print `command -v emacs`'
$ perl -e 'print `:;command -v emacs`'
/usr/bin/emacs

Penambahan :; di atas memaksa perl untuk memanggil shell di sana. Dengan menggunakan which , Anda tidak perlu menggunakan trik itu.


Linux
  1. Cara:Apa itu Git dan Github? Bagaimana cara menggunakannya dan mengapa saya harus peduli?

  2. Mengapa Cd Bukan Program?

  3. Linux – Mengapa Kami Menggunakan Su – Dan Bukan Hanya Su?

  1. Mengapa Tidak Ada yang Menggunakan Shell Bourne Sejati Sebagai /bin/sh?

  2. Apa yang Tidak Harus Dipasang Di SSD?

  3. Mengapa eval harus dihindari di Bash, dan apa yang harus saya gunakan?

  1. apa daemon dbus dan mengapa vlc membutuhkannya

  2. Mengapa kita menggunakan su - dan bukan hanya su?

  3. Karakter apa yang harus saya gunakan atau tidak gunakan pada nama pengguna di Linux?