エラーを返して終了するか、スクリプトを続行する方法で、プログラムが存在することをどのように検証しますか?
簡単なようですが、困っています。
エラーを返して終了するか、スクリプトを続行する方法で、プログラムが存在することをどのように検証しますか?
簡単なようですが、困っています。
回答:
POSIX互換:
command -v <the_command>
Bash固有の環境の場合:
hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords
避けてくださいwhich
。外部プロセスの効果は簡単に変化しますがhash
、type
それcommand
はあなたがほとんど何もしないために起動している外部プロセスであるだけでなく(のような組み込みコマンドを意味するか、またははるかに安価です)、組み込みコマンドに依存して実際に必要なことを実行することもできますシステム間。
なぜ気にするの?
which
があります。つまり、if which foo
はそこでも機能せず、存在しない場合でも常にfoo
存在することを報告します(一部のPOSIXシェルもこれを行うように見えることに注意してくださいhash
)。which
は、出力を変更したり、パッケージマネージャーにフックしたりするなど、カスタムおよび悪質なことを行います。したがって、は使用しないでくださいwhich
。代わりに次のいずれかを使用してください。
$ 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
が短いと示唆します– これは正しくありません。stderrに書き込もうとするとプログラムでエラー2>&-
が発生するFD 2を閉じます。 これは、正常に書き込んで出力を破棄することとは大きく異なります。 (そして危険!))
あなたのハッシュバンがそうであるならば、あなたは/bin/sh
POSIXが言うことを気にするべきです。type
とhash
の終了コードはPOSIXであまり明確に定義されてhash
おらず、コマンドが存在しない場合(これをtype
まだ確認していない場合)は正常に終了するように見えます。 command
の終了ステータスはPOSIXによって明確に定義されているため、おそらく最も安全に使用できます。
スクリプトが使用する場合はbash
しかし、POSIX規則はもう本当に問題ではないとの両方type
とhash
使用に完全に安全になります。type
今持っている-P
だけで検索するようにPATH
してhash
、コマンドの場所は通常、あなたは、おそらく実際にそれを使用するために、その存在を確認するため、良いことである、(より速くあなたがそれを使用する次の時間をルックアップするために)ハッシュ化されることが副作用を持っています。
簡単な例として、gdate
存在する場合に実行される関数を次に示しますdate
。
gnudate() {
if hash gdate 2>/dev/null; then
gdate "$@"
else
date "$@"
fi
}
2>&-
(「出力ファイル記述子2を閉じる」。これはstderrです)の結果はと同じ2> /dev/null
です。2)>&2
はのショートカットで1>&2
、「stdoutをstderrにリダイレクトする」と認識される場合があります。詳細については、「高度なBashスクリプトガイド」の「i / oリダイレクト」ページを参照してください。
while read element ; do .. done <<< $(echo ${ArrayVar[*]})
、for word in $(fgrep -l $ORIGINAL *.txt)
、ls -l "$directory" | sed 1d
、{{中のためseq $BEGIN $END
}}、...多くは、作者に連絡し、改善を提案しようとしたが、それはまったくのwikiや要望聴覚障害者の耳に着弾したません。
2>&-
はありません2>/dev/null
。前者はファイル記述子を閉じますが、後者は単にそれをにリダイレクトし/dev/null
ます。プログラムがstderrでstderrが閉じていることを通知しようとするため、エラーが表示されない場合があります。
以下は、コマンドが存在し$PATH
、実行可能かどうかを確認するための移植可能な方法です。
[ -x "$(command -v foo)" ]
例:
if ! [ -x "$(command -v git)" ]; then
echo 'Error: git is not installed.' >&2
exit 1
fi
でその名前の実行可能ファイルが見つからない場合、bashは非実行可能ファイルを返すため、実行可能チェックが必要です$PATH
。
また、実行可能ファイルと同じ名前の非実行可能ファイルが以前に存在する場合$PATH
、後者は実行されますが、ダッシュは前者を返します。これはバグであり、POSIX標準に違反しています。[ バグレポート ] [標準 ]
さらに、探しているコマンドがエイリアスとして定義されている場合、これは失敗します。
command -v
も、非実行ファイルのパスを作成?つまり、-xは本当に必要なのでしょうか。
-x
は、ファイルが実行可能かどうかをテストします。これが問題でした。
command
それ自体が実行可能かどうかをテストするので、それは冗長であるように見えますか?
$PATH
、コマンドの実行時に非実行可能ファイルをスキップします。ただし、の動作command -v
は非常に一貫していません。ダッシュでは、$PATH
実行可能ファイルかどうかに関係なく、最初に一致したファイルをで返します。bashでは、で実行可能ファイルの最初の一致を返しますが、一致し$PATH
ない場合は、非実行可能ファイルを返すことができます。また、zshでは、実行可能でないファイルを返すことはありません。
dash
、これら3つのうちPOSIXに準拠していないのは1つだけです。[ -x "$(command -v COMMANDNAME)"]
他の2つで動作します。このバグはすでに報告されているようですが、まだ応答がありません:bugs.debian.org/cgi-bin/bugreport.cgi
lhunathの使用を阻止することに同意します。which
彼のソリューションは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に準拠しています。仕様についてはこちらをご覧ください:コマンド-簡単なコマンドを実行します
注:type
はPOSIXに準拠していますが、準拠していtype -P
ません。
exit 1;
そこから呼び出された場合、xtermを強制終了します。
&>/dev/null
です。しかし、私はあなたに同意します。本当に重要なことは移植性です。私はそれに応じて私の回答を編集し、標準のshリダイレクトを使用し>/dev/null 2>&1
ます。
これを簡単にする.bashrcに関数を定義しています。
command_exists () {
type "$1" &> /dev/null ;
}
これがどのように使用されるかの例を示します(myから.bash_profile
)。
if command_exists mvim ; then
export VISUAL="mvim --nofork"
fi
&>
ますか?
&>
stdoutとstderrの両方をリダイレクト一緒に。
&>
ご使用のバージョンのBashでは使用できない場合があります。マルチェロのコードは問題なく動作するはずです。同じことをします。
それが$PATH
変数のディレクトリの1つに存在するかどうか、またはその絶対的な場所を知っているかどうかによって異なります。$PATH
変数に含まれているかどうかを知りたい場合は、
if which programname >/dev/null; then
echo exists
else
echo does not exist
fi
そうでなければ使用する
if [ -x /path/to/programname ]; then
echo exists
else
echo does not exist
fi
/dev/null/
最初の例のへのリダイレクトは、which
プログラムの出力を抑制します。
@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
command
エイリアスに対しても成功することです。これは多少直観に反するかもしれません。対話型シェルでの存在を確認すると、スクリプトに移動したときとは異なる結果が得られます。
shopt -u expand_aliases
エイリアスを無視/非表示にし(alias ls='ls -F'
別の回答で述べたように)、shopt -s expand_aliases
を介してそれらを解決しますcommand -v
。したがって、コマンドコールの出力を明示的にキャプチャして返さない場合、関数の戻り値に影響する可能性がありますが、チェックの前に設定し、後で設定解除する必要があります。
使ってみてください:
test -x filename
または
[ -x filename ]
条件式の Bashマンページから:
-x file True if file exists and is executable.
@lhunathが示唆するように、Bashスクリプトでを使用するにhash
は:
hash foo &> /dev/null
if [ $? -eq 1 ]; then
echo >&2 "foo not found."
fi
このスクリプトが実行hash
され$?
、最新のコマンドの終了コードであるに保存されている値がに等しいかどうかが確認され1
ます。hash
が見つからない場合foo
、終了コードはになります1
。場合はfoo
存在している、終了コードは次のようになります0
。
&> /dev/null
リダイレクト標準エラーと標準出力からhash
、それが画面上に表示されないようにし、echo >&2
標準エラーにメッセージを書き込みます。
if hash foo &> /dev/null; then ...
ですか?
私がアクセスできるボックスで作業するために以前の答えを得ることはありませんでした。1つは、type
インストールされています(何をするかmore
)。したがって、組み込みディレクティブが必要です。このコマンドは私にとってはうまくいきます:
if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
if
構文の一部ではありませんif builtin type -p vim; then ...
。単にを使用してください。また、バックティックは非常に古く、非推奨の構文で$()
ありsh
、すべての最新システムでもサポートされています。
複数の依存関係を確認し、ステータスをエンドユーザーに通知する
for cmd in latex pandoc; do
printf '%-10s' "$cmd"
if hash "$cmd" 2>/dev/null; then
echo OK
else
echo missing
fi
done
出力例:
latex OK
pandoc missing
を10
コマンドの最大長に調整します。冗長ではないPOSIXの方法が見当たらないため、自動ではありません。
スペースで区切られたテーブルの列をBashで整列するにはどうすればよいですか?
いくつかのapt
パッケージが一緒dpkg -s
にインストールされているかどうかを確認し、そうでない場合はインストールします。
参照:apt-getパッケージがインストールされているかどうかを確認し、Linuxにインストールされていない場合はインストールしてください
それは以前に言及されました:プログラムがBashスクリプトから存在するかどうかを確認するにはどうすればよいですか?
column -t
(util-linuxの一部)にパイプします。
プログラムの存在を確認すると、おそらく後で実行することになります。そもそも実行してみませんか?
if foo --version >/dev/null 2>&1; then
echo Found
else
echo Not found
fi
これは、単にPATHディレクトリとファイルのアクセス許可を確認するよりも、プログラムが実行されていることの信頼できるチェックです。
さらに、バージョンなど、プログラムから有用な結果を得ることができます。
もちろん、欠点は、一部のプログラムは起動が重く、一部のプログラムは--version
すぐに(そして正常に)終了するオプションがないことです。
次のことができる場合は、Bashビルトインを使用します。
which programname
...
type -P programname
which
Bashビルトインではありません。
-P
がPOSIX ではないということです。なぜtype -P
好まれますか?
-v
POSIX_BUILTINSオプションが設定されている場合、コマンドは正常に動作します<command>
テスト、そうでない場合は失敗する可能性があります。(それは私のために何年も働いていました、しかし私は最近それがうまくいかないものに出会いました。)
次の方がフェイルプルーフであることがわかります。
test -x $(which <command>)
パス、存在、実行許可の3つをテストするため。
test -x $(which ls)
0を返し、などしtest -x $(which sudo)
ていても、ls
インストールされ、実行可能とされてsudo
も、私が実行しているドッキングウィンドウコンテナ内に設置されていません。
test -x "$(which <command>)"
ls
エイリアスされていますか?コマンドにパラメータがある場合は機能しないと思います。
関心のある人にとって、インストール済みのライブラリを検出したい場合、以前の回答の方法はどれも機能しません。パス(ヘッダーファイルなどの可能性がある)を物理的にチェックするか、次のようなもの(Debianベースのディストリビューションを使用している場合)が残っていると思います。
dpkg --status libdb-dev | grep -q not-installed
if [ $? -eq 0 ]; then
apt-get install libdb-dev
fi
上記からわかるように、クエリからの「0」の回答は、パッケージがインストールされていないことを意味します。これは「grep」の関数です。「0」は一致が見つかったことを意味し、「1」は一致が見つからなかったことを意味します。
cmd; if [ $? -eq 0 ]; then
にリファクタリングする必要がありますif cmd; then
dpkg
またはを介してインストールされたライブラリに対してのみ機能しますapt
ぶら下がっているalias
esのため、ポータブルで100%信頼できる方法はないと思います。例えば:
alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/
もちろん、最後の1つだけが問題です(Ringoに害はありません!)。しかし、それらはすべてalias
、の観点からは有効なes ですcommand -v
。
のようなぶら下がっているものを拒否するにringo
は、シェルの組み込みalias
コマンドの出力を解析し、それらに再帰する必要command -v
があります(は、alias
ここいるわけではありません)。そのためのポータブルソリューションはなく、Bash-特定のソリューションはかなり退屈です。
このようなソリューションは無条件に拒否されalias ls='ls -F'
ます:
test() { command -v $1 | grep -qv alias }
shopt -u expand_aliases
してこれらのエイリアスを無視/非shopt -s expand_aliases
表示にし、を介して表示していcommand -v
ます。
これにより、プログラムが存在するかどうかが場所に応じて通知されます。
if [ -x /usr/bin/yum ]; then
echo "This is Centos"
fi
which
コマンドは便利かもしれません。男は
実行可能ファイルが見つかった場合は0を返し、見つからないか実行可能でない場合は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 executable
2 if an invalid option is specified
良い点which
は、実行可能ファイルが実行されている環境で使用可能かどうかを判断することwhich
です。これにより、いくつかの問題が解消されます...
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
}
もしあなたがここで答えのものがうまく機能せず、背中から髪を引っ張っているなら、を使って同じコマンドを実行してみてくださいbash -c
。この陰鬱なせん妄を見てください。これは、$(サブコマンド)を実行したときに実際に起こっていることです。
最初。それはあなたに完全に異なる出力を与えることができます。
$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls
第二。それはあなたに全く出力を与えることができません。
$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found
.bashrc
、[ -z "$PS1" ] && return
前にが付いている# If not running interactively, don't do anything
ので、それが、非インタラクティブモードでのbashrcの明示的なソーシングでも役に立たない理由だと思います。この問題は、ss64.com / bash / source.htmlドット演算子を使用してスクリプトを呼び出すことで回避できますが、毎回入力. ./script.sh
することを覚えておく必要はありません。
ハッシュバリアントには1つの落とし穴があります。たとえば、コマンドラインで次のように入力できます。
one_folder/process
プロセスを実行します。このため、one_folderの親フォルダは$ PATHにある必要があります。ただし、このコマンドをハッシュしようとすると、常に成功します。
hash one_folder/process; echo $? # will always output '0'
$PATH
」-これは完全に不正確です。それを試してみてください。これが機能するには、one_folderが現在のディレクトリにある必要があります。
「コマンド-v」の2番目の使用法。例えばこのように:
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
CIサーバーのデプロイの一部としてGitがインストールされているかどうかを確認する必要がありました。私の最終的なBashスクリプトは次のとおりです(Ubuntuサーバー)。
if ! builtin type -p git &>/dev/null; then
sudo apt-get -y install git-core
fi
sudo
次のとおりです。条件なしでは、常に停止してパスワードを要求します(最近sudoを実行した場合を除く)。ところで、sudo -p "Type your password to install missing git-core: "
プロンプトが突然出ないようにすると便利かもしれません。
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; }
type
ではaのようですが、builtin
をenv
使用execvp
して実行command
するcommand
ことはできないので、これは機能しませんbuiltin
(そして、builtin
常に同じ環境内で実行されます)。これは私のために失敗したbash
、ksh93
、zsh
、busybox [a]sh
およびdash
それらの全ては提供type
シェル組み込みとして。
type
利用可能な外部コマンドがない場合(ここで当然と見なされているように)、POSIX準拠を使用できます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
少なくともMac OS X v10.6.8(Snow Leopard)では、Bash 4.2.24(2)を使用するcommand -v ls
と、移動され/bin/ls-temp
たと一致しません。
ケースでは、プログラムが存在するかどうかを確認したいと本当にプログラムではなく、組み込みコマンドバッシュで、その後、command
、type
およびhash
彼らとしてビルトインコマンドのすべての戻り0終了ステータスをテストするには適していません。
たとえば、time組み込みコマンドよりも多くの機能を提供するtimeプログラムがあります。プログラムが存在するかどうかを確認するには、次の例のように使用することをお勧めします。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
同じ質問に答えたいが、Makefile内で実行したい。
install:
@if [[ ! -x "$(shell command -v ghead)" ]]; then \
echo 'ghead does not exist. Please install it.'; \
exit -1; \
fi
脚本
#!/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 [ `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
シェルのビルトインとプログラムのエコーステータスを標準出力に使用し、標準エラーには何も使用しません。一方、コマンドが見つからない場合は、ステータスを標準エラーにのみエコーします。
which
これらに対してtrueを返します。type
引数なしの場合、予約語とシェルのビルトインに対してさらにtrueが返されます。「プログラム」が「で実行可能」を意味する場合$PATH
は、この回答を参照してください。