shell是什麼 - 從Bash腳本檢查程序是否存在




linux shell教學 (20)

回答

兼容POSIX:

command -v <the_command>

對於bash特定的環境:

hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords

說明

避免which 。 它不僅僅是一個外部過程,你只需要做很少的事情(意思是像hashtypecommand這樣的內建hash便宜),你也可以依靠內建函數實際做你想做的事,而外部命令的影響可以從系統到系統很容易變化。

為什麼在意?

  • 許多操作系統有一個甚至沒有設置退出狀態 ,這意味著if which foo甚至不會在那里工作,並且總是會報告foo存在,即使它不存在(注意一些POSIX shell似乎做了這也是hash )。
  • 許多操作系統會自定義和惡意的東西,比如改變輸出甚至掛鉤到包管理器。

所以,不要使用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時, ,這與成功寫入並丟棄輸出(和危險!)非常不同)

如果你的hash bang是/bin/sh那麼你應該關心POSIX的說法。 typehash的退出代碼不是由POSIX很好地定義的,並且在命令不存在的情況下hash可以成功退出(尚未見到這種type )。 command的退出狀態由POSIX很好地定義,所以一個可能是最安全的使用。

如果你的腳本使用bash ,POSIX規則就不再重要了,而且typehash變得非常安全。 type現在有一個-P來搜索PATHhash具有副作用,即命令的位置將被散列(以便下一次使用它時進行更快的查找),這通常是一件好事,因為您可能檢查其存在為了實際使用它。

作為一個簡單的例子,如果它存在,這裡有一個運行gdate的函數,否則為date

gnudate() {
    if hash gdate 2>/dev/null; then
        gdate "[email protected]"
    else
        date "[email protected]"
    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  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調整為最大命令長度。 不是自動的,因為我沒有看到一個非冗長的方式來做到這一點。


以下是檢查命令是否存在於$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 ]

另外,如果您正在查找的命令已被定義為別名,​​則這將失敗。


哈希變量有一個缺陷:在命令行中,例如可以輸入

one_folder/process

執行進程。 為此,one_folder的父文件夾必須位於$ PATH中 。 但是當你試圖散列這個命令時,它總是會成功的:

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

嘗試使用:

test -x filename

要么

[ -x filename ]

條件表達式下的bash manpage:

 -x file
          True if file exists and is executable.

如果你們不能將上面/下面的東西拿出來工作,把頭髮拉出來,嘗試使用bash -c運行相同的命令。 看看這個夢幻般的deli妄,當你運行$(子命令)時,這就是真正發生的事情:

第一。 它可以給你完全不同的輸出。

$ 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

如果你想檢查一個程序是否存在並且確實是一個程序,而不是bash內置命令 ,那麼commandtypehash不適合測試,因為它們都返回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

如果可以的話,為什麼不使用Bash buildins?

which programname

...

type -P programname

如果沒有可用的外部type命令( 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

至少在使用Bash 4.2.24(2)的Mac OS X 10.6.8上, command -v ls與移動的/bin/ls-temp不匹配。


對於那些有興趣的人來說,如果你想檢測已安裝的庫,上述方法都不起作用。 我想你不是在物理上檢查路徑(可能是用於頭文件等),或者是類似的東西(如果你在基於Debian的發行版中):

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

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

正如你從上面看到的,查詢中的“0”答案意味著包沒有安裝。 這是“grep”的功能 - “0”表示找到匹配,“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

另一方面,如果找不到命令,它會使用shell內建和程序回顯狀態來輸出標準輸出,而不會發生任何錯誤,它只會響應標準錯誤。


我從來沒有得到上述解決方案在我有權訪問的框中工作。 首先,類型已經安裝(做更多的事情)。 所以內建指令是必需的。 這個命令適用於我:

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

我必須檢查是否將git安裝為部署CI服務器的一部分。 我最後的bash腳本如下(Ubuntu服務器):

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

希望這可以幫助別人!


我會說由於懸掛alias沒有便攜和100%可靠的方法。 例如:

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

當然,只有最後一個是有問題的(Ringo沒有冒犯!)但是從command -v的角度來看,它們都是有效的alias

為了拒絕像ringo這樣的懸掛類,我們必須解析shell內建的alias命令的輸出並遞歸到它們中( command -v在這裡不會優於alias )。它沒有可移植的解決方案,甚至是Bash具體的解決方案相當繁瑣。

請注意,像這樣的解決方案將無條件地拒絕alias ls='ls -F'

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

我有一個在我的.bashrc中定義的函數,這使得這更容易。

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

這是一個如何使用它的例子(來自我的.bash_profile 。)

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

我第二次使用“command -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

腳本

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

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