複数 - プログラムがBashスクリプトから存在するかどうかをチェックする方法?
シェルスクリプト if (20)
回答
POSIX互換:
command -v <the_command>
bash
固有の環境の場合:
hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords
説明
which
避けるwhich
。 ( hash
、 type
、 command
ような組み込み関数は安価です)、外部コマンドの影響を受けることができますが、組み込み関数を使用して実際に必要な処理を行うこともできます。システムによって容易に異なる。
なぜ気に?
- 多くのオペレーティングシステムには、終了ステータスを設定していないものもあります 。つまり、
if which foo
がそこで動作しない場合でも、foo
が存在していなくても常にそのfoo
が存在すると報告します(POSIXシェルによってはこれもhash
用です)。 - 多くのオペレーティングシステムは、出力を変更したり、パッケージマネージャーに接続したりといったカスタムや悪いことをします。
だから、どちらを使用しないでください。 代わりに、次のいずれかを使用します。
$ 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; }
(マイナーサイドノート: 2>&-
は同じ2>/dev/null
だが短く - これは真実ではない 2>&-
stderrへの書き込みを試みるときにプログラムにエラーを引き起こすFD 2を閉じるこれは、正常に書き込んだり出力を破棄したり(危険です)とはまったく異なります)
あなたのハッシュ・バングが/bin/sh
ならば、POSIXの言うことを気にする必要があります。 type
とhash
の終了コードはPOSIXによってひどくよく定義されておらず、コマンドが存在しないときにhash
が正常に終了することがわかります(まだtype
は見られません)。 command
の終了ステータスはPOSIXによって明確に定義されているため、おそらく最も安全なものになります。
あなたのスクリプトがbash
使っていても、POSIXのルールはそれほど重要ではなく、 type
とhash
両方が完全に安全に使えるようになります。 type
はPATH
だけを検索する-P
があり、 hash
はコマンドの場所がハッシュされる副作用があります(次回の検索ではより速い参照)。それを実際に使用するために
簡単な例として、 gdate
が存在する場合はそれを実行する関数、それ以外の場合はdate
:
gnudate() {
if hash gdate 2>/dev/null; then
gdate "[email protected]"
else
date "[email protected]"
fi
}
エラーと終了を返したり、スクリプトを続行したりする方法で、プログラムが存在することをどのように確認するのですか?
それは簡単だと思われるが、それは私を悩ましている。
@ lhunathと@ GregVの答えを展開すると、 if
文の中にその小切手を簡単に入れたい人のためのコードです:
exists()
{
command -v "$1" >/dev/null 2>&1
}
それを使用する方法は次のとおりです。
if exists bash; then
echo 'Bash exists!'
else
echo 'Your system does not have Bash'
fi
Bashのtype -P cmd
を模倣するために、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; }
@lhunathが示唆するように、bashスクリプトでhash
を使用するには、 次のようにします。
hash foo &> /dev/null
if [ $? -eq 1 ]; then
echo >&2 "foo not found."
fi
このスクリプトはhash
を実行し、最新のコマンドの終了コード、 $?
1
等しい。 hash
がfoo
見つけられない場合、終了コードは1
ます。 foo
が存在する場合、終了コードは0
になり0
。
&> /dev/null
標準エラーと標準出力をhash
からリダイレクトし、画面に表示されず、 echo >&2
がメッセージを標準エラーに書き込みます。
alias
が壊れてしまい、ポータブルで100%信頼できる方法がないと言えます。 例えば:
alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/
もちろん最後のものだけが問題になります(リンゴにとっては犯罪ではありません!)しかし、それらはすべてcommand -v
の観点から有効なalias
です。
ringo
ようなringo
なものを拒否するには、シェル組み込みalias
コマンドの出力を解析して再帰させる必要があります( command -v
はalias
より優れていません)。それには移植可能なソリューションはなく、Bash固有の解決法はかなり面倒です。
このような解決策は、無条件にalias ls='ls -F'
拒否することに注意してください。
test() { command -v $1 | grep -qv alias }
which
コマンドが役に立つかもしれない。 男
実行ファイルが見つかった場合は0、見つからない場合は1、実行可能でない場合は1を返します。
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 exe-
cutable
2 if an invalid option is specified
それは実行可能ファイルが実行されている環境で利用可能かどうかを把握していることです。問題はいくつかあります...
-アダム
複数の依存関係をチェックし、エンドユーザーにステータスを通知する
for cmd in "latex" "pandoc"; do
printf "%-10s" "$cmd"
if hash "$cmd" 2>/dev/null; then printf "OK\n"; else printf "missing\n"; fi
done
サンプル出力:
latex OK
pandoc missing
10
を最大コマンド長に調整します。 私はそれを行うための非冗長なPOSIXの方法を見ていないので、自動ではありません: どのようにBashでスペース区切りのテーブルの列を揃えるには?
あなたがプログラムが存在し、実際にプログラムであるかどうかチェックしたい場合、bashビルトインコマンドではなく、 command
、 type
、 hash
はすべて組み込みコマンドの終了ステータスを0に戻すので、テストには適していません。
たとえば、 時間組み込みコマンドより多くの機能を提供する時間プログラムがあります。 プログラムが存在するかどうかを確認するには、次の例のようにwhich
を使用することをお勧めします。
# first check if the time program exists
timeProg=`which time`
if [ "$timeProg" = "" ]
then
echo "The time program does not exist on this system."
exit 1
fi
# 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
ここにはたくさんのオプションがありますが、私は素早くワンライナーに驚いています。これはスクリプトの冒頭で使用したものです。 [[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; } [[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }
[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; } [[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }
これは、ここで選択された回答と別の情報源(と私が少し遊んでいる)に基づいています。
これが他の人にとって便利になることを願っています。
これは非常に簡単なので、私はこれを使用します:
if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi
または
if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi
シェルの組み込み関数とプログラムのエコー状態をstdoutに使用し、コマンドが見つからない場合にはstderrには何も表示しません。ステータスはstderrにのみ反映されます。
コマンド-vは、テストする<command>
にPOSIX_BUILTINSオプションが設定されていても正しく動作しますが、そうでない場合は失敗する可能性があります。 (それは何年も私のために働いていましたが、最近はうまくいきませんでした)。
私は、次のことがもっと失敗に耐えられると感じています。
test -x $(which <command>)
パス、実行、パーミッションの3つをテストします。
スクリプト
#!/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 || return 1
hash $mycomm 2>/dev/null || \
printf "\xe2\x9c\x98 [ABRT]: $mycomm: command does not exist\n"; return 1;
}
readonly -f exists
exists notacmd
exists bash
hash
bash -c 'printf "Fin.\n"'
結果
✘ [ABRT]: notacmd: command does not exist
hits command
0 /usr/bin/bash
Fin.
プログラムの存在を確認すると、おそらく後で実行することになります。 なぜ最初にそれを実行しようとしない?
if foo --version >/dev/null 2>&1; then
echo Found
else
echo Not found
fi
PATHディレクトリとファイルのアクセス権を調べるだけでなく、プログラムが実行されるというより信頼できる検査です。
さらに、プログラムのバージョンなどの有用な結果を得ることができます。
もちろん、いくつかのプログラムは重くて起動することができ、いくつかのプログラムはすぐに(そして正常に)終了するための--version
オプションを持たないことがあります。
以下を使用してください。
test -x filename
または
[ -x filename ]
条件式の bashマンページから:
-x file True if file exists and is executable.
次は、コマンドが$PATH
存在し、実行可能であるかどうかをチェックする移植可能な方法です:
[ -x "$(command -v foo)" ]
例:
if ! [ -x "$(command -v git)" ]; then
echo 'Error: git is not installed.' >&2
exit 1
fi
$PATH
その名前の実行可能ファイルがない場合、bashは実行不可能なファイルを返すので、実行可能なチェックが必要です。
また、実行可能ファイルと同じ名前の実行可能でないファイルが$PATH
前に存在する場合、後者が実行されたとしても、ダッシュは前者を返します。 これはバグであり、POSIX標準に違反しています。 [ バグ報告 ] [ Standard ]
さらに、探しているコマンドがエイリアスとして定義されている場合、これは失敗します。
私のDebianサーバーのセットアップ。 複数のパッケージに同じ名前が含まれているときに問題が発生しました。 例えばapache2。 これは私の解決策でした。
function _apt_install() {
apt-get install -y $1 > /dev/null
}
function _apt_install_norecommends() {
apt-get install -y --no-install-recommends $1 > /dev/null
}
function _apt_available() {
if [ `apt-cache search $1 | grep -o "$1" | uniq | wc -l` = "1" ]; then
echo "Package is available : $1"
PACKAGE_INSTALL="1"
else
echo "Package $1 is NOT available for install"
echo "We can not continue without this package..."
echo "Exitting now.."
exit 0
fi
}
function _package_install {
_apt_available $1
if [ "${PACKAGE_INSTALL}" = "1" ]; then
if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
echo "package is already_installed: $1"
else
echo "installing package : $1, please wait.."
_apt_install $1
sleep 0.5
fi
fi
}
function _package_install_no_recommends {
_apt_available $1
if [ "${PACKAGE_INSTALL}" = "1" ]; then
if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
echo "package is already_installed: $1"
else
echo "installing package : $1, please wait.."
_apt_install_norecommends $1
sleep 0.5
fi
fi
}
私はlhunathの使用を阻止することに同意し、彼のソリューションはBASHユーザーにとって完全に有効です。 しかし、移植性を高めるために、 command -v
を代わりに使用します。
$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it's not installed. Aborting." >&2; exit 1; }
コマンドcommand
はPOSIXに準拠しています。仕様についてはこちらを参照してください: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html : http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html
注: type
はPOSIXに準拠していますが、 type -P
はそうではありません。
私はこれを簡単にする私の.bashrcで定義された関数を持っています。
command_exists () {
type "$1" &> /dev/null ;
}
これは、私の.bash_profile
からの使用例.bash_profile
。
if command_exists mvim ; then
export VISUAL="mvim --nofork"
fi
私は非常に便利で短いバージョンを使用しています:
dpkg -s curl 2>/dev/null >/dev/null || apt-get -y install curl
1つのプログラムだけをチェックしなければならない場合は簡単です。
興味のある人は、インストールされているライブラリを検出したい場合は、上記の方法論のどれも動作しません。 私はあなたが物理的にパス(ヘッダファイルなどのために)をチェックするか、このようなもの(Debianベースのディストリビューションの場合)を残していると思います:
dpkg --status libdb-dev | grep -q not-installed
if [ $? -eq 0 ]; then
apt-get install libdb-dev
fi
上記からわかるように、クエリからの "0"答えはパッケージがインストールされていないことを意味します。 これは "grep"の機能です - "0"は一致が見つかったことを意味し、 "1"は一致が見つからなかったことを意味します。