bash जांचें कि कोई प्रोग्राम बैश स्क्रिप्ट से मौजूद है या नहीं




shell (24)

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 न दिखाई दे echo >&2 मानक त्रुटि को संदेश लिखता है।

मैं कैसे सत्यापित करूं कि कोई प्रोग्राम मौजूद है, इस तरह से कोई त्रुटि या बाहर निकल जाएगी, या स्क्रिप्ट के साथ जारी रहेगी?

ऐसा लगता है कि यह आसान होना चाहिए, लेकिन यह मुझे रोक रहा है।


हैश-वेरिएंट में एक गड़बड़ी है: कमांड लाइन पर आप उदाहरण के लिए टाइप कर सकते हैं

one_folder/process

प्रक्रिया निष्पादित करने के लिए। इसके लिए one_folder का मूल फ़ोल्डर $ PATH में होना चाहिए। लेकिन जब आप इस आदेश को हैश करने का प्रयास करते हैं, तो यह हमेशा सफल रहेगा:

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

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  exe-
          cutable

       2      if an invalid option is specified

इसके बारे में अच्छी बात यह है कि यह पता चलता है कि निष्पादन योग्य वातावरण में उपलब्ध है जो कि चल रहा है - कुछ समस्याएं बचाता है ...

-Adam



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

उत्तर

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 भी)।
  • कई ऑपरेटिंग सिस्टम कस्टम और बुरी चीजें करते हैं जो उत्पादन को बदलते हैं या पैकेज मैनेजर में भी हुक करते हैं।

तो, इसका उपयोग न करें। इसके बजाय इनमें से एक का उपयोग करें:

$ 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>&- एफडी 2 बंद करता है जो प्रोग्राम में त्रुटि का कारण बनता है जब यह stderr को लिखने का प्रयास करता है , जो सफलतापूर्वक इसे लिखने और आउटपुट को छोड़ने से बहुत अलग है (और खतरनाक!))

यदि आपके हैश बैंग /bin/sh तो आपको पोसिक्स के बारे में क्या ख्याल रखना चाहिए। type और hash के एक्जिट कोड को पॉज़िक्स द्वारा बहुत अच्छी तरह से परिभाषित नहीं किया गया है, और hash को सफलतापूर्वक बाहर निकलने के लिए देखा जाता है जब कमांड मौजूद नहीं होता है (इसे अभी तक type नहीं किया गया है)। command की निकास स्थिति अच्छी तरह से POSIX द्वारा परिभाषित की गई है, ताकि एक संभवतः उपयोग करने के लिए सबसे सुरक्षित हो।

यदि आपकी स्क्रिप्ट bash का उपयोग करती है, तो POSIX नियम वास्तव में अब कोई फर्क नहीं पड़ता है और दोनों type और hash उपयोग करने के लिए पूरी तरह सुरक्षित हैं। type अब केवल PATH खोजने के लिए PATH और hash का दुष्प्रभाव है कि कमांड का स्थान धोया जाएगा (अगली बार जब आप इसे इस्तेमाल करते हैं तो तेज़ लुकअप के लिए), जो आम तौर पर एक अच्छी बात है क्योंकि आप शायद इसके अस्तित्व की जांच कर सकते हैं वास्तव में इसका उपयोग करने के लिए आदेश।

एक साधारण उदाहरण के रूप में, यहां एक ऐसा फ़ंक्शन है जो gdate चलाता है यदि यह मौजूद है, अन्यथा date :

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

मुझे उस बॉक्स पर काम करने के लिए उपर्युक्त समाधान कभी नहीं मिले हैं जिनके पास मुझे एक्सेस है। एक के लिए, प्रकार स्थापित किया गया है (और क्या करता है)। तो अंतर्निहित निर्देश की आवश्यकता है। यह आदेश मेरे लिए काम करता है:

if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi

मैं इसका उपयोग करता हूं क्योंकि यह बहुत आसान है:

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

यह स्टॉलआउट के लिए शैल बिल्टिन और प्रोग्राम इको स्टेटस का उपयोग करता है और यदि कोई कमांड नहीं मिलता है, तो दूसरी तरफ stderr करने के लिए कुछ नहीं, यह केवल stderr स्थिति echos।


