Inilah semua yang Anda tidak pernah berpikir Anda tidak akan mau tahu tentang hal itu:
Ringkasan
Untuk mendapatkan pathname dari executable dalam skrip shell Bourne-like (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 seperti Bourne interaktif:
type ls
The whichperintah adalah warisan yang rusak dari C-Shell dan lebih baik ditinggalkan sendirian di kerang Bourne-seperti.
Gunakan Kasing
Ada perbedaan antara mencari informasi itu 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 tahu apa yang dilakukan shell Anda ketika Anda menjalankan perintah tanpa benar-benar menjalankan perintah.
Dalam skrip shell, cenderung sangat berbeda. Dalam skrip shell tidak ada alasan mengapa Anda ingin tahu di mana atau apa perintah itu jika semua yang ingin Anda lakukan adalah menjalankannya. Secara umum, apa yang ingin Anda ketahui adalah jalur yang dapat dieksekusi, sehingga Anda dapat memperoleh lebih banyak informasi darinya (seperti jalur ke file lain terkait dengan itu, atau membaca informasi dari konten file yang dapat dieksekusi di jalur itu).
Interaktif, Anda mungkin ingin tahu tentang semua yang my-cmdperintah yang tersedia pada sistem, dalam skrip, jarang jadi.
Sebagian besar alat yang tersedia (seperti yang sering terjadi) telah dirancang untuk digunakan secara interaktif.
Sejarah
Sedikit sejarah dulu.
Kerang Unix awal hingga akhir 70-an tidak memiliki fungsi atau alias. Hanya mencari tradisional executable di $PATH. cshmemperkenalkan alias sekitar tahun 1978 (meskipun cshpertama kali dirilis pada 2BSDbulan Mei 1979), dan juga pemrosesan .cshrcbagi pengguna untuk menyesuaikan shell (setiap shell, seperti yang cshdibaca .cshrcbahkan ketika tidak interaktif seperti dalam skrip).
sementara shell Bourne pertama kali dirilis di Unix V7 pada tahun 1979, dukungan fungsi hanya ditambahkan jauh kemudian (1984 dalam SVR2), dan lagi pula, itu tidak pernah memiliki beberapa rcfile ( .profileyaitu untuk mengkonfigurasi lingkungan Anda, bukan shell per se ).
csh mendapat jauh lebih populer daripada shell Bourne (meskipun memiliki sintaks yang jauh lebih buruk daripada shell Bourne) itu menambahkan banyak fitur yang lebih nyaman dan menyenangkan untuk penggunaan interaktif.
Pada 3BSD(1980), whichskrip csh ditambahkan bagi cshpengguna untuk membantu mengidentifikasi sebuah executable, dan skrip ini hampir tidak berbeda dengan yang dapat Anda temukan whichdi banyak Unix komersial saat ini (seperti Solaris, HP / UX, AIX atau Tru64).
Skrip itu membaca pengguna ~/.cshrc(seperti semua cshskrip lakukan kecuali dipanggil dengan csh -f), dan mencari nama perintah yang disediakan dalam daftar alias dan dalam $path(array yang cshmempertahankan berdasarkan $PATH).
Ini dia, whichmenjadi yang pertama untuk shell paling populer saat itu (dan cshmasih populer sampai pertengahan 90-an), yang merupakan alasan utama mengapa ia didokumentasikan dalam buku dan masih banyak digunakan.
Perhatikan bahwa, bahkan untuk cshpengguna, whichskrip csh tidak selalu memberi Anda informasi yang benar. Itu mendapatkan alias yang didefinisikan ~/.cshrc, bukan yang Anda mungkin telah didefinisikan nanti pada prompt atau misalnya dengan memasukkan file sourcelain csh, dan (meskipun itu bukan ide yang baik), PATHmungkin didefinisikan ulang ~/.cshrc.
Menjalankan whichperintah itu dari shell Bourne, masih akan mencari alias yang didefinisikan di Anda ~/.cshrc, tetapi jika tidak memilikinya karena Anda tidak menggunakannya csh, itu mungkin masih memberi Anda jawaban yang benar.
Fungsionalitas yang serupa tidak ditambahkan ke shell Bourne hingga 1984 di SVR2 dengan typeperintah builtin. Fakta bahwa ia dibangun (sebagai lawan dari skrip eksternal) berarti ia dapat memberi Anda informasi yang benar (sampai batas tertentu) karena memiliki akses ke internal shell.
typePerintah awal mengalami masalah yang sama seperti whichskrip yang tidak mengembalikan status keluar kegagalan jika perintah tidak ditemukan. Juga, untuk executable, sebaliknya which, ini menghasilkan sesuatu seperti ls is /bin/lsbukannya /bin/lsyang membuatnya kurang mudah digunakan dalam skrip.
Unix Version 8's (tidak dirilis di wild) Bourne shell telah typedibangun ulang namanya menjadi whatis. Dan shell Plan9 (penerus Unix yang pernah menjadi) juga rc(dan turunannya seperti akangadan es) whatisjuga.
Shell Korn (subset yang mendasari definisi sh POSIX), dikembangkan pada pertengahan 80-an tetapi tidak banyak tersedia sebelum 1988, menambahkan banyak cshfitur (editor baris, alias ...) di atas shell Bourne . Ia menambahkan whencebuiltin -nya sendiri (sebagai tambahan type) yang mengambil beberapa opsi ( -vuntuk menyediakan typekeluaran verbose-like, dan -phanya mencari executable (bukan alias / fungsi ...)).
Bersamaan dengan kekacauan terkait dengan masalah hak cipta antara AT&T dan Berkeley, beberapa implementasi perangkat lunak shell gratis muncul di akhir tahun 80-an awal tahun 90-an. Semua shell Almquist (abu, untuk menjadi pengganti shell Bourne di BSD), implementasi domain publik ksh (pdksh), bash(disponsori oleh FSF), zshkeluar di antara tahun 1989 dan 1991.
Ash, meskipun dimaksudkan sebagai pengganti shell Bourne tidak memiliki typebuiltin sampai jauh kemudian (di NetBSD 1.3 dan FreeBSD 2.3), meskipun sudah hash -v. OSF / 1 /bin/shmemiliki typebuiltin yang selalu mengembalikan 0 hingga OSF / 1 v3.x. bashtidak menambahkan whencetetapi menambahkan -popsi untuk typemencetak jalur ( type -pakan seperti whence -p) dan -amelaporkan semua perintah yang cocok. tcshdibuat whichbuiltin dan menambahkan whereperintah bertindak seperti bash's type -a. zshmemiliki semuanya.
The fishshell (2005) memiliki typeperintah diimplementasikan sebagai fungsi.
The whichScript csh Sementara itu dihapus dari NetBSD (seperti yang builtin di tcsh dan tidak banyak digunakan dalam kerang lainnya), dan fungsi ditambahkan ke whereis(ketika dipanggil sebagai which, whereisberperilaku seperti whichkecuali bahwa itu hanya mendongak executable di $PATH). Di OpenBSD dan FreeBSD, whichjuga diubah menjadi satu yang ditulis dalam C yang mencari perintah $PATHsaja.
Implementasi
Ada lusinan implementasi whichperintah pada berbagai Unix dengan sintaks dan perilaku yang berbeda.
Di Linux (di samping yang builtin di dalam tcshdan zsh) kami menemukan beberapa implementasi. Pada sistem Debian terbaru misalnya, ini adalah skrip shell POSIX sederhana yang mencari perintah di $PATH.
busyboxjuga memiliki whichperintah.
Ada GNU whichyang mungkin merupakan yang paling mewah. Ia mencoba untuk memperluas apa yang dilakukan whichskrip csh ke shell lain: Anda dapat memberi tahu apa itu alias dan fungsi Anda sehingga dapat memberikan jawaban yang lebih baik (dan saya percaya beberapa distribusi Linux menetapkan beberapa alias global di sekitarnya untuk bashmelakukannya) .
zshmemiliki beberapa operator untuk memperluas ke jalur yang dapat dieksekusi: operator = ekspansi nama file dan :cpengubah ekspansi riwayat (di sini diterapkan pada ekspansi parameter ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zsh, dalam zsh/parametersmodul juga membuat tabel hash perintah sebagai commandsarray asosiatif:
$ print -r -- $commands[ls]
/bin/ls
The whatisutilitas (kecuali untuk satu di Unix V8 Bourne shell atau Plan 9 rc/ es) tidak benar-benar terkait seperti itu untuk dokumentasi saja (greps database whatis, yaitu halaman man sinopsis).
whereisjuga ditambahkan pada 3BSDsaat yang sama seolah- whicholah itu ditulis C, tidak cshdan digunakan untuk mencari pada saat yang sama, halaman buku panduan dan sumber yang dapat dieksekusi tetapi tidak didasarkan pada lingkungan saat ini. Jadi sekali lagi, itu menjawab kebutuhan yang berbeda.
Sekarang, di bagian depan standar, POSIX menentukan perintah command -vdan -V(yang dulunya opsional sampai POSIX.2008). UNIX menentukan typeperintah (tidak ada opsi). Itu semua ( where, which, whencetidak ditentukan dalam standar apapun)
Hingga beberapa versi, typedan command -vmerupakan opsional dalam spesifikasi Pangkalan Linux Standar yang menjelaskan mengapa misalnya beberapa versi lama posh(meskipun berdasarkan pdkshkeduanya) tidak memiliki keduanya. command -vjuga ditambahkan ke beberapa implementasi shell Bourne (seperti pada Solaris).
Status Hari Ini
Status saat ini adalah bahwa typedan command -vada di mana-mana di semua cangkang Bourne-like (meskipun, seperti dicatat oleh @jarno, perhatikan peringatan / bug bashketika tidak dalam mode POSIX atau beberapa keturunan cangkang Almquist di bawah ini dalam komentar). tcshadalah satu-satunya shell di mana Anda ingin menggunakan which(karena tidak typeada dan whichdibangun).
Di dalam shell selain tcshdan zsh, whichdapat memberi tahu Anda jalur executable yang diberikan selama tidak ada alias atau fungsi dengan nama yang sama di salah satu dari kami ~/.cshrc, ~/.bashrcatau file startup shell apa pun dan Anda tidak menentukan $PATHdi ~/.cshrc. Jika Anda memiliki alias atau fungsi yang ditentukan untuknya, itu mungkin atau mungkin tidak memberi tahu Anda tentang hal itu, atau memberi tahu Anda hal yang salah.
Jika Anda ingin tahu tentang semua perintah dengan nama yang diberikan, tidak ada yang portabel. Anda akan menggunakan wheredi tcshatau zsh, type -adalam bashatau zsh, whence -adi ksh93 dan kerang lainnya, Anda dapat menggunakan typedalam kombinasi dengan which -ayang mungkin bekerja.
Rekomendasi
Mendapatkan pathname ke executable
Sekarang, untuk mendapatkan pathname dari executable dalam skrip, ada beberapa peringatan:
ls=$(command -v ls)
akan menjadi cara standar untuk melakukannya.
Ada beberapa masalah:
- Tidak mungkin mengetahui jalur yang dapat dieksekusi tanpa mengeksekusinya. Semua
type, which, command -v... semua heuristik digunakan untuk mengetahui jalan. Mereka loop melalui $PATHkomponen dan menemukan file non-direktori pertama yang Anda jalankan izinnya. Namun, tergantung pada shell, ketika menjalankan perintah, banyak dari mereka (Bourne, AT&T ksh, zsh, ash ...) hanya akan menjalankannya dalam urutan $PATHsampai execvepanggilan sistem tidak kembali dengan kesalahan . Misalnya jika $PATHberisi /foo:/bardan Anda ingin mengeksekusi ls, mereka akan terlebih dahulu mencoba mengeksekusi /foo/lsatau jika itu gagal /bar/ls. Sekarang eksekusi/foo/lsmungkin gagal karena Anda tidak memiliki izin eksekusi tetapi juga karena banyak alasan lain, seperti itu bukan eksekusi yang valid. command -v lsakan melaporkan /foo/lsjika Anda memiliki izin eksekusi /foo/ls, tetapi menjalankan lsmungkin benar-benar berjalan /bar/lsjika /foo/lsbukan merupakan eksekusi yang valid.
- jika
foomerupakan builtin atau fungsi atau alias, command -v fookembali foo. Dengan beberapa shell seperti ash, pdkshatau zsh, ia juga dapat kembali foojika $PATHmenyertakan string kosong dan ada foofile yang dapat dieksekusi di direktori saat ini. Ada beberapa kondisi di mana Anda mungkin perlu memperhitungkannya. Ingatlah misalnya bahwa daftar bawaan bervariasi dengan implementasi shell (misalnya, mountkadang-kadang dibangun untuk busybox sh), dan misalnya bashbisa mendapatkan fungsi dari lingkungan.
- jika
$PATHberisi komponen path relatif (biasanya .atau string kosong yang keduanya merujuk ke direktori saat ini tetapi bisa berupa apa saja), tergantung pada shell, command -v cmdmungkin tidak menampilkan path absolut. Jadi jalur yang Anda dapatkan pada saat Anda menjalankan command -vtidak akan lagi berlaku setelah Anda di cdtempat lain.
- Anekdot: dengan shell ksh93, jika
/opt/ast/bin(meskipun jalan yang tepat dapat bervariasi pada sistem yang berbeda saya percaya) di dalam kamu $PATH, ksh93 akan membuat tersedia beberapa builtin tambahan ( chmod, cmp, cat...), tapi command -v chmodakan kembali /opt/ast/bin/chmodbahkan jika jalan itu doesn' tidak ada.
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 orang mungkin ingin menggunakan which
(t)csh
Di cshdan tcsh, Anda tidak punya banyak pilihan. Dalam tcsh, itu bagus seperti whichbuiltin. Dalam csh, itu akan menjadi whichperintah sistem , yang mungkin tidak melakukan apa yang Anda inginkan dalam beberapa kasus.
temukan perintah hanya di beberapa shell
Suatu kasus di mana masuk akal untuk digunakan whichadalah jika Anda ingin mengetahui lintasan sebuah perintah, mengabaikan potensi builtin shell atau fungsi dalam bash, csh(tidak tcsh) dash, atau Bourneskrip shell, yaitu shell yang tidak memiliki whence -p(suka kshatau zsh) , command -ev(seperti yash), whatis -p( rc, akanga) atau builtin which(suka tcshatau zsh) pada sistem di mana whichtersedia dan bukan cshskrip.
Jika persyaratan tersebut dipenuhi, maka:
echo=$(which echo)
akan memberikan jalan yang pertama echodi $PATH(kecuali dalam kasus sudut), terlepas dari apakah echojuga terjadi menjadi shell builtin / alias / fungsi atau tidak.
Di shell lain, Anda lebih suka:
- zsh :
echo==echoatau echo=$commands[echo]atauecho=${${:-echo}:c}
- ksh , zsh :
echo=$(whence -p echo)
- yash :
echo=$(command -ev echo)
- rc , akanga :
echo=`whatis -p echo`(waspadalah terhadap jalur dengan spasi)
- ikan :
set echo (type -fp echo)
Perhatikan bahwa jika semua yang Anda ingin lakukan adalah menjalankan bahwa echoperintah, Anda tidak harus mendapatkan jalan, Anda hanya dapat melakukan:
env echo this is not echoed by the builtin echo
Misalnya, dengan tcsh, untuk mencegah builtin whichdigunakan:
set Echo = "`env which echo`"
ketika Anda membutuhkan perintah eksternal
Kasus lain di mana Anda mungkin ingin menggunakan whichadalah ketika Anda benar - benar membutuhkan perintah eksternal. POSIX mensyaratkan bahwa semua shell builtin (seperti command) juga tersedia sebagai perintah eksternal, tetapi sayangnya itu tidak berlaku untuk commandbanyak sistem. Sebagai contoh, jarang menemukan commandperintah pada sistem operasi berbasis Linux sementara kebanyakan dari mereka memiliki whichperintah (meskipun berbeda dengan opsi dan perilaku yang berbeda).
Kasus di mana Anda mungkin menginginkan perintah eksternal berada di mana pun Anda akan menjalankan perintah tanpa menggunakan shell POSIX.
Fungsi system("some command line"), popen()... C atau berbagai bahasa memang memanggil shell untuk mem-parsing baris perintah itu, jadi system("command -v my-cmd")kerjakanlah di dalamnya. Pengecualian untuk hal itu adalah perlyang mengoptimalkan shell jika tidak melihat karakter khusus shell (selain ruang). Itu juga berlaku untuk operator backtick-nya:
$ 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 bahwa :;kekuatan di atas perluntuk memanggil shell di sana. Dengan menggunakan which, Anda tidak perlu menggunakan trik itu.
whichmengasumsikan konteks shell interaktif. Pertanyaan ini ditandai / mudah dibawa. Jadi saya menafsirkan pertanyaan dalam konteks ini sebagai "apa yang harus digunakan daripadawhichmenemukan executable pertama dari nama yang diberikan dalam$PATH". Sebagian besar jawaban dan alasan menentangwhichberurusan dengan alias, bawaan dan fungsi, yang dalam kebanyakan skrip shell portabel di dunia nyata hanya untuk kepentingan akademis. Alias yang didefinisikan secara lokal tidak diwariskan saat menjalankan skrip shell (kecuali jika Anda sumbernya dengan.).