Comment vérifier si un programme existe depuis un script Bash?
Comment puis-je valider qu'un programme existe, d'une manière qui retournera une erreur et sortira, ou qui continuera avec le script?
on dirait que ça devrait être facile, mais ça me dérange.
30 réponses
réponse
compatible POSIX:
command -v <the_command>
pour bash
environnements spécifiques:
hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords
explication
éviter which
. Non seulement c'est un processus externe que vous lancez pour faire très peu (ce qui signifie que les builtins comme hash
, type
ou command
sont beaucoup moins cher), vous pouvez également compter sur les builtins pour faire réellement ce que vous voulez, tandis que le 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
que ne fixe même pas un statut de sortie , ce qui signifie que leif which foo
ne fonctionnera même pas là et toujours signaler quefoo
existe, même si elle n'existe pas (notez que certains shell POSIX semblent le faire pourhash
aussi). - de nombreux systèmes d'exploitation font
which
faire des trucs personnalisés et diaboliques comme changer la sortie ou même s'accrocher dans le gestionnaire de paquets.
donc, n'utilisez pas which
. Utilisez plutôt l'une d'entre elles:
$ 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; }
(petit côté-note: certains vont suggérer que 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 essaie d'écrire à stderr, ce qui est très différent de réussir à lui écrire et de rejeter la sortie (et dangereux!))
si votre hash bang est /bin/sh
alors vous devez vous soucier de ce que POSIX dit. Les codes de sortie de type
et hash
ne sont pas très bien définis par POSIX, et hash
est vu pour sortir avec succès quand la commande n'existe pas (n'ont pas vu cela avec type
encore). Le statut de sortie de command
est bien défini par POSIX, de sorte que L'on est probablement le plus sûr à utiliser.
si votre script utilise bash
cependant, les règles POSIX n'ont plus vraiment d'importance et les deux type
et hash
deviennent parfaitement sûrs à utiliser. type
a maintenant un -P
pour rechercher juste le PATH
et hash
a l'effet secondaire que l'emplacement de la commande sera hachée (pour une recherche plus rapide La prochaine fois que vous l'utilisez), ce qui est généralement une bonne chose car vous avez probablement vérifiez 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 "$@"
else
date "$@"
fi
}
ce qui suit est un moyen portable de vérifier si une commande existe dans $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 de l'exécutable est nécessaire car bash renvoie un fichier non exécutable si aucun fichier exécutable portant ce nom n'est trouvé dans $PATH
.
aussi noter que si un fichier non exécutable avec le même nom que l'exécutable existe plus tôt dans $PATH
, dash renvoie le premier, même si le second serait exécuté. Ceci est un bug et est 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.
je suis d'accord avec lhunath pour décourager l'utilisation de which
, et sa solution est parfaitement valide pour les utilisateurs de BASH . Toutefois, pour être plus portable, command -v
doit être utilisé à la place:
$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it's not installed. Aborting." >&2; exit 1; }
Commande command
est conforme à POSIX, voir ici pour son cahier des charges: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html
Note: type
est conforme à POSIX, mais type -P
ne l'est pas.
j'ai une fonction définie dans mon .bashrc qui rend cela plus facile.
command_exists () {
type "" &> /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
Cela dépend si vous voulez savoir si il existe dans l'un des répertoires dans le $PATH
variable ou si vous connaissez l'emplacement absolu. 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
autre utilisation
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 which
.
en développant les réponses de @lhunath et @GregV, voici le code pour les gens qui veulent mettre facilement ce chèque à l'intérieur d'une déclaration if
:
exists()
{
command -v "" >/dev/null 2>&1
}
Voici comment l'utiliser:
if exists bash; then
echo 'Bash exists!'
else
echo 'Your system does not have Bash'
fi
Essayez d'utiliser:
test -x filename
ou
[ -x filename ]
de la page de manuel de bash sous Expressions conditionnelles :
-x file True if file exists and is executable.
pour utiliser hash
, comme @lhunath suggère , dans un script bash:
hash foo &> /dev/null
if [ $? -eq 1 ]; then
echo >&2 "foo not found."
fi
ce script exécute hash
et vérifie ensuite 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 de hash
pour qu'elle n'apparaisse pas à l'écran et echo >&2
écrit le message à l'erreur standard.
je n'ai jamais eu les solutions ci-dessus pour travailler sur la boîte, j'ai accès. Pour l'un, le type a été installé (faire ce que plus fait). La directive builtin est donc nécessaire. Cette commande fonctionne pour moi:
if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
Si vous vérifiez l'existence du programme, vous allez probablement pour 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
il s'agit d'une vérification plus fiable que le programme exécute simplement en regardant les répertoires de chemin et les permissions de fichier.
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 l'option --version
pour sortir immédiatement (et avec succès).
pourquoi ne pas utiliser Bash builtins si vous le pouvez?
which programname
...
type -P programname
vérifiez les dépendances multiples et informez les utilisateurs finaux du statut
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
sortie de L'échantillon:
latex OK
pandoc missing
ajuster la 10
à la longueur de commande maximale. Pas automatique parce que je ne vois pas une façon non verbeuse POSIX de le faire:
comment aligner les colonnes D'une table séparée par des espaces dans Bash?
pour les personnes intéressées, aucune des méthodologies ci-dessus ne fonctionne si vous souhaitez détecter une bibliothèque installée. J'imagine qu'il vous reste à vérifier physiquement le chemin (potentiellement pour les fichiers d'en-tête et autres), ou quelque chose comme ceci (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, un "0" réponse de la requête signifie que le paquet n'est pas installé. C'est une fonction de "grep" - un "0" signifie qu'une correspondance a été trouvée, un "1" signifie non la correspondance a été trouvée.
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 avec dash (il interprète -p
comme argument).
command -v foo
: fonctionne avec zsh, bash, dash, mais pas ash (busybox) ( -ash: command: not found
).
notez Également que builtin
n'est pas disponible avec ash
et dash
.
la commande which
pourrait être utile. l'homme qui
renvoie 0 si l'exécutable est trouvé, 1 s'il n'est pas trouvé ou pas 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
bonne chose à propos de ce qui est qu'il détermine si l'exécutable est disponible dans l'environnement ce qui est exécuté dans - permet d'économiser quelques problèmes...
- Adam
je dirais qu'il n'y a pas de moyen portable et 100% fiable à cause de la pendaison alias
es. 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 (aucune offense à Ringo!) Mais Tous sont valides alias
es du point de vue de command -v
.
pour rejeter ceux qui sont pendants comme ringo
, nous devons analyser la sortie du shell intégré alias
commande et y revenir ( command -v
n'est pas supérieur à alias
ici. Il n'y a pas de solution portable pour cela, et même une solution spécifique à Bash est assez fastidieuse.
notez que cette solution rejettera inconditionnellement alias ls='ls -F'
test() { command -v | grep -qv alias }
S'il n'y a pas de commande externe type
disponible (prise pour acquis ici ), nous pouvons utiliser POSIX conforme 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=""
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
' _ "" || 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 Bash 4.2.24(2) command -v ls
ne correspond pas à un /bin/ls-temp
déplacé .
pour imiter le type -P cmd
de Bash, nous pouvons utiliser le env -i type cmd 1>/dev/null 2>&1
conforme à POSIX .
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; }
la variante de hachage a un pitfall: sur la ligne de commande vous pouvez par exemple taper
one_folder/process
pour faire exécuter le processus. Pour cela, le dossier parent de one_folder doit être dans $PATH . Mais quand vous essayez de hachez cette commande, elle réussira toujours:
hash one_folder/process; echo $? # will always output '0'
je seconde l'utilisation de la commande "- 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
ma configuration pour un serveur debian. j'ai eu un problème lorsque plusieurs paquets contiennent le même nom. par exemple apache2. donc c'était ma solution.
function _apt_install() {
apt-get install -y > /dev/null
}
function _apt_install_norecommends() {
apt-get install -y --no-install-recommends > /dev/null
}
function _apt_available() {
if [ `apt-cache search | grep -o "" | uniq | wc -l` = "1" ]; then
echo "Package is available : "
PACKAGE_INSTALL="1"
else
echo "Package is NOT available for install"
echo "We can not continue without this package..."
echo "Exitting now.."
exit 0
fi
}
function _package_install {
_apt_available
if [ "${PACKAGE_INSTALL}" = "1" ]; then
if [ "$(dpkg-query -l | tail -n1 | cut -c1-2)" = "ii" ]; then
echo "package is already_installed: "
else
echo "installing package : , please wait.."
_apt_install
sleep 0.5
fi
fi
}
function _package_install_no_recommends {
_apt_available
if [ "${PACKAGE_INSTALL}" = "1" ]; then
if [ "$(dpkg-query -l | tail -n1 | cut -c1-2)" = "ii" ]; then
echo "package is already_installed: "
else
echo "installing package : , please wait.."
_apt_install_norecommends
sleep 0.5
fi
fi
}
si vous ne pouvez pas obtenir les choses ci-dessus/ci-dessous pour travailler et tirer les cheveux hors de votre dos, essayez d'exécuter la même commande en utilisant bash -c
. Il suffit de regarder ce délire somnambulaire, c'est ce qui se passe vraiment quand vous lancez $(sous-commande):
D'abord. Il peut vous donner la sortie complètement différente.
$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls
à la Seconde. Il peut vous donner aucune sortie du tout.
$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found
dans le cas où vous voulez vérifier si un programme existe et est vraiment un programme, pas un bash intégré à la commande , puis command
, type
et hash
ne sont pas appropriés pour tester car ils retournent tous l'état de sortie 0 pour les commandes intégrées.
par exemple, il y a le programme time qui offre plus de fonctionnalités que la commande time intégrée. Pour vérifier si le programme existe, je suggérez d'utiliser which
comme 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
il y a une tonne d'options ici mais je n'ai pas été surpris d'avoir des montages rapides, c'est 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 une autre source (et moi jouant un peu).
espérons que ce sera pratique pour les autres.
checkexists() {
while [ -n "" ]; do
[ -n "$(which "")" ] || echo "": command not found
shift
done
}
j'utilise ceci 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 shell builtin et programme echo status à stdout et rien à stderr par l'autre main si une commande n'est pas trouvée, il echos status seulement à stderr.
Script
#!/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=; 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.
- v fonctionne bien si L'option POSIX_BUILTINS est définie pour l'option <command>
à tester mais peut échouer si non. (il a travaillé pour moi pendant des années, mais récemment tombé sur un où il n'a pas fonctionné).
je trouve que ce qui suit est plus à l'épreuve des échecs:
test -x $(which <command>)
Puisqu'il teste 3 choses: le chemin, l'exécution et la permission.
j'utilise une version très pratique et courte:
dpkg -s curl 2>/dev/null >/dev/null || apt-get -y install curl
Si facile si un seul programme doit être coché.
Super réponse et explication de @lhunath. Sauvé ma journée. J'ai prolongé un peu. Ne pouvais pas me contrôler partage - en espérant qu'il pourrait être utile pour quelqu'un. Si quelqu'un a besoin de vérifier (un tableau de) plusieurs programmes, voici l'extrait rapide.
Que fait-il? (1) Lire le tableau des programmes. (2) afficher le message pour échec du programme. (3) invite l'utilisateur à continuer (forçant la boucle) O/N options pour la validation du reste des programmes.
#!/bin/bash
proginstalldir=/full/dir/path/of/installation
progsbindir=$proginstalldir/bin
echo -e "\nMy install directory - $proginstalldir"
echo -e "My binaries directory - $progsbindir"
VerifyInstall () {
clear
myprogs=( program1 program2 program3 program4 program5 programn );
echo -e "\nValidation of my programs started...."
for ((i=0; i<${#myprogs[@]}; i++)) ; do
command -v $progsbindir/${myprogs[i]} >/dev/null && echo -e "Validating....\t${myprogs[i]}\tSUCCESSFUL" || { echo -e "Validating.... \t${myprogs[i]}\tFAILED" >&2;
while true; do
printf "%s: " "ERROR.... Validation FAILED for ${myprogs[i]} !!!! Continue?"; read yn;
case $yn in [Yy] ) echo -e "Please wait..." ; break;;
[Nn]) echo -e "\n\n#################################\n## Validation Failed .. !! ##\n#################################\n\n" ; exit 1; break;;
*) echo -e "\nPlease answer y or n then press Enter\n"; esac; done; >&2; }; done
sleep 2
}
VerifyInstall