Apa itu "program"? Apakah itu termasuk fungsi dan alias? whichmengembalikan true untuk ini. typetanpa argumen juga akan mengembalikan true untuk kata-kata yang dicadangkan dan shell builtin. Jika "program" berarti "excutable in $PATH", maka lihat jawaban ini .
hash <the_command># For regular commands. Or...
type <the_command># To check built-ins and keywords
Penjelasan
Hindari which. Tidak hanya itu proses eksternal yang Anda luncurkan untuk melakukan sangat sedikit (artinya seperti bawaan hash, typeatau commandlebih murah), Anda juga dapat mengandalkan bawaan untuk benar-benar melakukan apa yang Anda inginkan, sementara efek dari perintah eksternal dapat dengan mudah bervariasi dari sistem ke sistem.
Mengapa peduli
Banyak sistem operasi whichyang bahkan tidak menetapkan status keluar , yang berarti if which foobahkan tidak akan berfungsi di sana dan akan selalu melaporkan yang fooada, bahkan jika tidak (perhatikan bahwa beberapa shell POSIX juga melakukan hal ini hash).
Banyak sistem operasi whichmelakukan hal-hal kebiasaan dan jahat seperti mengubah output atau bahkan menghubungkan ke manajer paket.
Jadi, jangan gunakan which. Alih-alih gunakan salah satu dari ini:
$ command -v foo >/dev/null 2>&1||{ echo >&2"I require foo but it's not installed. Aborting."; exit 1;}
$ type foo >/dev/null 2>&1||{ echo >&2"I require foo but it's not installed. Aborting."; exit 1;}
$ hash foo 2>/dev/null ||{ echo >&2"I require foo but it's not installed. Aborting."; exit 1;}
(Minor-note: beberapa akan menyarankan 2>&-adalah sama 2>/dev/nulltetapi lebih pendek - ini tidak benar . 2>&-Menutup FD 2 yang menyebabkan kesalahan dalam program ketika mencoba untuk menulis ke stderr, yang sangat berbeda dari berhasil menulis ke sana dan membuang output (dan berbahaya!))
Jika hash bang /bin/shAnda maka Anda harus peduli dengan apa yang dikatakan POSIX. typedan hashkode keluar tidak terlalu didefinisikan dengan baik oleh POSIX, dan hashterlihat berhasil keluar ketika perintah belum ada (belum melihat ini dengan sebelumnya type). commandStatus keluar didefinisikan dengan baik oleh POSIX, sehingga seseorang mungkin paling aman untuk digunakan.
Jika skrip Anda menggunakan bash, aturan POSIX tidak terlalu penting lagi dan keduanyatype dan hashmenjadi sangat aman untuk digunakan. typesekarang memiliki -Puntuk mencari hanya PATHdan hashmemiliki efek samping bahwa lokasi perintah akan di-hash (untuk pencarian lebih cepat saat Anda menggunakannya), yang biasanya merupakan hal yang baik karena Anda mungkin memeriksa keberadaannya agar benar-benar menggunakannya .
Sebagai contoh sederhana, inilah fungsi yang berjalan gdatejika ada, jika tidak date:
gnudate(){if hash gdate 2>/dev/null;then
gdate "$@"else
date "$@"fi}
@ Geert: Bagian &> / dev / null menyembunyikan pesan 'ketik' yang dipancarkan ketika 'foo' tidak ada. > & 2 pada echo memastikan untuk mengirim pesan kesalahan ke kesalahan standar alih-alih output standar; karena itulah konvensi. Keduanya muncul di terminal Anda, tetapi kesalahan standar jelas merupakan output yang disukai untuk pesan kesalahan dan peringatan yang tidak terduga.
Bagi mereka yang tidak terbiasa dengan pengalihan i / o 'maju' di bash: 1) 2>&-("tutup file output deskriptor 2", yang merupakan stderr) memiliki hasil yang sama dengan 2> /dev/null; 2) >&2adalah jalan pintas untuk 1>&2, yang dapat Anda kenali sebagai "redirect stdout to stderr". Lihat halaman pengarahan ulang Panduan Script Bash Lanjutan / i untuk informasi lebih lanjut.
mikewaters
9
@mikewaters ABS terlihat cukup canggih dan menggambarkan berbagai fungsi CLI bash dan non-bash, tetapi sangat lalai dalam banyak aspek dan tidak mengikuti praktik yang baik. Saya tidak memiliki cukup ruang dalam komentar ini untuk melakukan penulisan; tapi aku bisa menyisipkan contoh acak beberapa kode BAD: while read element ; do .. done <<< $(echo ${ArrayVar[*]}), for word in $(fgrep -l $ORIGINAL *.txt), ls -l "$directory" | sed 1d , {{untuk di seq $BEGIN $END}}, ... Banyak telah mencoba untuk menghubungi penulis dan mengusulkan perbaikan tapi itu tidak ada wiki dan permintaan telah mendarat di telinga tuli.
lhunath
56
@mikewaters 2>&-adalah tidak sama dengan 2>/dev/null. Yang pertama menutup deskriptor file, sedangkan yang kedua hanya mengarahkan ke /dev/null. Anda mungkin tidak melihat kesalahan karena program mencoba memberi tahu Anda tentang stderr bahwa stderr ditutup.
nyuszika7h
577
Berikut ini adalah cara portabel untuk memeriksa apakah ada perintah $PATHdan dapat dieksekusi:
[-x "$(command -v foo)"]
Contoh:
if![-x "$(command -v git)"];then
echo 'Error: git is not installed.'>&2
exit 1fi
Pemeriksaan yang dapat dieksekusi diperlukan karena bash mengembalikan file yang tidak dapat dieksekusi jika tidak ditemukan file yang dapat dieksekusi dengan nama itu $PATH .
Juga perhatikan bahwa jika file yang tidak dapat dieksekusi dengan nama yang sama dengan yang dapat dieksekusi ada sebelumnya $PATH, dash mengembalikan yang pertama, meskipun yang terakhir akan dieksekusi. Ini adalah bug dan melanggar standar POSIX. [ Laporan bug ] [ Standar ]
Selain itu, ini akan gagal jika perintah yang Anda cari telah didefinisikan sebagai alias.
Akankah command -vmenghasilkan path bahkan untuk file yang tidak dapat dieksekusi? Artinya, -x benar-benar diperlukan?
einpoklum
5
@einpoklum -xmenguji apakah file tersebut dapat dieksekusi, yang merupakan pertanyaannya.
Ken Sharp
3
@ KenSharp: Tapi itu sepertinya mubazir, karena commanditu sendiri akan menguji untuk dieksekusi - bukan?
einpoklum
13
@einpoklum Ya, itu perlu. Bahkan, bahkan solusi ini dapat pecah dalam satu kasus tepi. Terima kasih sudah membawa ini padaku. tanda hubung, bash, dan zsh semua melompati file yang tidak dapat dieksekusi $PATHsaat menjalankan perintah. Namun, perilaku command -vtersebut sangat tidak konsisten. Di dasbor, ia mengembalikan file yang cocok pertama $PATH, terlepas dari apakah itu dapat dieksekusi atau tidak. Dalam bash, ia mengembalikan kecocokan yang dapat dieksekusi pertama kali $PATH, tetapi jika tidak ada, ia dapat mengembalikan file yang tidak dapat dieksekusi. Dan di zsh, itu tidak akan pernah mengembalikan file yang tidak dapat dieksekusi.
nyuszika7h
5
Sejauh yang saya tahu, dashadalah satu-satunya dari tiga yang tidak sesuai dengan POSIX; [ -x "$(command -v COMMANDNAME)"]akan bekerja di dua lainnya. Sepertinya bug ini sudah dilaporkan tetapi belum mendapat respons: bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264
nyuszika7h
210
Saya setuju dengan lhunath untuk mencegah penggunaan which, dan solusinya sangat valid untuk pengguna Bash . Namun, agar lebih portabel, command -vharus digunakan sebagai gantinya:
$ command -v foo >/dev/null 2>&1||{ echo "I require foo but it's not installed. Aborting.">&2; exit 1;}
Sama seperti di atas - exit 1;membunuh xterm, jika dipanggil dari sana.
pengguna tidak diketahui
1
Ini tidak akan berfungsi pada standar sh: Anda &> bukan instruksi pengalihan yang valid.
jyavenard
7
@jyavenard: Pertanyaannya diberi tag bash , karenanya notasi pengalihan bash-spesifik lebih ringkas &>/dev/null. Namun, saya setuju dengan Anda, yang benar-benar penting adalah portabilitas, saya telah mengedit jawaban saya sesuai, sekarang menggunakan pengalihan standar >/dev/null 2>&1.
GregV
untuk lebih meningkatkan jawaban ini, saya akan melakukan dua hal: 1: gunakan "&>" untuk menyederhanakannya, seperti jawaban Josh. 2: pecahkan {} menjadi baris tambahan, letakkan tab di depan gema, agar mudah dibaca
&>mungkin tidak tersedia di versi Bash Anda. Kode Marcello seharusnya bekerja dengan baik; itu melakukan hal yang sama.
Josh Strater
3
Gagal pada bawaan dan kata-kata yang dicadangkan: Coba ini dengan kata thenmisalnya. Lihat jawaban ini jika Anda membutuhkan eksekutabel di $PATH.
Tom Hale
84
Itu tergantung pada apakah Anda ingin tahu apakah itu ada di salah satu direktori dalam $PATHvariabel atau apakah Anda tahu lokasi absolutnya. Jika Anda ingin tahu apakah itu ada dalam $PATHvariabel, gunakan
if which programname >/dev/null;then
echo exists
else
echo does not exist
fi
jika tidak gunakan
if[-x /path/to/programname ];then
echo exists
else
echo does not exist
fi
Pengalihan ke /dev/null/dalam contoh pertama menekan output dari whichprogram.
Kesediaan untuk belajar dan meningkatkan harus dihargai. +1 Ini bersih dan sederhana. Satu-satunya hal yang dapat saya tambahkan adalah bahwa commandberhasil bahkan untuk alias, yang mungkin agak berlawanan dengan intuisi. Memeriksa keberadaan dalam shell interaktif akan memberikan hasil yang berbeda dari ketika Anda memindahkannya ke skrip.
Palec
1
Saya baru saja menguji dan menggunakan shopt -u expand_aliasesabaikan / sembunyikan alias (seperti yang alias ls='ls -F'disebutkan dalam jawaban lain) dan shopt -s expand_aliasesselesaikan melalui command -v. Jadi mungkin itu harus ditetapkan sebelum pemeriksaan dan tidak disetel setelah, meskipun itu dapat mempengaruhi nilai kembali fungsi jika Anda tidak menangkap dan mengembalikan output dari panggilan perintah secara eksplisit.
Itu berarti Anda harus sudah mengetahui jalur lengkap ke aplikasi.
lhunath
12
OP tidak menentukan apakah dia ingin memeriksa untuk contoh spesifik atau untuk setiap instance yang dapat dieksekusi ... Saya menjawabnya dengan cara saya membacanya.
hash foo &>/dev/null
if[ $?-eq 1];then
echo >&2"foo not found."fi
Script ini berjalan hashdan kemudian memeriksa apakah kode keluar dari perintah terbaru, nilai yang disimpan $?, sama dengan 1. Jika hashtidak ditemukan foo, kode keluarnya adalah 1. Jika fooada, kode keluar akan menjadi0 .
&> /dev/nullpengalihan standar error dan output standar dari hashsehingga tidak muncul pada layar dan echo >&2menulis pesan ke standard error.
Kenapa tidak adil if hash foo &> /dev/null; then ...?
Beni Cherniavsky-Paskin
9
Saya tidak pernah mendapatkan jawaban sebelumnya untuk bekerja pada kotak yang saya akses. Untuk satu, typetelah diinstal (melakukan apa more). Jadi direktif builtin diperlukan. Perintah ini bekerja untuk saya:
if[`builtin type -p vim`];then echo "TRUE";else echo "FALSE";fi
Kurung bukan bagian dari ifsintaks, cukup gunakan if builtin type -p vim; then .... Dan backtick adalah sintaks yang benar-benar kuno dan usang, $()didukung bahkan oleh shsemua sistem modern.
nyuszika7h
9
Periksa beberapa dependensi dan informasikan status ke pengguna akhir
for cmd in latex pandoc;do
printf '%-10s'"$cmd"if hash "$cmd"2>/dev/null;then
echo OK
else
echo missing
fidone
Cara non-verbal untuk melakukannya: 1) singkirkan specifier lebar; 2) tambahkan spasi setelah printf nama perintah Anda; 3) pipa untuk loop Anda ke column -t(bagian dari util-linux).
Patrice Levesque
8
Jika Anda memeriksa keberadaan program, Anda mungkin akan menjalankannya nanti. Mengapa tidak mencoba menjalankannya?
if foo --version >/dev/null 2>&1;then
echo Foundelse
echo Not found
fi
Ini adalah pemeriksaan yang lebih dapat dipercaya bahwa program berjalan daripada hanya melihat direktori PATH dan izin file.
Plus, Anda bisa mendapatkan hasil yang bermanfaat dari program Anda, seperti versinya.
Tentu saja kekurangannya adalah bahwa beberapa program bisa berat untuk memulai dan beberapa tidak memiliki --versionopsi untuk segera (dan berhasil) keluar.
ketik -P nama program lebih disukai, lihat jawaban yang diterima
RobertG
@ RobertTG Yang saya lihat adalah itu -Pbukan POSIX. Mengapa type -Plebih disukai?
mikemaccana
Saya seharusnya mengatakan bahwa "lebih disukai di lingkungan bash" - karena saya bermaksud untuk menjawab komentar sebelumnya khusus bash. Bagaimanapun, itu bertahun-tahun yang lalu - saya kira saya harus, sekali lagi, mengarahkan Anda ke jawaban yang ditandai sebagai "tertipu"
RobertG
4
Perintah -vberfungsi dengan baik jika opsi POSIX_BUILTINS diatur untuk<command> untuk menguji, tetapi bisa gagal jika tidak. (Ini telah bekerja untuk saya selama bertahun-tahun, tetapi saya baru-baru ini bertemu dengan yang tidak berfungsi.)
Saya menemukan yang berikut ini lebih anti-gagal:
test -x $(which <command>)
Karena tes untuk tiga hal: jalur, keberadaan dan izin eksekusi.
Tidak bekerja test -x $(which ls)mengembalikan 0, seperti halnya test -x $(which sudo), meskipun lssudah diinstal dan dapat dijalankan dan sudobahkan tidak diinstal dalam wadah buruh pelabuhan tempat saya menjalankan.
algal
@algal Anda harus menggunakan tanda kutip, saya kiratest -x "$(which <command>)"
JoniVR
@algal Mungkin lsalias? Saya tidak berpikir itu akan berhasil jika perintah memiliki parameter.
AnthonyC
3
Bagi yang berminat, tidak ada metodologi dalam jawaban sebelumnya yang berfungsi jika Anda ingin mendeteksi pustaka yang diinstal. Saya membayangkan Anda dibiarkan dengan memeriksa path secara fisik (berpotensi untuk file header dan semacamnya), atau sesuatu seperti ini (jika Anda menggunakan distribusi berbasis Debian):
Seperti yang dapat Anda lihat dari atas, jawaban "0" dari kueri berarti paket tersebut tidak diinstal. Ini adalah fungsi dari "grep" - a "0" berarti kecocokan ditemukan, "1" berarti tidak ada kecocokan yang ditemukan.
Saya akan mengatakan tidak ada cara portabel dan 100% dapat diandalkan karena menggantung aliases. Sebagai contoh:
alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/
Tentu saja, hanya yang terakhir yang bermasalah (jangan tersinggung oleh Ringo!). Tapi semuanya valid aliasdari sudut pandang command -v.
Untuk menolak yang menjuntai seperti ringo, kita harus mengurai output dari perintah built-in shell aliasdan recurse ke dalamnya ( command -vbukan superior untukalias sini.) Tidak ada solusi portabel untuk itu, dan bahkan Bash- solusi spesifik agak membosankan.
Perhatikan bahwa solusi seperti ini akan menolak tanpa syarat alias ls='ls -F':
Ini mengembalikan 0 jika executable ditemukan dan mengembalikan 1 jika tidak ditemukan atau tidak dapat dieksekusi:
NAME
which - locate a command
SYNOPSIS
which [-a] filename ...
DESCRIPTION
which returns the pathnames of the files which would
be executed in the current environment, had its
arguments been given as commands in a strictly
POSIX-conformant shell. It does this by searching
the PATH for executable files matching the names
of the arguments.
OPTIONS
-a print all matching pathnames of each argument
EXIT STATUS
0 if all specified commands are
found and executable
1 if one or more specified commands is nonexistent
or not executable
2 if an invalid option is specified
Yang menyenangkan tentang itu whichadalah mengetahui apakah executable tersedia di lingkungan yang whichdijalankan - ini menghemat beberapa masalah ...
Gunakan yang jika Anda mencari executable bernama foo, tetapi lihat jawaban saya jika Anda ingin memeriksa file / path / ke / a / bernama / foo tertentu. Juga perhatikan bahwa yang mungkin tidak tersedia pada beberapa sistem minimal, meskipun harus ada pada setiap instalasi lengkap ...
dmckee --- ex-moderator kitten
9
Jangan mengandalkan status keluar yang. Banyak sistem operasi memiliki yang yang bahkan tidak menetapkan status keluar selain 0.
Jika kalian / gals tidak bisa mendapatkan jawaban di sini untuk bekerja dan menarik rambut dari punggungmu, cobalah untuk menjalankan perintah yang sama menggunakan bash -c . Lihat saja delirium somnambular ini. Inilah yang sebenarnya terjadi ketika Anda menjalankan $ (sub-perintah):
Pertama. Ini dapat memberi Anda output yang sama sekali berbeda.
$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"/bin/ls
Kedua. Itu tidak bisa memberi Anda hasil sama sekali.
Perbedaan tersebut disebabkan oleh perbedaan antara mode shell interaktif dan non-interaktif. ~ / .Bashrc Anda hanya dibaca ketika shell non-login dan interaktif. Yang kedua terlihat aneh, karena ini harus disebabkan oleh perbedaan dalam variabel lingkungan PATH, tetapi subkulit mewarisi lingkungan.
Palec
Dalam kasus saya .bashrcada yang [ -z "$PS1" ] && returnditulis oleh # If not running interactively, don't do anythingjadi saya kira itu adalah alasan mengapa bahkan sumber eksplisit bashrc dalam mode non-interaktif tidak membantu. Masalahnya dapat diatasi dengan memanggil skrip dengan operator titik ss64.com/bash/source.html. ./script.sh tapi itu bukan hal yang ingin diingat untuk mengetik setiap kali.
user619271
1
Sumber skrip yang tidak seharusnya bersumber adalah ide yang buruk. Yang saya coba katakan adalah bahwa jawaban Anda tidak ada hubungannya dengan pertanyaan yang diajukan dan banyak hubungannya dengan Bash dan mode (non-) interaktifnya.
Palec
Jika itu menjelaskan apa yang terjadi dalam kasus-kasus ini, itu akan menjadi tambahan yang bermanfaat untuk sebuah jawaban.
Palec
0
Varian hash memiliki satu perangkap: Di baris perintah Anda dapat misalnya mengetik
one_folder/process
untuk memiliki proses dieksekusi. Untuk ini folder induk dari one_folder harus dalam $ PATH . Tetapi ketika Anda mencoba untuk hash perintah ini, itu akan selalu berhasil:
hash one_folder/process; echo $?# will always output '0'
"Untuk ini folder induk one_folder harus dalam $PATH" —Ini benar-benar tidak akurat. Cobalah. Agar ini berfungsi, one_folder harus ada di direktori saat ini .
Wildcard
0
Saya kedua penggunaan "command -v". Misal seperti ini:
md=$(command -v mkdirhier); alias md=${md:=mkdir}# bash
emacs="$(command -v emacs) -nw"|| emacs=nano
alias e=$emacs
[[-z $(command -v jed)]]&& alias jed=$emacs
Saya harus memeriksa apakah Git diinstal sebagai bagian dari penerapan server CI kami . Skrip Bash terakhir saya adalah sebagai berikut (server Ubuntu):
if! builtin type -p git &>/dev/null;then
sudo apt-get -y install git-core
fi
Syaratnya agak tidak berguna, modulo waktu startup untuk menjalankan apt-get, karena apt-get akan puas dan keluar jika git-core sudah diinstal.
tripleee
3
Waktu startup-nya tidak dapat diabaikan, tetapi motivasi yang lebih penting adalah sudo: tanpa syarat, itu akan selalu berhenti dan meminta kata sandi (kecuali jika Anda melakukan sudo baru-baru ini). BTW, mungkin berguna untuk melakukannya sudo -p "Type your password to install missing git-core: "agar prompt tidak muncul tiba-tiba.
Beni Cherniavsky-Paskin
0
Untuk meniru Bash type -P cmd, kita bisa menggunakan yang sesuai dengan POSIX env -i type cmd 1>/dev/null 2>&1.
man env
# "The option '-i' causes env to completely ignore the environment it inherits."# In other words, there are no aliases or functions to be looked up by the type command.
ls(){ echo 'Hello, world!';}
ls
type ls
env -i type ls
cmd=ls
cmd=lsx
env -i type $cmd 1>/dev/null 2>&1||{ echo "$cmd not found"; exit 1;}
Mengapa ini dibalik? Pada sistem apa ini bekerja untuk Anda? typetampaknya menjadi builtinsebagian besar shell jadi ini tidak bisa berfungsi karena envdigunakan execvpuntuk menjalankan commandjadi commandtidak bisa menjadi builtin(dan builtinakan selalu dijalankan dalam lingkungan yang sama). Ini gagal untuk saya di bash, ksh93, zsh, busybox [a]shdan dashsemua yang menyediakan typesebagai builtin shell.
Adrian Frühwirth
0
Jika tidak ada typeperintah eksternal yang tersedia (seperti yang diterima begitu saja di sini ), kita dapat menggunakan POSIX compliant env -i sh -c 'type cmd 1>/dev/null 2>&1':
# Portable version of Bash's type -P cmd (without output on stdout)
typep(){
command -p env -i PATH="$PATH" sh -c '
export LC_ALL=C LANG=C
cmd="$1"
cmd="`type "$cmd" 2>/dev/null || { echo "error: command $cmd not found; exiting ..." 1>&2; exit 1; }`"
[ $? != 0 ] && exit 1
case "$cmd" in
*\ /*) exit 0;;
*) printf "%s\n" "error: $cmd" 1>&2; exit 1;;
esac
' _ "$1"|| exit 1}# Get your standard $PATH value#PATH="$(command -p getconf PATH)"
typep ls
typep builtin
typep ls-temp
Setidaknya pada Mac OS X v10.6.8 (Snow Leopard) menggunakan Bash 4.2.24 (2) command -v lstidak cocok dengan yang dipindahkan /bin/ls-temp.
Jika Anda ingin memeriksa apakah suatu program ada dan benar-benar sebuah program, bukan perintah bawaan Bash , maka command, typedanhash tidak yang sesuai untuk menguji karena mereka semua kembali 0 status keluar untuk built-in perintah.
Sebagai contoh, ada waktu Program yang menawarkan fitur lebih dari waktu built-in perintah. Untuk memeriksa apakah program itu ada, saya akan menyarankan menggunakan whichseperti pada contoh berikut:
# First check if the time program exists
timeProg=`which time`if["$timeProg"=""]then
echo "The time program does not exist on this system."
exit 1fi# Invoke the time program
$timeProg --quiet -o result.txt -f "%S %U + p" du -sk ~
echo "Total CPU time: `dc -f result.txt` seconds"
rm result.txt
#!/bin/bash# Commands found in the hash table are checked for existence before being# executed and non-existence forces a normal PATH search.
shopt -s checkhash
function exists(){local mycomm=$1; shift ||return1
hash $mycomm 2>/dev/null || \
printf "\xe2\x9c\x98 [ABRT]: $mycomm: command does not exist\n";return1;}
readonly -f exists
exists notacmd
exists bash
hash
bash -c 'printf "Fin.\n"'
Hasil
✘[ABRT]: notacmd: command does not exist
hits command
0/usr/bin/bash
Fin.
if[`LANG=C type example 2>/dev/null|wc -l`=1];then echo exists;else echo "not exists";fi
atau
if[`LANG=C type example 2>/dev/null|wc -l`=1];then
echo exists
else echo "not exists"fi
Ia menggunakan shell builtins dan status gema program untuk output standar dan tidak ada kesalahan standar. Di sisi lain, jika suatu perintah tidak ditemukan, itu hanya status gema kesalahan standar.
which
mengembalikan true untuk ini.type
tanpa argumen juga akan mengembalikan true untuk kata-kata yang dicadangkan dan shell builtin. Jika "program" berarti "excutable in$PATH
", maka lihat jawaban ini .Jawaban:
Menjawab
Kompatibel dengan POSIX:
Untuk lingkungan spesifik Bash:
Penjelasan
Hindari
which
. Tidak hanya itu proses eksternal yang Anda luncurkan untuk melakukan sangat sedikit (artinya seperti bawaanhash
,type
ataucommand
lebih murah), Anda juga dapat mengandalkan bawaan untuk benar-benar melakukan apa yang Anda inginkan, sementara efek dari perintah eksternal dapat dengan mudah bervariasi dari sistem ke sistem.Mengapa peduli
which
yang bahkan tidak menetapkan status keluar , yang berartiif which foo
bahkan tidak akan berfungsi di sana dan akan selalu melaporkan yangfoo
ada, bahkan jika tidak (perhatikan bahwa beberapa shell POSIX juga melakukan hal inihash
).which
melakukan hal-hal kebiasaan dan jahat seperti mengubah output atau bahkan menghubungkan ke manajer paket.Jadi, jangan gunakan
which
. Alih-alih gunakan salah satu dari ini:(Minor-note: beberapa akan menyarankan
2>&-
adalah sama2>/dev/null
tetapi lebih pendek - ini tidak benar .2>&-
Menutup FD 2 yang menyebabkan kesalahan dalam program ketika mencoba untuk menulis ke stderr, yang sangat berbeda dari berhasil menulis ke sana dan membuang output (dan berbahaya!))Jika hash bang
/bin/sh
Anda maka Anda harus peduli dengan apa yang dikatakan POSIX.type
danhash
kode keluar tidak terlalu didefinisikan dengan baik oleh POSIX, danhash
terlihat berhasil keluar ketika perintah belum ada (belum melihat ini dengan sebelumnyatype
).command
Status keluar didefinisikan dengan baik oleh POSIX, sehingga seseorang mungkin paling aman untuk digunakan.Jika skrip Anda menggunakan
bash
, aturan POSIX tidak terlalu penting lagi dan keduanyatype
danhash
menjadi sangat aman untuk digunakan.type
sekarang memiliki-P
untuk mencari hanyaPATH
danhash
memiliki efek samping bahwa lokasi perintah akan di-hash (untuk pencarian lebih cepat saat Anda menggunakannya), yang biasanya merupakan hal yang baik karena Anda mungkin memeriksa keberadaannya agar benar-benar menggunakannya .Sebagai contoh sederhana, inilah fungsi yang berjalan
gdate
jika ada, jika tidakdate
:sumber
2>&-
("tutup file output deskriptor 2", yang merupakan stderr) memiliki hasil yang sama dengan2> /dev/null
; 2)>&2
adalah jalan pintas untuk1>&2
, yang dapat Anda kenali sebagai "redirect stdout to stderr". Lihat halaman pengarahan ulang Panduan Script Bash Lanjutan / i untuk informasi lebih lanjut.while read element ; do .. done <<< $(echo ${ArrayVar[*]})
,for word in $(fgrep -l $ORIGINAL *.txt)
,ls -l "$directory" | sed 1d
, {{untuk diseq $BEGIN $END
}}, ... Banyak telah mencoba untuk menghubungi penulis dan mengusulkan perbaikan tapi itu tidak ada wiki dan permintaan telah mendarat di telinga tuli.2>&-
adalah tidak sama dengan2>/dev/null
. Yang pertama menutup deskriptor file, sedangkan yang kedua hanya mengarahkan ke/dev/null
. Anda mungkin tidak melihat kesalahan karena program mencoba memberi tahu Anda tentang stderr bahwa stderr ditutup.Berikut ini adalah cara portabel untuk memeriksa apakah ada perintah
$PATH
dan dapat dieksekusi:Contoh:
Pemeriksaan yang dapat dieksekusi diperlukan karena bash mengembalikan file yang tidak dapat dieksekusi jika tidak ditemukan file yang dapat dieksekusi dengan nama itu
$PATH
.Juga perhatikan bahwa jika file yang tidak dapat dieksekusi dengan nama yang sama dengan yang dapat dieksekusi ada sebelumnya
$PATH
, dash mengembalikan yang pertama, meskipun yang terakhir akan dieksekusi. Ini adalah bug dan melanggar standar POSIX. [ Laporan bug ] [ Standar ]Selain itu, ini akan gagal jika perintah yang Anda cari telah didefinisikan sebagai alias.
sumber
command -v
menghasilkan path bahkan untuk file yang tidak dapat dieksekusi? Artinya, -x benar-benar diperlukan?-x
menguji apakah file tersebut dapat dieksekusi, yang merupakan pertanyaannya.command
itu sendiri akan menguji untuk dieksekusi - bukan?$PATH
saat menjalankan perintah. Namun, perilakucommand -v
tersebut sangat tidak konsisten. Di dasbor, ia mengembalikan file yang cocok pertama$PATH
, terlepas dari apakah itu dapat dieksekusi atau tidak. Dalam bash, ia mengembalikan kecocokan yang dapat dieksekusi pertama kali$PATH
, tetapi jika tidak ada, ia dapat mengembalikan file yang tidak dapat dieksekusi. Dan di zsh, itu tidak akan pernah mengembalikan file yang tidak dapat dieksekusi.dash
adalah satu-satunya dari tiga yang tidak sesuai dengan POSIX;[ -x "$(command -v COMMANDNAME)"]
akan bekerja di dua lainnya. Sepertinya bug ini sudah dilaporkan tetapi belum mendapat respons: bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264Saya setuju dengan lhunath untuk mencegah penggunaan
which
, dan solusinya sangat valid untuk pengguna Bash . Namun, agar lebih portabel,command -v
harus digunakan sebagai gantinya:Perintah
command
sesuai dengan POSIX. Lihat di sini untuk spesifikasinya: perintah - jalankan perintah sederhanaCatatan:
type
kompatibel dengan POSIX, tetapitype -P
tidak.sumber
exit 1;
membunuh xterm, jika dipanggil dari sana.&>/dev/null
. Namun, saya setuju dengan Anda, yang benar-benar penting adalah portabilitas, saya telah mengedit jawaban saya sesuai, sekarang menggunakan pengalihan standar>/dev/null 2>&1
.Saya memiliki fungsi yang didefinisikan dalam .bashrc saya yang membuatnya lebih mudah.
Berikut ini contoh penggunaannya (dari saya
.bash_profile
.)sumber
&>
dilakukan?&>
pengalihan kedua stdout dan stderr bersama-sama.&>
mungkin tidak tersedia di versi Bash Anda. Kode Marcello seharusnya bekerja dengan baik; itu melakukan hal yang sama.then
misalnya. Lihat jawaban ini jika Anda membutuhkan eksekutabel di$PATH
.Itu tergantung pada apakah Anda ingin tahu apakah itu ada di salah satu direktori dalam
$PATH
variabel atau apakah Anda tahu lokasi absolutnya. Jika Anda ingin tahu apakah itu ada dalam$PATH
variabel, gunakanjika tidak gunakan
Pengalihan ke
/dev/null/
dalam contoh pertama menekan output dariwhich
program.sumber
Memperluas jawaban @ lhunath dan @ GregV, inilah kode untuk orang-orang yang ingin dengan mudah memasukkan cek itu di dalam
if
pernyataan:Berikut cara menggunakannya:
sumber
command
berhasil bahkan untuk alias, yang mungkin agak berlawanan dengan intuisi. Memeriksa keberadaan dalam shell interaktif akan memberikan hasil yang berbeda dari ketika Anda memindahkannya ke skrip.shopt -u expand_aliases
abaikan / sembunyikan alias (seperti yangalias ls='ls -F'
disebutkan dalam jawaban lain) danshopt -s expand_aliases
selesaikan melaluicommand -v
. Jadi mungkin itu harus ditetapkan sebelum pemeriksaan dan tidak disetel setelah, meskipun itu dapat mempengaruhi nilai kembali fungsi jika Anda tidak menangkap dan mengembalikan output dari panggilan perintah secara eksplisit.Coba gunakan:
atau
Dari manual Bash di bawah Ekspresi Bersyarat :
sumber
Untuk menggunakan
hash
, seperti yang disarankan @lhunath , dalam skrip Bash:Script ini berjalan
hash
dan kemudian memeriksa apakah kode keluar dari perintah terbaru, nilai yang disimpan$?
, sama dengan1
. Jikahash
tidak ditemukanfoo
, kode keluarnya adalah1
. Jikafoo
ada, kode keluar akan menjadi0
.&> /dev/null
pengalihan standar error dan output standar darihash
sehingga tidak muncul pada layar danecho >&2
menulis pesan ke standard error.sumber
if hash foo &> /dev/null; then ...
?Saya tidak pernah mendapatkan jawaban sebelumnya untuk bekerja pada kotak yang saya akses. Untuk satu,
type
telah diinstal (melakukan apamore
). Jadi direktif builtin diperlukan. Perintah ini bekerja untuk saya:sumber
if
sintaks, cukup gunakanif builtin type -p vim; then ...
. Dan backtick adalah sintaks yang benar-benar kuno dan usang,$()
didukung bahkan olehsh
semua sistem modern.Periksa beberapa dependensi dan informasikan status ke pengguna akhir
Output sampel:
Sesuaikan dengan
10
panjang perintah maksimum. Itu tidak otomatis, karena saya tidak melihat cara POSIX non-verbose untuk melakukannya: Bagaimana saya bisa meluruskan kolom dari tabel yang dipisahkan ruang di Bash?Periksa apakah beberapa
apt
paket diinstal dengandpkg -s
dan instal sebaliknya .Lihat: Periksa apakah paket apt-get diinstal dan kemudian instal jika tidak di Linux
Itu sebelumnya disebutkan di: Bagaimana saya bisa memeriksa apakah ada program dari skrip Bash?
sumber
column -t
(bagian dari util-linux).Jika Anda memeriksa keberadaan program, Anda mungkin akan menjalankannya nanti. Mengapa tidak mencoba menjalankannya?
Ini adalah pemeriksaan yang lebih dapat dipercaya bahwa program berjalan daripada hanya melihat direktori PATH dan izin file.
Plus, Anda bisa mendapatkan hasil yang bermanfaat dari program Anda, seperti versinya.
Tentu saja kekurangannya adalah bahwa beberapa program bisa berat untuk memulai dan beberapa tidak memiliki
--version
opsi untuk segera (dan berhasil) keluar.sumber
hash foo 2>/dev/null
: bekerja dengan Z shell (Zsh), Bash, Dash dan ash .type -p foo
: tampaknya berfungsi dengan Z shell, Bash dan ash ( BusyBox ), tetapi tidak Dash (ini diinterpretasikan-p
sebagai argumen).command -v foo
: bekerja dengan Z shell, Bash, Dash, tetapi tidak abu (BusyBox) (-ash: command: not found
).Perhatikan juga bahwa
builtin
tidak tersedia dengan abu dan Dash.sumber
Gunakan Bash bawaan jika Anda dapat:
...
sumber
which
bukan Bash builtin.-P
bukan POSIX. Mengapatype -P
lebih disukai?Perintah
-v
berfungsi dengan baik jika opsi POSIX_BUILTINS diatur untuk<command>
untuk menguji, tetapi bisa gagal jika tidak. (Ini telah bekerja untuk saya selama bertahun-tahun, tetapi saya baru-baru ini bertemu dengan yang tidak berfungsi.)Saya menemukan yang berikut ini lebih anti-gagal:
Karena tes untuk tiga hal: jalur, keberadaan dan izin eksekusi.
sumber
test -x $(which ls)
mengembalikan 0, seperti halnyatest -x $(which sudo)
, meskipunls
sudah diinstal dan dapat dijalankan dansudo
bahkan tidak diinstal dalam wadah buruh pelabuhan tempat saya menjalankan.test -x "$(which <command>)"
ls
alias? Saya tidak berpikir itu akan berhasil jika perintah memiliki parameter.Bagi yang berminat, tidak ada metodologi dalam jawaban sebelumnya yang berfungsi jika Anda ingin mendeteksi pustaka yang diinstal. Saya membayangkan Anda dibiarkan dengan memeriksa path secara fisik (berpotensi untuk file header dan semacamnya), atau sesuatu seperti ini (jika Anda menggunakan distribusi berbasis Debian):
Seperti yang dapat Anda lihat dari atas, jawaban "0" dari kueri berarti paket tersebut tidak diinstal. Ini adalah fungsi dari "grep" - a "0" berarti kecocokan ditemukan, "1" berarti tidak ada kecocokan yang ditemukan.
sumber
cmd; if [ $? -eq 0 ]; then
harus di refactored keif cmd; then
dpkg
atauapt
Ada banyak pilihan di sini, tapi saya terkejut tidak ada satu baris cepat. Inilah yang saya gunakan di awal skrip saya:
Ini didasarkan pada jawaban yang dipilih di sini dan sumber lain.
sumber
Saya akan mengatakan tidak ada cara portabel dan 100% dapat diandalkan karena menggantung
alias
es. Sebagai contoh:Tentu saja, hanya yang terakhir yang bermasalah (jangan tersinggung oleh Ringo!). Tapi semuanya valid
alias
dari sudut pandangcommand -v
.Untuk menolak yang menjuntai seperti
ringo
, kita harus mengurai output dari perintah built-in shellalias
dan recurse ke dalamnya (command -v
bukan superior untukalias
sini.) Tidak ada solusi portabel untuk itu, dan bahkan Bash- solusi spesifik agak membosankan.Perhatikan bahwa solusi seperti ini akan menolak tanpa syarat
alias ls='ls -F'
:sumber
shopt -u expand_aliases
abaikan / sembunyikan alias ini dan tampilkanshopt -s expand_aliases
melaluicommand -v
.Ini akan memberi tahu menurut lokasi jika program ada atau tidak:
sumber
The
which
perintah mungkin berguna. pria yangIni mengembalikan 0 jika executable ditemukan dan mengembalikan 1 jika tidak ditemukan atau tidak dapat dieksekusi:
Yang menyenangkan tentang itu
which
adalah mengetahui apakah executable tersedia di lingkungan yangwhich
dijalankan - ini menghemat beberapa masalah ...sumber
Pengaturan saya untuk seorang Debian server :
Saya memiliki masalah ketika beberapa paket berisi nama yang sama.
Sebagai contoh
apache2
. Jadi ini solusi saya:sumber
Jika kalian / gals tidak bisa mendapatkan jawaban di sini untuk bekerja dan menarik rambut dari punggungmu, cobalah untuk menjalankan perintah yang sama menggunakan
bash -c
. Lihat saja delirium somnambular ini. Inilah yang sebenarnya terjadi ketika Anda menjalankan $ (sub-perintah):Pertama. Ini dapat memberi Anda output yang sama sekali berbeda.
Kedua. Itu tidak bisa memberi Anda hasil sama sekali.
sumber
.bashrc
ada yang[ -z "$PS1" ] && return
ditulis oleh# If not running interactively, don't do anything
jadi saya kira itu adalah alasan mengapa bahkan sumber eksplisit bashrc dalam mode non-interaktif tidak membantu. Masalahnya dapat diatasi dengan memanggil skrip dengan operator titik ss64.com/bash/source.html. ./script.sh
tapi itu bukan hal yang ingin diingat untuk mengetik setiap kali.Varian hash memiliki satu perangkap: Di baris perintah Anda dapat misalnya mengetik
untuk memiliki proses dieksekusi. Untuk ini folder induk dari one_folder harus dalam $ PATH . Tetapi ketika Anda mencoba untuk hash perintah ini, itu akan selalu berhasil:
sumber
$PATH
" —Ini benar-benar tidak akurat. Cobalah. Agar ini berfungsi, one_folder harus ada di direktori saat ini .Saya kedua penggunaan "command -v". Misal seperti ini:
sumber
Saya harus memeriksa apakah Git diinstal sebagai bagian dari penerapan server CI kami . Skrip Bash terakhir saya adalah sebagai berikut (server Ubuntu):
sumber
sudo
: tanpa syarat, itu akan selalu berhenti dan meminta kata sandi (kecuali jika Anda melakukan sudo baru-baru ini). BTW, mungkin berguna untuk melakukannyasudo -p "Type your password to install missing git-core: "
agar prompt tidak muncul tiba-tiba.Untuk meniru Bash
type -P cmd
, kita bisa menggunakan yang sesuai dengan POSIXenv -i type cmd 1>/dev/null 2>&1
.sumber
type
tampaknya menjadibuiltin
sebagian besar shell jadi ini tidak bisa berfungsi karenaenv
digunakanexecvp
untuk menjalankancommand
jadicommand
tidak bisa menjadibuiltin
(danbuiltin
akan selalu dijalankan dalam lingkungan yang sama). Ini gagal untuk saya dibash
,ksh93
,zsh
,busybox [a]sh
dandash
semua yang menyediakantype
sebagai builtin shell.Jika tidak ada
type
perintah eksternal yang tersedia (seperti yang diterima begitu saja di sini ), kita dapat menggunakan POSIX compliantenv -i sh -c 'type cmd 1>/dev/null 2>&1'
:Setidaknya pada Mac OS X v10.6.8 (Snow Leopard) menggunakan Bash 4.2.24 (2)
command -v ls
tidak cocok dengan yang dipindahkan/bin/ls-temp
.sumber
Jika Anda ingin memeriksa apakah suatu program ada dan benar-benar sebuah program, bukan perintah bawaan Bash , maka
command
,type
danhash
tidak yang sesuai untuk menguji karena mereka semua kembali 0 status keluar untuk built-in perintah.Sebagai contoh, ada waktu Program yang menawarkan fitur lebih dari waktu built-in perintah. Untuk memeriksa apakah program itu ada, saya akan menyarankan menggunakan
which
seperti pada contoh berikut:sumber
Saya ingin pertanyaan yang sama dijawab tetapi dijalankan dalam Makefile.
sumber
Naskah
Hasil
sumber
Saya menggunakan ini, karena sangat mudah:
atau
Ia menggunakan shell builtins dan status gema program untuk output standar dan tidak ada kesalahan standar. Di sisi lain, jika suatu perintah tidak ditemukan, itu hanya status gema kesalahan standar.
sumber