Comment puis-je vérifier si un programme existe à partir d'un script Bash?

2212

Comment pourrais-je valider qu'un programme existe, d'une manière qui retournera une erreur et quittera, ou continuera avec le script?

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

gregh
la source
Qu'est-ce qu'un "programme"? Comprend-il des fonctions et des alias? whichrenvoie vrai pour ceux-ci. typesans arguments renverra en outre true pour les mots réservés et les commandes internes du shell. Si "programme" signifie "échangeable en $PATH", alors voyez cette réponse .
Tom Hale

Réponses:

3056

Réponse

Compatible POSIX:

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

A éviter which. Non seulement est-ce un processus externe que vous lancez pour faire très peu (ce qui signifie des builds comme hash, typeou commandsont beaucoup moins chers), vous pouvez également compter sur les builtins pour faire réellement ce que vous voulez, tandis que les effets des commandes externes peuvent facilement varier de système à système.

Pourquoi s'en soucier?

  • De nombreux systèmes d'exploitation ont un whichqui ne définit même pas de statut de sortie , ce qui signifie if which fooqu'il ne fonctionnera même pas là-bas et signalera toujours l'foo existence, même si ce n'est pas le cas (notez que certains shells POSIX semblent le faire hashaussi).
  • De nombreux systèmes d'exploitation font des whichchoses personnalisées et malfaisantes comme changer la sortie ou même se connecter au gestionnaire de paquets.

Alors, n'utilisez pas which. Utilisez plutôt l'un d'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 mineure: certains suggéreront 2>&-la même chose 2>/dev/nullmais plus court - ce n'est pas vrai . 2>&-Ferme FD 2 qui provoque une erreur dans le programme lorsqu'il essaie d'écrire sur stderr, ce qui est très différent de réussir à y écrire et à ignorer la sortie (et dangereux!))

Si votre hash bang est, /bin/shvous devriez vous soucier de ce que dit POSIX. typeet hashles codes de sortie de ne sont pas terriblement bien définis par POSIX, et hashsont vus se terminer avec succès lorsque la commande n'existe pas (je ne l'ai pas encore vu avec type). commandLe statut de sortie de est bien défini par POSIX, de sorte que l'un est probablement le plus sûr à utiliser.

Si votre script utilise bashcependant, les règles POSIX n'ont plus vraiment d'importance et les deux typeet hashdeviennent parfaitement sûres à utiliser. typea maintenant -Ppour rechercher juste le PATHet hasha pour effet secondaire 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 car vous vérifiez probablement son existence afin de l'utiliser réellement .

À titre d'exemple simple, voici une fonction qui s'exécute gdatesi elle existe, sinon date:

gnudate() {
    if hash gdate 2>/dev/null; then
        gdate "$@"
    else
        date "$@"
    fi
}
lhunath
la source
35
@Geert: La partie &> / dev / null masque le message 'type' émis lorsque 'foo' n'existe pas. Le> & 2 sur l'écho s'assure d'envoyer le message d'erreur à l'erreur standard au lieu de la sortie standard; parce que c'est la convention. Ils apparaissent tous les deux sur votre terminal, mais l'erreur standard est certainement la sortie préférée pour les messages d'erreur et les avertissements inattendus.
lhunath
5
l'indicateur -P ne fonctionne pas dans 'sh', par exemple stackoverflow.com/questions/2608688/…
momeara
130
Pour ceux qui ne connaissent pas la redirection d'E / S "avancée" dans bash: 1) 2>&- ("close output file file descriptor 2", qui est stderr) a le même résultat que 2> /dev/null; 2) >&2est un raccourci pour 1>&2, que vous pouvez reconnaître comme "rediriger stdout vers stderr". Consultez la page de redirection d'E / S du Guide de script avancé Bash pour plus d'informations.
mikewaters
9
@mikewaters L'ABS semble assez avancé et décrit un large éventail de fonctionnalités CLI bash et non bash, mais il est très négligent à bien des égards et ne suit pas les bonnes pratiques. Je n'ai pas assez d'espace dans ce commentaire pour rédiger un article; mais je peux coller quelques exemples pris au hasard de code BAD: while read element ; do .. done <<< $(echo ${ArrayVar[*]}), for word in $(fgrep -l $ORIGINAL *.txt), ls -l "$directory" | sed 1d , {{pour en seq $BEGIN $END}}, ... Beaucoup ont essayé de contacter les auteurs et proposer des améliorations , mais il est pas wiki et les demandes ont atterri dans l' oreille d'un sourd.
lhunath
56
@mikewaters 2>&-n'est pas le même que 2>/dev/null. Le premier ferme le descripteur de fichier, tandis que le second le redirige simplement vers /dev/null. Vous ne pouvez pas voir une erreur car le programme essaie de vous informer sur stderr que stderr est fermé.
nyuszika7h
577

