while - Vérifier si un programme existe à partir d'un script Bash




while bash (20)

Comment est-ce que je validerais qu'un programme existe, d'une manière qui retournera une erreur et quittera, ou continuera avec le manuscrit?

Il semble que ça devrait être facile, mais ça m'a bousculé.


Répondre

POSIX compatible:

command -v <the_command>

Pour les environnements spécifiques à bash :

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

Explication

Évitez which . Non seulement c'est un processus externe que vous lancez pour faire très peu (les builtins comme hash , type ou command sont beaucoup moins chers), vous pouvez aussi compter sur les builtins pour faire ce que vous voulez, alors que les effets des commandes externes peuvent facilement varier d'un système à l'autre.

Pourquoi s'en soucier?

  • Beaucoup de systèmes d'exploitation ont un which ne définit même pas un statut de sortie , ce if which foo signifie if which foo ne fonctionnera pas là et signalera toujours que foo existe, même si ce n'est pas le cas (notez que certains obus POSIX semblent faire ceci pour le hash aussi).
  • Beaucoup de systèmes d'exploitation font which fait des choses personnalisées et mauvaises comme changer la sortie ou même accrocher dans le gestionnaire de paquets.

Donc, n'utilisez pas which . Au lieu de cela, utilisez l'un d'entre eux:

$ 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; }

(Note secondaire: certains vont suggérer 2>&- est le même 2>/dev/null mais plus court - c'est faux 2>&- ferme FD 2 qui provoque une erreur dans le programme quand il essaye d'écrire sur stderr , ce qui est très différent de l'écriture avec succès et en rejetant la sortie (et dangereux!))

Si votre hash bang est /bin/sh alors vous devriez vous préoccuper de ce que dit POSIX. type codes de sortie de type et hash ne sont pas très bien définis par POSIX, et hash est considéré comme se terminant avec succès lorsque la commande n'existe pas (cela n'a pas encore été vu avec le type ). Le statut de sortie de la command est bien défini par POSIX, de sorte que l'un est probablement le plus sûr à utiliser.

