معنى - تحقق من وجود برنامج من برنامج Bash




git bash (20)

إجابة

متوافق مع 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 لكن أقصر - هذا غير صحيح . 2>&- يغلق FD 2 الذي يسبب خطأ في البرنامج عندما يحاول الكتابة إلى stderr وهو يختلف كثيرًا عن الكتابة الناجحة إليه ونبذ المخرجات (وخطير!))

إذا كان لديك فرقعة تجزئة /bin/sh ثم يجب عليك أن تهتم ما يقول POSIX. لا يتم تحديد رموز إنهاء type hash بشكل جيد للغاية بواسطة POSIX ، وينظر إلى hash بنجاح عند عدم وجود الأمر (لم يشاهد هذا بعد type ). يتم تحديد حالة خروج الأمر بشكل جيد بواسطة POSIX ، لذلك ربما يكون هذا هو الأكثر أمانًا للاستخدام.

إذا كان النص البرمجي يستخدم bash رغم ذلك ، فإن قواعد POSIX لا تهم بعد الآن وكل من type hash يصبحان آمنين تمامًا للاستخدام. type الآن يحتوي على -P للبحث فقط في PATH و hash has side-effect that the command's's is is has hased (for search upup next time you use it)، which is a good thing you you not check for its existence there's there's a good thing؟ أجل استخدامها في الواقع.

كمثال بسيط ، هنا وظيفة تقوم بتشغيل gdate إذا كانت موجودة ، وإلا date :

gnudate() {
    if hash gdate 2>/dev/null; then
        gdate "[email protected]"
    else
        date "[email protected]"
    fi
}

كيف يمكنني التحقق من وجود برنامج ما ، بطريقة إما إرجاع خطأ والخروج ، أو متابعة البرنامج النصي؟

يبدو أنه يجب أن يكون سهلاً ، لكنه كان يعنيني.


أتفق مع 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 ، انظر هنا لمواصفاته: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html

ملاحظة: type متوافق مع POSIX ، ولكن type -P ليس كذلك.


أنا ثاني استخدام "الأمر -v". مثال مثل هذا:

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

أود أن أقول أنه لا يوجد طريقة محمولة وموثوق بها بنسبة 100٪ بسبب تداخل الأسماء alias . فمثلا:

alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/

وبالطبع فقط المشكلة الأخيرة هي إشكالية (لا إهانة لرينجو!) ولكن كل هذه الأسماء هي أسماء alias صحيحة من وجهة نظر command -v .

لكي نرفض الأطراف المتدلية مثل ringo ، يجب علينا تحليل ناتج الأمر alias المسمى بـ alias وتكرارها ( command -v لا يتفوق على alias هنا.) لا يوجد حل محمول له ، وحتى Bash الحل المحدد ممل.

لاحظ أن مثل هذا الحل سيرفض دون قيد أو شرط alias ls='ls -F'

test() { command -v $1 | grep -qv alias }

إذا لم تتمكن من الحصول على الأشياء المذكورة أعلاه / أسفل للعمل وسحب الشعر من ظهرك ، فحاول تشغيل نفس الأمر باستخدام 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

إذا لم يكن هناك أمر خارجي متاح (كما هو مستخدم share ) ، فيمكننا استخدام 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 10.6.8 باستخدام الأمر Bash 4.2.24 (2) ، لا يتطابق command -v ls lvs مع نقل /bin/ls-temp .


الإعداد الخاص بي لخادم ديبيان. كان لدي مشكلة عندما تحتوي عدة حزم على نفس الاسم. على سبيل المثال 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
}

النصي

#!/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.

جرب استخدام:

test -x filename

أو

[ -x filename ]

من صفحة "bash manpage" تحت " التعبيرات الشرطية" :

 -x file
          True if file exists and is executable.

في حالة ما إذا كنت تريد التحقق من وجود برنامج وما إذا كان برنامجًا بالفعل ، أم لا أمر 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

كان علي أن git مما إذا كان قد تم تركيب git كجزء من نشر خادم CI الخاص بنا. كان برنامجي النصي النهائي bash كما يلي (خادم Ubuntu):

if ! builtin type -p git &>/dev/null; then
  sudo apt-get -y install git-core
fi

آمل أن يساعد هذا شخص آخر!


لاستخدام hash ، كما يقترح @ lhunath ، في البرنامج النصي باش:

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 يكتب الرسالة إلى الخطأ القياسي.


للمهتمين ، لا توجد أي من المنهجيات أعلاه تعمل إذا كنت ترغب في اكتشاف مكتبة مثبتة. أتخيل أنك تركت إما مع التحقق الفعلي من المسار (من المحتمل أن تكون ملفات رأسية أو هكذا) ، أو شيء من هذا القبيل (إذا كنت في توزيعة ديبيان):

dpkg --status libdb-dev | grep -q not-installed

if [ $? -eq 0 ]; then
    apt-get install libdb-dev
fi

كما ترى من أعلاه ، تعني الإجابة "0" من الاستعلام أن الحزمة غير مثبتة. هذه هي وظيفة "grep" - "0" تعني أنه تم العثور على تطابق ، "1" يعني أنه لم يتم العثور على أي تطابق.


لم أتمكن من الحصول على أحد الحلول للعمل ، ولكن بعد تحريره قليلاً ، توصلت إلى هذا. الذي يعمل بالنسبة لي:

dpkg --get-selections | grep -q linux-headers-$(uname -r)

if [ $? -eq 1 ]; then
        apt-get install linux-headers-$(uname -r)
fi

لماذا لا تستخدم باش مدمج إذا استطعت؟

which programname

...

type -P programname

لمحاكاة type -P cmd 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; }

يكون لعنصر التباين متغير واحد: في سطر الأوامر يمكنك على سبيل المثال كتابة

one_folder/process

لتنفيذ العملية. لهذا يجب أن يكون المجلد الأصل لـ one_folder في PATH $ . ولكن عند محاولة تجزئة هذا الأمر ، سينجح دائمًا:

hash one_folder/process; echo $? # will always output '0'

hash foo 2>/dev/null : يعمل مع zsh، bash، dash and ash.

type -p foo : يبدو أنه يعمل مع zsh ، bash و ash (busybox) ، ولكن ليس شرطة (يفسر -p كوسيطة).

command -v foo : يعمل مع zsh ، bash ، dash ، لكن ليس الرماد (busybox) ( -ash: command: not found ).

لاحظ أيضا أن builtin غير متوفر مع ash dash .


GIT=/usr/bin/git                     # STORE THE RELATIVE PATH
# GIT=$(which git)                   # USE THIS COMMAND TO SEARCH FOR THE RELATIVE PATH

if [[ ! -e $GIT ]]; then             # CHECK IF THE FILE EXISTS
    echo "PROGRAM DOES NOT EXIST."
    exit 1                           # EXIT THE PROGRAM IF IT DOES NOT
fi

# DO SOMETHING ...

exit 0                               # EXIT THE PROGRAM IF IT DOES

checkexists() {
    while [ -n "$1" ]; do
        [ -n "$(which "$1")" ] || echo "$1": command not found
        shift
    done
}






shell