Ce qui suit est un moyen portable de vérifier si une commande existe $PATH et est exécutable:

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

Exemple:

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

La vérification des exécutables est nécessaire car bash renvoie un fichier non exécutable si aucun fichier exécutable portant ce nom ne se trouve dans $PATH.

Notez également que si un fichier non exécutable portant le même nom que l'exécutable existe plus tôt $PATH, dash renvoie le premier, même si le dernier est exécuté. Il s'agit d'un bogue et en violation du standard POSIX. [ Rapport de bogue ] [ Standard ]

De plus, cela échouera si la commande que vous recherchez a été définie comme un alias.

nyuszika7h
la source
4
Produira command -vun chemin d'accès même pour un fichier non exécutable? Autrement dit, le -x vraiment nécessaire?
einpoklum
5
@einpoklum -xteste que le fichier est exécutable, ce qui était la question.
Ken Sharp
3
@KenSharp: Mais cela semble être redondant, car commandtestera lui-même son exécutabilité - n'est-ce pas?
einpoklum
13
@einpoklum Oui, c'est nécessaire. En fait, même cette solution peut se casser dans un cas de bord. Merci d'avoir porté cela à mon attention. dash, bash et zsh ignorent tous les fichiers non exécutables $PATHlors de l'exécution d'une commande. Cependant, le comportement de command -vest très incohérent. Dans le tiret, il renvoie le premier fichier correspondant dans $PATH, qu'il soit exécutable ou non. En bash, il renvoie la première correspondance exécutable dans $PATH, mais s'il n'y en a pas, il peut renvoyer un fichier non exécutable. Et dans zsh, il ne renverra jamais un fichier non exécutable.
nyuszika7h
5
Pour autant que je sache, dashest le seul parmi ces trois à ne pas être conforme à POSIX; [ -x "$(command -v COMMANDNAME)"]fonctionnera dans les deux autres. Il semble que ce bogue ait déjà été signalé mais n'a pas encore reçu de réponse: bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264
nyuszika7h
210

Je suis d'accord avec lhunath pour décourager l'utilisation de which, et sa solution est parfaitement valable pour les utilisateurs de Bash . Cependant, pour être plus portable, command -vdoit être utilisé à la place:

$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it's not installed.  Aborting." >&2; exit 1; }

La commande commandest compatible POSIX. Voir ici pour sa spécification: commande - exécuter une commande simple

Remarque: typeest compatible POSIX, mais type -Pne l'est pas.

GregV
la source
3
Comme ci-dessus - exit 1;tue un xterm, s'il est invoqué à partir de là.
utilisateur inconnu
1
Cela ne fonctionnerait pas sur un sh standard: vous &> n'est pas une instruction de redirection valide.
jyavenard
7
@jyavenard: La question est taguée bash , d'où la notation de redirection spécifique à bash plus concise &>/dev/null. Cependant, je suis d'accord avec vous, ce qui compte vraiment, c'est la portabilité, j'ai modifié ma réponse en conséquence, maintenant en utilisant la redirection sh standard >/dev/null 2>&1.
GregV
pour améliorer encore plus cette réponse, je ferais deux choses: 1: utiliser "&>" pour la simplifier, comme la réponse de Josh. 2: divisez le {} en une ligne supplémentaire, en mettant un onglet avant l'écho, pour plus de lisibilité
knocte
Je mets juste ce revêtement dans une fonction bash si quelqu'un veut ... github.com/equant/my_bash_tools/blob/master/tarp.bash
equant
94