Si votre script utilise bash , les règles POSIX ne comptent plus vraiment et le type et le hash deviennent parfaitement sécurisés. type maintenant un -P pour rechercher uniquement le PATH et hash a l'effet de bord que l'emplacement de la commande sera haché (pour une recherche plus rapide la prochaine fois que vous l'utiliserez), ce qui est généralement une bonne chose puisque vous vérifiez probablement son existence. afin de l'utiliser réellement.

Comme exemple simple, voici une fonction qui exécute gdate si elle existe, sinon date :

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

Cela dépend si vous voulez savoir s'il existe dans l'un des répertoires de la variable $PATH ou si vous connaissez l'emplacement absolu de celui-ci. Si vous voulez savoir si elle est dans la variable $PATH , utilisez

if which programname >/dev/null; then
    echo exists
else
    echo does not exist
fi

sinon utiliser

if [ -x /path/to/programname ]; then
    echo exists
else
    echo does not exist
fi

La redirection vers /dev/null/ dans le premier exemple supprime la sortie du programme.


Essayez d'utiliser:

test -x filename

ou

[ -x filename ]

À partir de la page de manuel bash sous Expressions conditionnelles :

 -x file
          True if file exists and is executable.

J'ai dû vérifier si git était installé dans le cadre du déploiement de notre serveur CI. Mon dernier script bash était le suivant (serveur Ubuntu):

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

J'espère que ça aide quelqu'un d'autre!


Je dirais qu'il n'y a pas de manière portable et 100% fiable en raison des alias suspens. Par exemple:

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

Bien sûr, seul le dernier est problématique (pas d'offense à Ringo!) Mais tous sont des alias valides du point de vue de la command -v .

Afin de rejeter ceux qui pendent comme ringo , nous devons analyser la sortie de la commande alias intégrée shell et la recurse en ( command -v n'est pas supérieure à alias ici.) Il n'y a pas de solution portable pour cela, et même un Bash solution spécifique est plutôt fastidieuse.

Notez qu'une telle solution rejettera inconditionnellement l' alias ls='ls -F'

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

Je l'utilise parce que c'est très facile:

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi

ou

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi

Il utilise le shell intégré et le statut d'écho du programme sur stdout et rien sur stderr d'une autre main si une commande n'est pas trouvée, elle ne renvoie l'état que vers stderr.


Je n'arrivais pas à obtenir une des solutions, mais après l'avoir un peu modifié, je suis arrivé avec ça. Ce qui fonctionne pour moi:

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

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

Je seconde l'utilisation de "command -v". Par exemple:

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

La commande which pourrait être utile. homme qui

Il renvoie 0 si l'exécutable est trouvé, 1 s'il est introuvable ou non exécutable:

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

Une bonne chose à propos de ce qui est de savoir si l'exécutable est disponible dans l'environnement qui est exécuté - sauve quelques problèmes ...

-Adam


Le hash-variant a un piège: Sur la ligne de commande, vous pouvez par exemple saisir

one_folder/process

avoir un processus exécuté. Pour cela, le dossier parent de one_folder doit être dans $ PATH . Mais quand vous essayez de hacher cette commande, elle réussira toujours:

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

Pour imiter le type -P cmd de Bash, nous pouvons utiliser 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; }

Pour utiliser le hash , comme le suggère @lhunath , dans un script bash:

hash foo &> /dev/null
if [ $? -eq 1 ]; then
    echo >&2 "foo not found."
fi

Ce script exécute hash puis vérifie si le code de sortie de la commande la plus récente, la valeur stockée dans $? , est égal à 1 . Si hash ne trouve pas foo , le code de sortie sera 1 . Si foo est présent, le code de sortie sera 0 .

&> /dev/null redirige l'erreur standard et la sortie standard du hash afin qu'il n'apparaisse pas à l'écran et echo >&2 écrit le message à l'erreur standard.


S'étendant sur les réponses de @ lhunath et de @ GregV, voici le code pour les personnes qui veulent mettre facilement cette vérification dans une instruction if :

exists()
{
  command -v "$1" >/dev/null 2>&1
}

Voici comment l'utiliser:

if exists bash; then
  echo 'Bash exists!'
else
  echo 'Your system does not have Bash'
fi

S'il n'y a pas de commande de type externe disponible (comme cela est tenu pour acquis share ), nous pouvons utiliser env -i sh -c 'type cmd 1>/dev/null 2>&1' conforme à 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

Au moins sur Mac OS X 10.6.8 en utilisant la commande Bash 4.2.24 (2) command -v ls ne correspond pas à un déplacement /bin/ls-temp .


Si vous ne pouvez pas faire fonctionner les choses au-dessus et au-dessous et tirer les cheveux de votre dos, essayez d'exécuter la même commande en utilisant bash -c . Il suffit de regarder ce délire somnambulique, c'est ce qui se passe vraiment quand vous exécutez $ (sous-commande):

Premier. Cela peut vous donner une sortie complètement différente.

$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls

Seconde. Il ne peut vous donner aucune sortie.

$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found

Si vous vérifiez l'existence du programme, vous allez probablement l'exécuter plus tard de toute façon. Pourquoi ne pas essayer de l'exécuter en premier lieu?

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

C'est une vérification plus fiable que le programme s'exécute que de simplement regarder les répertoires PATH et les autorisations de fichiers.

De plus, vous pouvez obtenir un résultat utile de votre programme, comme sa version.

Bien sûr, les inconvénients sont que certains programmes peuvent être lourds à démarrer et certains n'ont pas d'option --version à quitter immédiatement (et avec succès).


ma configuration pour un serveur Debian. J'ai eu un problème lorsque plusieurs paquets contient le même nom. par exemple apache2. donc c'était ma solution.

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
}

hash foo 2>/dev/null : fonctionne avec zsh, bash, dash et ash.

type -p foo : il semble fonctionner avec zsh, bash et ash (busybox), mais pas dash (il interprète -p comme un argument).

command -v foo : fonctionne avec zsh, bash, dash, mais pas ash (busybox) ( -ash: command: not found ).

Notez également que le builtin n'est pas disponible avec les ash et les 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