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…)).
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
sampaiexecve
panggilan sistem tidak kembali dengan kesalahan. Misalnya jika$PATH
berisi/foo:/bar
dan Anda ingin menjalankanls
, 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 menjalankanls
mungkin benar-benar menjalankan/bar/ls
jika/foo/ls
bukan executable yang valid. - jika
foo
adalah builtin atau fungsi atau alias,command -v foo
mengembalikanfoo
. Dengan beberapa shell sepertiash
,pdksh
atauzsh
, itu juga dapat mengembalikanfoo
jika$PATH
termasuk string kosong dan adafoo
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 busyboxsh
), dan misalnyabash
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 menjalankancommand -v
tidak akan berlaku lagi setelah Andacd
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
…), tetapicommand -v chmod
akan mengembalikan/opt/ast/bin/chmod
bahkan jika jalur itu 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 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
atauecho=$commands[echo]
atauecho=${${:-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.