J'ai une fonction définie dans mon .bashrc qui facilite cela.

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

Voici un exemple de la façon dont il est utilisé (de mon .bash_profile.)

if command_exists mvim ; then
    export VISUAL="mvim --nofork"
fi
Josh Strater
la source
Que fait le &>?
Saad Malik
7
Le &> redirige à la fois stdout et stderr ensemble.
Josh Strater
&>peut ne pas être disponible dans votre version de Bash. Le code de Marcello devrait bien fonctionner; ça fait la même chose.
Josh Strater
3
Échoue sur les mots intégrés et réservés: essayez ceci avec le mot thenpar exemple. Consultez cette réponse si vous souhaitez que l'exécutable existe dans $PATH.
Tom Hale
84

Cela dépend si vous voulez savoir si elle existe dans l'un des répertoires de la $PATHvariable ou si vous en connaissez l'emplacement absolu. Si vous voulez savoir si elle se trouve dans la $PATHvariable, 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 whichprogramme.

dreamlax
la source
22
Vous ne devriez vraiment pas utiliser "qui" pour les raisons décrites dans mon commentaire.
lhunath
39

En développant les réponses de @ lhunath et @ GregV, voici le code pour les personnes qui veulent facilement mettre cette vérification dans une ifdéclaration:

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
Romário
la source
13
La volonté d'apprendre et de s'améliorer doit être récompensée. +1 C'est propre et simple. La seule chose que je peux ajouter, c'est que cela commandréussit même pour les alias, ce qui pourrait être quelque peu contre-intuitif. Vérifier l'existence dans un shell interactif donnera des résultats différents lorsque vous le déplacerez dans un script.
Palec
1
Je viens de tester et d'utiliser shopt -u expand_aliasesignore / masque les alias (comme ceux alias ls='ls -F'mentionnés dans une autre réponse) et les shopt -s expand_aliasesrésout via command -v. Ainsi, il devrait peut-être être défini avant la vérification et unset après, bien qu'il puisse affecter la valeur de retour de la fonction si vous ne capturez pas et ne renvoyez pas explicitement la sortie de l'appel de commande.
dragon788
24

Essayez d'utiliser:

test -x filename

ou

[ -x filename ]

Depuis la page de manuel Bash sous Expressions conditionnelles :

 -x file
          True if file exists and is executable.
dmckee --- chaton ex-modérateur
la source
26
Cela signifie que vous devez déjà connaître le chemin d'accès complet à l'application.
lhunath
12
L'OP n'a pas précisé s'il voulait vérifier une instance spécifique ou une instance exécutable ... Je lui ai répondu de la façon dont je l'ai lu.
dmckee --- chaton ex-modérateur
16

Pour utiliser 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 s'exécute hashpuis vérifie si le code de sortie de la commande la plus récente, la valeur stockée dans $?, est égal à 1. Si hashne le trouve pas foo, le code de sortie sera 1. Si fooest présent, le code de sortie sera 0.

&> /dev/nullredirige l'erreur standard et la sortie standard de hashsorte qu'elle n'apparaisse pas à l'écran et echo >&2écrit le message en erreur standard.

dcharles
la source
8
Pourquoi pas juste if hash foo &> /dev/null; then ...?
Beni Cherniavsky-Paskin
9

Je n'ai jamais réussi à faire fonctionner les réponses précédentes sur la boîte à laquelle j'ai accès. D'une part, typea été installé (faire ce qui morefait). La directive intégrée est donc nécessaire. Cette commande fonctionne pour moi:

if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
Magnus
la source
3
Les crochets ne font pas partie de la ifsyntaxe, utilisez simplement if builtin type -p vim; then .... Et les backticks sont une syntaxe vraiment ancienne et obsolète, $()prise en charge même par shsur tous les systèmes modernes.
nyuszika7h
9

Vérifiez les dépendances multiples et informez les utilisateurs finaux de leur statut

