Vérifier si un programme existe depuis un script
En interactif, on peut avoir l’habitude d’utiliser which
qui répond facilement à la problématique.
Mais dans un script, il ne faut pas.
Et cette question simple en apparence cache en fait quelques subtilités que je vais essayer de retranscrire ici.
Remarque : C’est à l’origine un post sur Stack Overflow. Je me permets de le reprendre et traduire ici à des fins d’archivage, au cas où le post ou le site venaient à disparaitre.
POSIX
command -v <the_command>
Exemple :
if ! command -v <the_command> &> /dev/null
then
echo "<the_command> could not be found"
exit
fi
Spécifique Bash
hash <the_command> # Programmes sur le système, ou bien ...
type <the_command> # Vérifier les built-ins, mots-clefs réservés et les alias
Plus d’explications
Il faut éviter which
.
Non seulement ça utilise un processus en plus pour pas grand-chose,
mais en plus, en fonction des systèmes, les effets d’une commande externe peuvent varier.
Utiliser les built-ins du shell (comme command
, hash
ou type
) est optimal car bien plus économe en ressources.
Pourquoi s’en soucier ?
Sur certains systèmes, which
ne définit pas de statu de retour.
Cela signifie que if which foo
retournera toujours vrai, même si foo
n’est pas présent sur la machine.
Remarque : a priori certains shells POSIX font de même avec hash
.
De plus, il arrive sur certains systèmes que which
fasse des choses en plus comme changer la sortie,
voire interroger le gestionnaire de paquets.
En conclusion
Si le shebang est /bin/sh
, soyez POSIX et utilisez command
.
À noter que type
sans option est également POSIX.
Si vous utilisez Bash, type
ou hash
sont recommandés.
type
a maintenant une option -P
pour ne rechercher que dans le PATH
(non POSIX, mais en bash pur, peu importe).
hash
aura pour conséquence de mettre en cache le chemin si la commande existe,
ce qui est une bonne chose puisque le but de rechercher une commande est probablement de l’utiliser ensuite.
Un dernier exemple pour la route :
if hash systemclt 2>/dev/null; then
systemctl stop "${service}"
else
/etc/init.d/${service} stop
fi
Remarque : l’exemple ci-dessus provient d’un script qui a nécessité que je fasse une recherche sur la question, pas du post d’origine.