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 .
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}
@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.
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 $PATHet est exécutable:
[-x "$(command -v foo)"]
Exemple:
if![-x "$(command -v git)"];then
echo 'Error: git is not installed.'>&2
exit 1fi
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.
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;}
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é
&>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.
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
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.
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.
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
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
fidone
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 Foundelse
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).
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.
Ç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):
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.
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':
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 ...
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.
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
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'
"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
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;}
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.
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 1fi# 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
#!/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 ||return1
hash $mycomm 2>/dev/null || \
printf "\xe2\x9c\x98 [ABRT]: $mycomm: command does not exist\n";return1;}
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.
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.
which
renvoie vrai pour ceux-ci.type
sans 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 .Réponses:
Réponse
Compatible POSIX:
Pour les environnements spécifiques à Bash:
Explication
A éviter
which
. Non seulement est-ce un processus externe que vous lancez pour faire très peu (ce qui signifie des builds commehash
,type
oucommand
sont 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?
which
qui ne définit même pas de statut de sortie , ce qui signifieif which foo
qu'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 fairehash
aussi).which
choses 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:(Note secondaire mineure: certains suggéreront
2>&-
la même chose2>/dev/null
mais 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/sh
vous devriez vous soucier de ce que dit POSIX.type
ethash
les codes de sortie de ne sont pas terriblement bien définis par POSIX, ethash
sont vus se terminer avec succès lorsque la commande n'existe pas (je ne l'ai pas encore vu avectype
).command
Le 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
bash
cependant, les règles POSIX n'ont plus vraiment d'importance et les deuxtype
ethash
deviennent parfaitement sûres à utiliser.type
a maintenant-P
pour rechercher juste lePATH
ethash
a 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
gdate
si elle existe, sinondate
:la source
2>&-
("close output file file descriptor 2", qui est stderr) a le même résultat que2> /dev/null
; 2)>&2
est un raccourci pour1>&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.while read element ; do .. done <<< $(echo ${ArrayVar[*]})
,for word in $(fgrep -l $ORIGINAL *.txt)
,ls -l "$directory" | sed 1d
, {{pour enseq $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.2>&-
n'est pas le même que2>/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é.Ce qui suit est un moyen portable de vérifier si une commande existe
$PATH
et est exécutable:Exemple:
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.
la source
command -v
un chemin d'accès même pour un fichier non exécutable? Autrement dit, le -x vraiment nécessaire?-x
teste que le fichier est exécutable, ce qui était la question.command
testera lui-même son exécutabilité - n'est-ce pas?$PATH
lors de l'exécution d'une commande. Cependant, le comportement decommand -v
est 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.dash
est 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=874264Je 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 -v
doit être utilisé à la place:La commande
command
est compatible POSIX. Voir ici pour sa spécification: commande - exécuter une commande simpleRemarque:
type
est compatible POSIX, maistype -P
ne l'est pas.la source
exit 1;
tue un xterm, s'il est invoqué à partir de là.&>/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
.J'ai une fonction définie dans mon .bashrc qui facilite cela.
Voici un exemple de la façon dont il est utilisé (de mon
.bash_profile
.)la source
&>
?&>
redirige à la fois stdout et stderr ensemble.&>
peut ne pas être disponible dans votre version de Bash. Le code de Marcello devrait bien fonctionner; ça fait la même chose.then
par exemple. Consultez cette réponse si vous souhaitez que l'exécutable existe dans$PATH
.Cela dépend si vous voulez savoir si elle existe dans l'un des répertoires de la
$PATH
variable ou si vous en connaissez l'emplacement absolu. Si vous voulez savoir si elle se trouve dans la$PATH
variable, utilisezsinon utiliser
La redirection vers
/dev/null/
dans le premier exemple supprime la sortie duwhich
programme.la source
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
if
déclaration:Voici comment l'utiliser:
la source
command
ré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.shopt -u expand_aliases
ignore / masque les alias (comme ceuxalias ls='ls -F'
mentionnés dans une autre réponse) et lesshopt -s expand_aliases
résout viacommand -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.Essayez d'utiliser:
ou
Depuis la page de manuel Bash sous Expressions conditionnelles :
la source
Pour utiliser
hash
, comme le suggère @lhunath , dans un script Bash:Ce script s'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
. Sihash
ne le trouve pasfoo
, le code de sortie sera1
. Sifoo
est présent, le code de sortie sera0
.&> /dev/null
redirige l'erreur standard et la sortie standard dehash
sorte qu'elle n'apparaisse pas à l'écran etecho >&2
écrit le message en erreur standard.la source
if hash foo &> /dev/null; then ...
?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,
type
a été installé (faire ce quimore
fait). La directive intégrée est donc nécessaire. Cette commande fonctionne pour moi:la source
if
syntaxe, utilisez simplementif builtin type -p vim; then ...
. Et les backticks sont une syntaxe vraiment ancienne et obsolète,$()
prise en charge même parsh
sur tous les systèmes modernes.Vérifiez les dépendances multiples et informez les utilisateurs finaux de leur statut
Exemple de sortie:
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
apt
packages sont installés avecdpkg -s
et 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?
la source
column -t
(partie d'util-linux).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?
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
--version
possibilité de quitter immédiatement (et avec succès).la source
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-p
comme un argument).command -v foo
: fonctionne avec Z shell, Bash, Dash, mais pas ash (BusyBox) (-ash: command: not found
).Notez également que
builtin
n'est pas disponible avec ash et Dash.la source
Utilisez les fonctions intégrées Bash si vous pouvez:
...
la source
which
n'est pas un Bash intégré.-P
n'est pas POSIX. Pourquoi esttype -P
préféré?La commande
-v
fonctionne 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:
Puisqu'il teste trois choses: chemin d'accès, existence et permission d'exécution.
la source
test -x $(which ls)
renvoie 0, comme le faittest -x $(which sudo)
, même s'ills
est installé et exécutable etsudo
n'est même pas installé dans le conteneur docker dans lequel je cours.test -x "$(which <command>)"
ls
- être a un alias? Je ne pense pas que cela fonctionnerait si la commande a un paramètre.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):
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.
la source
cmd; if [ $? -eq 0 ]; then
devrait être refactorisé enif cmd; then
dpkg
ouapt
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:
Ceci est basé sur la réponse sélectionnée ici et sur une autre source.
la source
Je dirais qu'il n'y a aucun moyen portable et fiable à 100% en raison de la suspension
alias
. Par exemple:Bien sûr, seul le dernier est problématique (n'offense pas Ringo!). Mais tous sont valables
alias
du point de vuecommand -v
.Afin de rejeter ceux qui pendent comme
ringo
, nous devons analyser la sortie de laalias
commande intégrée du shell et y revenir (cecommand -v
n'est pas un supérieur àalias
ici.) 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'
:la source
shopt -u expand_aliases
ignore / masque ces alias et lesshopt -s expand_aliases
montre viacommand -v
.Cela indiquera en fonction de l'emplacement si le programme existe ou non:
la source
La
which
commande peut être utile. homme quiIl renvoie 0 si l'exécutable est trouvé et renvoie 1 s'il n'est pas trouvé ou non exécutable:
Ce
which
qui est bien, c'est qu'il détermine si l'exécutable est disponible dans l'environnement quiwhich
est exécuté - cela évite quelques problèmes ...la source
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:la source
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.
Seconde. Il ne peut vous donner aucune sortie.
la source
.bashrc
j'ai un[ -z "$PS1" ] && return
préfixé par# If not running interactively, don't do anything
donc 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.sh
mais ce n'est pas une chose que vous souhaitez vous rappeler de taper à chaque fois.La variante de hachage a un écueil: sur la ligne de commande, vous pouvez par exemple saisir
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:
la source
$PATH
" —Ceci est complètement inexact. Essayez-le. Pour que cela fonctionne, one_folder doit se trouver dans le répertoire courant .J'appuie l'utilisation de "command -v". Par exemple, comme ceci:
la source
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):
la source
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 fairesudo -p "Type your password to install missing git-core: "
pour que l'invite ne sorte pas du bleu.Pour imiter Bash's
type -P cmd
, nous pouvons utiliser la compatibilité POSIXenv -i type cmd 1>/dev/null 2>&1
.la source
type
semble être unbuiltin
dans la plupart des shells donc cela ne peut pas fonctionner car lesenv
utilisationsexecvp
pour s'exécuter ne peuventcommand
donccommand
pas être unbuiltin
(et lebuiltin
sera toujours exécuté dans le même environnement). Cela échoue pour moibash
,ksh93
,zsh
,busybox [a]sh
etdash
tous qui fournissenttype
une commande du shell.S'il n'y a pas de
type
commande externe disponible (comme pris pour acquis ici ), nous pouvons utiliser la conformité POSIXenv -i sh -c 'type cmd 1>/dev/null 2>&1'
:Au moins sur Mac OS X v10.6.8 (Snow Leopard), l'utilisation de Bash 4.2.24 (2)
command -v ls
ne correspond pas à un déplacement/bin/ls-temp
.la source
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
,type
ethash
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
which
comme dans l'exemple suivant:la source
Je voulais une réponse à la même question, mais pour exécuter dans un Makefile.
la source
Scénario
Résultat
la source
J'utilise cela, car c'est très simple:
ou
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.
la source