for cmd in latex pandoc; do
  printf '%-10s' "$cmd"
  if hash "$cmd" 2>/dev/null; then
    echo OK
  else
    echo missing
  fi
done

Exemple de sortie:

latex     OK
pandoc    missing

Ajustez la 10à la longueur de commande maximale. Ce n'est pas automatique, car je ne vois pas de méthode POSIX non verbeuse pour le faire: comment puis-je aligner les colonnes d'une table séparée par des espaces dans Bash?

Vérifiez si certains aptpackages sont installés avec dpkg -set installez-les autrement .

Voir: Vérifier si un package apt-get est installé puis l'installer s'il n'est pas sous Linux

Il a été mentionné précédemment à: Comment puis-je vérifier si un programme existe à partir d'un script Bash?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
1
Façon non verbeuse de le faire: 1) se débarrasser du spécificateur de largeur; 2) ajoutez un espace après printf de votre nom de commande; 3) Dirigez votre boucle for column -t(partie d'util-linux).
Patrice Levesque
8

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 des résultats utiles de votre programme, tels que sa version.

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

0xF
la source
6

hash foo 2>/dev/null: fonctionne avec Z shell (Zsh), Bash, Dash et ash .

type -p foo: il semble fonctionner avec Z shell, Bash et ash ( BusyBox ), mais pas Dash (il interprète -pcomme un argument).

command -v foo: fonctionne avec Z shell, Bash, Dash, mais pas ash (BusyBox) ( -ash: command: not found).

Notez également que builtinn'est pas disponible avec ash et Dash.

bleuté
la source
4

Utilisez les fonctions intégrées Bash si vous pouvez:

which programname

...

type -P programname
Peter Mortensen
la source
15
Hein? whichn'est pas un Bash intégré.
tripleee
le type de programme -P doit être préféré, voir la réponse acceptée
RobertG
@RobertG Tout ce que je vois, c'est que ce -Pn'est pas POSIX. Pourquoi est type -Ppréféré?
mikemaccana
J'aurais dû formuler cela "à préférer dans les environnements bash" - comme j'avais l'intention de répondre au commentaire précédent spécifique à bash. Quoi qu'il en soit, c'était il y a des années - je suppose que je devrais juste, encore une fois, vous indiquer la réponse marquée comme "acceptée"
RobertG
4

La commande -vfonctionne correctement si l'option POSIX_BUILTINS est définie pour le <command>test, mais elle peut échouer sinon. (Cela a fonctionné pour moi pendant des années, mais j'en ai récemment rencontré un où cela n'a pas fonctionné.)

Je trouve que les éléments suivants sont plus résistants aux pannes:

test -x $(which <command>)

Puisqu'il teste trois choses: chemin d'accès, existence et permission d'exécution.

AnthonyC
la source
Ça ne marche pas. test -x $(which ls)renvoie 0, comme le fait test -x $(which sudo), même s'il lsest installé et exécutable et sudon'est même pas installé dans le conteneur docker dans lequel je cours.
algal
@algal Vous devez utiliser des guillemets, je pense, alorstest -x "$(which <command>)"
JoniVR
@algal Peut ls- être a un alias? Je ne pense pas que cela fonctionnerait si la commande a un paramètre.
AnthonyC
3

Pour les personnes intéressées, aucune des méthodologies des réponses précédentes ne fonctionne si vous souhaitez détecter une bibliothèque installée. J'imagine qu'il vous reste soit à vérifier physiquement le chemin (potentiellement pour les fichiers d'en-tête et autres), ou quelque chose comme ça (si vous êtes sur une distribution basée sur Debian):

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

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

Comme vous pouvez le voir ci-dessus, une réponse "0" à la requête signifie que le package n'est pas installé. Ceci est une fonction de "grep" - un "0" signifie qu'une correspondance a été trouvée, un "1" signifie qu'aucune correspondance n'a été trouvée.

Nathan Crause
la source
10
Cependant, l'anti-pattern cmd; if [ $? -eq 0 ]; thendevrait être refactorisé enif cmd; then
tripleee
Cela ne fonctionne que pour les bibliothèques installées via dpkgouapt
Weijun Zhou
3

Il y a une tonne d'options ici, mais je n'ai été surpris par aucune ligne rapide. Voici ce que j'ai utilisé au début de mes scripts:

[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; }
[[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }

Ceci est basé sur la réponse sélectionnée ici et sur une autre source.

keisar
la source
2

Je dirais qu'il n'y a aucun moyen portable et fiable à 100% en raison de la suspension alias. 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 (n'offense pas Ringo!). Mais tous sont valables aliasdu point de vue command -v.

Afin de rejeter ceux qui pendent comme ringo, nous devons analyser la sortie de la aliascommande intégrée du shell et y revenir (ce command -vn'est pas un supérieur à aliasici.) Il n'y a pas de solution portable pour cela, et même un Bash- une solution spécifique est plutôt fastidieuse.

Notez qu'une solution comme celle-ci rejettera sans condition alias ls='ls -F':

test() { command -v $1 | grep -qv alias }
nodakai
la source
Bon point. Cependant, lorsqu'ils sont exécutés à l'intérieur d'un script bash, les alias ne sont pas visibles.
Basil Musa
1
Il y a aussi un problème, il retournera false lorsque la commande 'alias' est vérifiée. Quand cela devrait être vrai. Exemple: test "alias"
Basil Musa
2
Je viens de tester et d'utiliser shopt -u expand_aliasesignore / masque ces alias et les shopt -s expand_aliasesmontre via command -v.
dragon788
2

Cela indiquera en fonction de l'emplacement si le programme existe ou non:

    if [ -x /usr/bin/yum ]; then
        echo "This is Centos"
    fi
Klevin Kona
la source
Oui, j'ai ajouté cette commande si vous devez installer un paquet dans le serveur, Open suse, centos, Debian
Klevin Kona
La coloration syntaxique est désactivée dans la ligne "écho". Quelle est la solution? Cela suggère-t-il que le script Bash devrait être différent?
Peter Mortensen
@PeterMortensen La coloration syntaxique est désactivée car elle ne reconnaît pas qu'il s'agit d'une chaîne.
Adrien
1

La whichcommande peut être utile. homme qui

Il renvoie 0 si l'exécutable est trouvé et renvoie 1 s'il n'est pas trouvé 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 executable

       2      if an invalid option is specified

Ce whichqui est bien, c'est qu'il détermine si l'exécutable est disponible dans l'environnement qui whichest exécuté - cela évite quelques problèmes ...

Adam Davis
la source
Utilisez-le si vous recherchez un exécutable nommé foo, mais voyez ma réponse si vous voulez vérifier un fichier / chemin d'accès / vers / a / named / foo. Notez également ce qui peut ne pas être disponible sur certains systèmes minimaux, bien qu'il devrait être présent sur toute installation à part entière ...
dmckee --- chaton ex-modérateur
9
Ne vous fiez pas au statut de sortie dont. De nombreux systèmes d'exploitation ont un qui ne définit même pas un état de sortie autre que 0.
lhunath
1

Ma configuration pour un serveur Debian :

J'ai eu le problème lorsque plusieurs packages contenaient le même nom.

Par exemple apache2. C'était donc 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
}
ThCTLo
la source
1

Si vous les gars / filles ne pouvez pas faire fonctionner les choses dans les réponses ici et que vous vous arrachez les cheveux, essayez d'exécuter la même commande en utilisant bash -c . Regardez ce délire somnambulaire. C'est ce qui se passe vraiment lorsque vous exécutez $ (sous-commande):

Première. Il 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
user619271
la source
Les différences sont causées par la différence entre le mode interactif et non interactif du shell. Votre ~ / .bashrc est en lecture seule lorsque le shell n'est pas connecté et interactif. Le second semble étrange, car cela doit être dû à une différence dans la variable d'environnement PATH, mais les sous-coquilles héritent de l'environnement.
Palec
Dans mon cas, .bashrcj'ai un [ -z "$PS1" ] && returnpréfixé par # If not running interactively, don't do anythingdonc je suppose que c'est une raison pour laquelle même l'approvisionnement explicite de bashrc en mode non interactif n'aide pas. Le problème peut être contourné en appelant un script avec un opérateur point ss64.com/bash/source.html , . ./script.shmais ce n'est pas une chose que vous souhaitez vous rappeler de taper à chaque fois.
user619271
1
L'approvisionnement de scripts qui ne sont pas censés provenir est une mauvaise idée. Tout ce que j'essayais de dire, c'est que votre réponse n'a pas grand-chose à voir avec la question posée et beaucoup à voir avec Bash et son mode (non) interactif.
Palec
S'il expliquait ce qui se passait dans ces cas, ce serait un additif utile à une réponse.
Palec
0

La variante de hachage a un écueil: sur la ligne de commande, vous pouvez par exemple saisir

one_folder/process

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

hash one_folder/process; echo $? # will always output '0'
anycast.cw
la source
4
"Pour cela, le dossier parent de one_folder doit être dans $PATH" —Ceci est complètement inexact. Essayez-le. Pour que cela fonctionne, one_folder doit se trouver dans le répertoire courant .
Wildcard
0

J'appuie l'utilisation de "command -v". Par exemple, comme ceci:

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
user2961933
la source
0

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

if ! builtin type -p git &>/dev/null; then
  sudo apt-get -y install git-core
fi
Greg K
la source
3
Le conditionnel est plutôt inutile, modulo le temps de démarrage pour exécuter apt-get, car apt-get sera satisfait et quittera si git-core est déjà installé.
tripleee
3
Son temps de démarrage n'est pas négligeable, mais la motivation la plus importante est sudo: sans le conditionnel, il s'arrêterait toujours et demanderait un mot de passe (sauf si vous avez fait un sudo récemment). BTW, il peut être utile de le faire sudo -p "Type your password to install missing git-core: "pour que l'invite ne sorte pas du bleu.
Beni Cherniavsky-Paskin
0

Pour imiter Bash's type -P cmd, nous pouvons utiliser la compatibilité 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; }
Tim
la source
7
Pourquoi cela est-il voté? Sur quels systèmes cela fonctionne-t-il réellement pour vous? typesemble être un builtindans la plupart des shells donc cela ne peut pas fonctionner car les envutilisations execvppour s'exécuter ne peuvent commanddonc commandpas être un builtin(et le builtinsera toujours exécuté dans le même environnement). Cela échoue pour moi bash, ksh93, zsh, busybox [a]shet dashtous qui fournissent typeune commande du shell.
Adrian Frühwirth
0

S'il n'y a pas de typecommande externe disponible (comme pris pour acquis ici ), nous pouvons utiliser la conformité 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 v10.6.8 (Snow Leopard), l'utilisation de Bash 4.2.24 (2) command -v lsne correspond pas à un déplacement /bin/ls-temp.

freno
la source
0

Dans le cas où vous souhaitez vérifier si un programme existe et est vraiment un programme, pas une commande intégrée Bash , alors command, typeethash ne sont pas appropriés pour les tests car ils renvoient tous 0 l'état de sortie pour les commandes intégrées.

Par exemple, il y a le programme horaire qui offre plus de fonctionnalités que la commande intégrée de l' heure . Pour vérifier si le programme existe, je suggère d'utiliser whichcomme dans l'exemple suivant:

# 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
rpr
la source
0

Je voulais une réponse à la même question, mais pour exécuter dans un Makefile.

install:
    @if [[ ! -x "$(shell command -v ghead)" ]]; then \
        echo 'ghead does not exist. Please install it.'; \
        exit -1; \
    fi
Richard A Quadling
la source
-1

Scénario

#!/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"'

Résultat

 [ABRT]: notacmd: command does not exist
hits    command
   0    /usr/bin/bash
Fin.
ecwpz91
la source
-1

J'utilise cela, car c'est très simple:

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 les commandes internes du shell et l'état d'écho des programmes sur la sortie standard et rien sur l'erreur standard. En revanche, si une commande n'est pas trouvée, elle renvoie l'état uniquement à l'erreur standard.

UNE
la source