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 which
perintah 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-cmd
perintah 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
. csh
memperkenalkan alias sekitar tahun 1978 (meskipun csh
pertama kali dirilis pada 2BSD
bulan Mei 1979), dan juga pemrosesan .cshrc
bagi pengguna untuk menyesuaikan shell (setiap shell, seperti yang csh
dibaca .cshrc
bahkan 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 rc
file ( .profile
yaitu 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), which
skrip csh ditambahkan bagi csh
pengguna untuk membantu mengidentifikasi sebuah executable, dan skrip ini hampir tidak berbeda dengan yang dapat Anda temukan which
di banyak Unix komersial saat ini (seperti Solaris, HP / UX, AIX atau Tru64).
Skrip itu membaca pengguna ~/.cshrc
(seperti semua csh
skrip lakukan kecuali dipanggil dengan csh -f
), dan mencari nama perintah yang disediakan dalam daftar alias dan dalam $path
(array yang csh
mempertahankan berdasarkan $PATH
).
Ini dia, which
menjadi yang pertama untuk shell paling populer saat itu (dan csh
masih populer sampai pertengahan 90-an), yang merupakan alasan utama mengapa ia didokumentasikan dalam buku dan masih banyak digunakan.
Perhatikan bahwa, bahkan untuk csh
pengguna, which
skrip 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 source
lain csh
, dan (meskipun itu bukan ide yang baik), PATH
mungkin didefinisikan ulang ~/.cshrc
.
Menjalankan which
perintah 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 type
perintah 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.
type
Perintah awal mengalami masalah yang sama seperti which
skrip yang tidak mengembalikan status keluar kegagalan jika perintah tidak ditemukan. Juga, untuk executable, sebaliknya which
, ini menghasilkan sesuatu seperti ls is /bin/ls
bukannya /bin/ls
yang membuatnya kurang mudah digunakan dalam skrip.
Unix Version 8's (tidak dirilis di wild) Bourne shell telah type
dibangun ulang namanya menjadi whatis
. Dan shell Plan9 (penerus Unix yang pernah menjadi) juga rc
(dan turunannya seperti akanga
dan es
) whatis
juga.
Shell Korn (subset yang mendasari definisi sh POSIX), dikembangkan pada pertengahan 80-an tetapi tidak banyak tersedia sebelum 1988, menambahkan banyak csh
fitur (editor baris, alias ...) di atas shell Bourne . Ia menambahkan whence
builtin -nya sendiri (sebagai tambahan type
) yang mengambil beberapa opsi ( -v
untuk menyediakan type
keluaran verbose-like, dan -p
hanya 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), zsh
keluar di antara tahun 1989 dan 1991.
Ash, meskipun dimaksudkan sebagai pengganti shell Bourne tidak memiliki type
builtin sampai jauh kemudian (di NetBSD 1.3 dan FreeBSD 2.3), meskipun sudah 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
opsi untuk type
mencetak jalur ( type -p
akan seperti whence -p
) dan -a
melaporkan semua perintah yang cocok. tcsh
dibuat which
builtin dan menambahkan where
perintah bertindak seperti bash
's type -a
. zsh
memiliki semuanya.
The fish
shell (2005) memiliki type
perintah diimplementasikan sebagai fungsi.
The which
Script 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
, whereis
berperilaku seperti which
kecuali bahwa itu hanya mendongak executable di $PATH
). Di OpenBSD dan FreeBSD, which
juga diubah menjadi satu yang ditulis dalam C yang mencari perintah $PATH
saja.
Implementasi
Ada lusinan implementasi which
perintah pada berbagai Unix dengan sintaks dan perilaku yang berbeda.
Di Linux (di samping yang builtin di dalam 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 merupakan yang paling mewah. Ia mencoba untuk memperluas apa yang dilakukan which
skrip 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 bash
melakukannya) .
zsh
memiliki beberapa operator untuk memperluas ke jalur yang dapat dieksekusi: operator =
ekspansi nama file dan :c
pengubah ekspansi riwayat (di sini diterapkan pada ekspansi parameter ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zsh
, dalam zsh/parameters
modul juga membuat tabel hash perintah sebagai commands
array asosiatif:
$ print -r -- $commands[ls]
/bin/ls
The whatis
utilitas (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).
whereis
juga ditambahkan pada 3BSD
saat yang sama seolah- which
olah itu ditulis C
, tidak csh
dan 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 -v
dan -V
(yang dulunya opsional sampai POSIX.2008). UNIX menentukan type
perintah (tidak ada opsi). Itu semua ( where
, which
, whence
tidak ditentukan dalam standar apapun)
Hingga beberapa versi, type
dan command -v
merupakan opsional dalam spesifikasi Pangkalan Linux Standar yang menjelaskan mengapa misalnya beberapa versi lama posh
(meskipun berdasarkan pdksh
keduanya) tidak memiliki keduanya. command -v
juga ditambahkan ke beberapa implementasi shell Bourne (seperti pada Solaris).
Status Hari Ini
Status saat ini adalah bahwa type
dan command -v
ada di mana-mana di semua cangkang Bourne-like (meskipun, seperti dicatat oleh @jarno, perhatikan peringatan / bug bash
ketika tidak dalam mode POSIX atau beberapa keturunan cangkang Almquist di bawah ini dalam komentar). tcsh
adalah satu-satunya shell di mana Anda ingin menggunakan which
(karena tidak type
ada dan which
dibangun).
Di dalam shell selain tcsh
dan zsh
, which
dapat memberi tahu Anda jalur executable yang diberikan selama tidak ada alias atau fungsi dengan nama yang sama di salah satu dari kami ~/.cshrc
, ~/.bashrc
atau file startup shell apa pun dan Anda tidak menentukan $PATH
di ~/.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 where
di tcsh
atau zsh
, type -a
dalam bash
atau zsh
, whence -a
di ksh93 dan kerang lainnya, Anda dapat menggunakan type
dalam kombinasi dengan which -a
yang 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 $PATH
komponen 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 $PATH
sampai execve
panggilan sistem tidak kembali dengan kesalahan . Misalnya jika $PATH
berisi /foo:/bar
dan Anda ingin mengeksekusi ls
, mereka akan terlebih dahulu mencoba mengeksekusi /foo/ls
atau jika itu gagal /bar/ls
. Sekarang eksekusi/foo/ls
mungkin gagal karena Anda tidak memiliki izin eksekusi tetapi juga karena banyak alasan lain, seperti itu bukan eksekusi yang valid. command -v ls
akan melaporkan /foo/ls
jika Anda memiliki izin eksekusi /foo/ls
, tetapi menjalankan ls
mungkin benar-benar berjalan /bar/ls
jika /foo/ls
bukan merupakan eksekusi yang valid.
- jika
foo
merupakan builtin atau fungsi atau alias, command -v foo
kembali foo
. Dengan beberapa shell seperti ash
, pdksh
atau zsh
, ia juga dapat kembali foo
jika $PATH
menyertakan string kosong dan ada foo
file 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, mount
kadang-kadang dibangun untuk busybox sh
), dan misalnya bash
bisa mendapatkan fungsi dari lingkungan.
- jika
$PATH
berisi komponen path 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 path absolut. Jadi jalur yang Anda dapatkan pada saat Anda menjalankan command -v
tidak akan lagi berlaku setelah Anda di cd
tempat 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 chmod
akan kembali /opt/ast/bin/chmod
bahkan 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 csh
dan tcsh
, Anda tidak punya banyak pilihan. Dalam tcsh
, itu bagus seperti which
builtin. Dalam csh
, itu akan menjadi which
perintah 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 which
adalah jika Anda ingin mengetahui lintasan sebuah perintah, mengabaikan potensi builtin shell atau fungsi dalam bash
, csh
(tidak tcsh
) dash
, atau Bourne
skrip shell, yaitu shell yang tidak memiliki whence -p
(suka ksh
atau zsh
) , command -ev
(seperti yash
), whatis -p
( rc
, akanga
) atau builtin which
(suka tcsh
atau zsh
) pada sistem di mana which
tersedia dan bukan csh
skrip.
Jika persyaratan tersebut dipenuhi, maka:
echo=$(which echo)
akan memberikan jalan yang pertama echo
di $PATH
(kecuali dalam kasus sudut), terlepas dari apakah echo
juga terjadi menjadi shell builtin / alias / fungsi atau tidak.
Di shell lain, Anda lebih suka:
- zsh :
echo==echo
atau 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 echo
perintah, 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 which
digunakan:
set Echo = "`env which echo`"
ketika Anda membutuhkan perintah eksternal
Kasus lain di mana Anda mungkin ingin menggunakan which
adalah 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 command
banyak sistem. Sebagai contoh, 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 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 perl
yang 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 perl
untuk memanggil shell di sana. Dengan menggunakan which
, Anda tidak perlu menggunakan trik itu.
which
mengasumsikan konteks shell interaktif. Pertanyaan ini ditandai / mudah dibawa. Jadi saya menafsirkan pertanyaan dalam konteks ini sebagai "apa yang harus digunakan daripadawhich
menemukan executable pertama dari nama yang diberikan dalam$PATH
". Sebagian besar jawaban dan alasan menentangwhich
berurusan 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.
).