यदि कोई बाह्य type कमांड उपलब्ध नहीं है (जैसा कि share दिया गया 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

बैश 4.2.24 (2) command -v ls का उपयोग कर कम से कम मैक ओएस एक्स 10.6.8 पर एक स्थानांतरित /bin/ls-temp मेल नहीं खाता है।


मेरे पास मेरे .bashrc में परिभाषित एक फ़ंक्शन है जो इसे आसान बनाता है।

command_exists () {
    type "$1" &> /dev/null ;
}

यहां इसका एक उदाहरण दिया गया है कि इसका उपयोग कैसे किया जाता है (मेरे .bash_profile ।)

if command_exists mvim ; then
    export VISUAL="mvim --nofork"
fi

मैं लुनथ से सहमत हूं कि इसका उपयोग हतोत्साहित हो, और उसका समाधान बाश उपयोगकर्ताओं के लिए बिल्कुल मान्य है। हालांकि, अधिक पोर्टेबल होने के लिए, 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 नहीं है।


मैं कहूंगा कि लापरवाही alias es के कारण कोई पोर्टेबल और 100% विश्वसनीय तरीका नहीं है। उदाहरण के लिए:

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

निस्संदेह केवल अंतिम समस्याग्रस्त है (रिंगो के लिए कोई अपराध नहीं है!) लेकिन वे सभी command -v के दृष्टिकोण से वैध alias हैं।

ringo जैसे ringo लोगों को अस्वीकार करने के लिए, हमें शैल के अंतर्निर्मित alias के आउटपुट को पार्स करना होगा और इन्हें दोबारा शुरू करना होगा ( command -v यहां alias बेहतर नहीं है।) इसके लिए कोई पोर्टेबल समाधान नहीं है, और यहां तक ​​कि एक बैश भी है - विशिष्ट समाधान बल्कि थकाऊ है।

ध्यान दें कि इस तरह का समाधान बिना शर्त alias ls='ls -F' को अस्वीकार कर देगा

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

यह जांचने के लिए एक पोर्टेबल तरीका है कि $PATH में कोई आदेश मौजूद है या निष्पादन योग्य है:

[ -x "$(command -v foo)" ]

उदाहरण:

if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  exit 1
fi

निष्पादन योग्य चेक की आवश्यकता होती है क्योंकि बैश एक निष्पादन योग्य फ़ाइल देता है यदि उस नाम के साथ कोई निष्पादन योग्य फ़ाइल $PATH में नहीं मिलती है।

यह भी ध्यान रखें कि यदि निष्पादन योग्य के समान नाम वाला एक गैर-निष्पादन योग्य फ़ाइल पहले $PATH में मौजूद है, तो डैश पूर्व को वापस कर देता है, भले ही बाद वाले को निष्पादित किया जाएगा। यह एक बग है और POSIX मानक का उल्लंघन कर रहा है। [ बग रिपोर्ट ] [ Standard ]

इसके अतिरिक्त, यह विफल हो जाएगा यदि आप जिस कमांड को ढूंढ रहे हैं उसे उपनाम के रूप में परिभाषित किया गया है।


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

@ लुनथ और @ ग्रेगवी के उत्तरों पर विस्तार, यहां उन लोगों के लिए कोड है जो आसानी से उस चेक को एक कथन के अंदर रखना चाहते हैं:

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 , type और hash परीक्षण के लिए उपयुक्त नहीं हैं क्योंकि वे अंतर्निहित कमांड के लिए 0 बाहर निकलने की स्थिति लौटते हैं।

उदाहरण के लिए, समय प्रोग्राम है जो अंतर्निहित कमांड की तुलना में अधिक सुविधाएं प्रदान करता है। यह जांचने के लिए कि प्रोग्राम मौजूद है या नहीं, मैं सुझाव दूंगा कि निम्न उदाहरण के रूप में:

# 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

यदि आप प्रोग्राम अस्तित्व की जांच करते हैं, तो आप शायद इसे बाद में चलाने जा रहे हैं। इसे पहले स्थान पर चलाने की कोशिश क्यों न करें?

if foo --version >/dev/null 2>&1; then
    echo Found
else
    echo Not found
fi

यह एक और भरोसेमंद जांच है कि कार्यक्रम केवल पैथ निर्देशिकाओं और फ़ाइल अनुमतियों को देखने से चलता है।

इसके अलावा आप अपने प्रोग्राम से कुछ उपयोगी परिणाम प्राप्त कर सकते हैं, जैसे इसका संस्करण।

बेशक दोष यह है कि कुछ कार्यक्रम शुरू करने के लिए भारी हो सकते हैं और कुछ के पास तुरंत (और सफलतापूर्वक) बाहर निकलने के लिए एक --version विकल्प नहीं है।


एक डेबियन सर्वर के लिए मेरा सेटअप। मुझे एक समस्या थी जब एकाधिक पैकेजों में एक ही नाम होता है। उदाहरण के लिए 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
}

मैं "कमांड-वी" का उपयोग दूसरा। इस तरह:

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

मुझे काम करने के समाधानों में से कोई एक नहीं मिला, लेकिन इसे संपादित करने के बाद मैं थोड़ा सा आया। मेरे लिए कौन सा काम करता है:

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

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

रुचि रखने वालों के लिए, यदि आप एक स्थापित लाइब्रेरी का पता लगाना चाहते हैं तो उपर्युक्त पद्धतियों में से कोई भी काम नहीं करता है। मुझे कल्पना है कि आप या तो शारीरिक रूप से पथ (संभावित रूप से हेडर फाइलों और ऐसे) के लिए जांच कर रहे हैं, या ऐसा कुछ (यदि आप डेबियन-आधारित डिस्ट्रो पर हैं):

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

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

जैसा कि आप उपरोक्त से देख सकते हैं, क्वेरी से "0" उत्तर का अर्थ है कि पैकेज स्थापित नहीं है। यह "grep" का एक कार्य है - एक "0" का अर्थ है कि एक मैच मिला था, "1" का मतलब है कि कोई मिलान नहीं मिला था।


कई निर्भरताओं की जांच करें और अंतिम उपयोगकर्ताओं को स्थिति सूचित करें

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 को समायोजित करें। स्वचालित नहीं है क्योंकि मुझे ऐसा करने के लिए एक गैर verbose रास्ता नहीं दिख रहा है।


यदि आप काम करने के लिए ऊपर / नीचे चीजें नहीं प्राप्त कर सकते हैं और बालों को बाहर खींच रहे हैं, तो bash -c का उपयोग करके उसी कमांड को चलाने का प्रयास करें। बस इस somnambular delirium को देखो, यह वास्तव में तब होता है जब आप $ (उप-आदेश) चलाते हैं:

प्रथम। यह आपको पूरी तरह से अलग आउटपुट दे सकता है।

$ 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

बैश के 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; }




shell