複数 - プログラムが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 。 ( hashtypecommandような組み込み関数は安価です)、外部コマンドの影響を受けることができますが、組み込み関数を使用して実際に必要な処理を行うこともできます。システムによって容易に異なる。

なぜ気に?

  • 多くのオペレーティングシステムには、終了ステータスを設定しいないものもあります 。つまり、 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の言うことを気にする必要があります。 typehashの終了コードはPOSIXによってひどくよく定義されておらず、コマンドが存在しないときにhashが正常に終了することがわかります(まだtypeは見られません)。 commandの終了ステータスはPOSIXによって明確に定義されているため、おそらく最も安全なものになります。

あなたのスクリプトがbash使っていても、POSIXのルールはそれほど重要ではなく、 typehash両方が完全に安全に使えるようになります。 typePATHだけを検索する-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等しい。 hashfoo見つけられない場合、終了コードは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 -valiasより優れていません)。それには移植可能なソリューションはなく、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ビルトインコマンドではなく、 commandtypehashはすべて組み込みコマンドの終了ステータスを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"は一致が見つからなかったことを意味します。





bash