Script Linux Shell: exécutez un programme uniquement s'il existe, ignorez-le s'il n'existe pas
Je programme un script Shell Linux qui affichera des bannières d'état pendant son exécution uniquement si l'outil approprié, par exemple figlet
, est installé (c'est: accessible) sur le système chemin).
Exemple:
#!/usr/bin/env bash
echo "foo"
figlet "Starting"
echo "moo"
figlet "Working"
echo "foo moo"
figlet "Finished"
J'aimerais que mon script fonctionne sans erreurs même lorsque figlet
est non installé.
Que pourrait être un méthode pratique?
Mon interprétation utiliserait une fonction wrapper nommée de la même manière que l'outil; dans cette fonction, exécutez l'outil réel s'il existe:
figlet() {
command -v figlet >/dev/null && command figlet "$@"
}
Vous pouvez alors avoir figlet arg1 arg2...
inchangé dans votre script.
@Olorin a proposé une méthode plus simple: définir une fonction wrapper uniquement si nous en avons besoin (si l'outil n'existe pas):
if ! command -v figlet > /dev/null; then figlet() { :; }; fi
Si vous souhaitez que les arguments de figlet
soient imprimés même si la figlet n'est pas installée, ajustez la suggestion d'Olorin comme suit:
if ! command -v figlet > /dev/null; then figlet() { printf '%s\n' "$*"; }; fi
Vous pouvez tester pour voir si figlet
existe
if type figlet >/dev/null 2>&1
then
echo Figlet is installed
fi
Une façon courante de le faire est avec test -x
alias [ -x
. Voici un exemple tiré de /etc/init.d/ntp
sur un système Linux:
if [ -x /usr/bin/lockfile-create ]; then
lockfile-create $LOCKFILE
lockfile-touch $LOCKFILE &
LOCKTOUCHPID="$!"
fi
Cette variante repose sur la connaissance du chemin complet de l'exécutable. Dans /bin/lesspipe
J'ai trouvé un exemple qui contourne cela en combinant -x
et la commande which
:
if [ -x "`which bunzip`" ]; then bunzip -c "$1"
else echo "No bunzip available"; fi ;;
De cette façon, cela fonctionnera sans savoir à l'avance où se trouve l'exécutable PATH
bunzip
.
Au début de votre script, vérifiez si figlet
existe, et si ce n'est pas le cas, définissez une fonction Shell qui ne fait rien:
type figlet >/dev/null 2>&1 || figlet() { :; }
type
vérifie si figlet
existe en tant que shell intégré, fonction, alias ou mot-clé, >/dev/null 2>&1
ignore stdin et stdout pour que vous n'obteniez aucune sortie, et si elle n'existe pas, figlet() { :; }
définit figlet
comme une fonction qui ne fait rien.
De cette façon, vous n'avez pas à modifier chaque ligne de votre script qui utilise figlet
, ni à vérifier si elle existe à chaque fois que figlet
est appelé.
Vous pouvez ajouter un message de diagnostic, si vous le souhaitez:
type figlet >/dev/null 2>&1 || { echo 'figlet not installed.' ; figlet() { :; } ; }
En prime, puisque vous n'avez pas mentionné le Shell que vous utilisez, je pense que c'est compatible POSIX, donc cela devrait fonctionner sur la plupart des Shell.
Une autre alternative - un modèle que j'ai vu dans les scripts de configuration automatique du projet:
if [ -x /usr/bin/figlet ]
then
FIGLET=/usr/bin/figlet
else
FIGLET=:
fi
$FIGLET "Hello, world!"
Dans votre cas spécifique, vous pourriez même le faire,
if [ -x /usr/bin/figlet ]
then
SAY=/usr/bin/figlet
Elif [ -x /usr/local/bin/figlet ]
then
SAY=/usr/local/bin/figlet
Elif [ -x /usr/bin/banner ]
then
SAY=/usr/bin/banner
else
SAY=/usr/bin/echo
fi
$SAY "Hello, world!"
Si vous ne connaissez pas le chemin spécifique, vous pouvez essayer plusieurs Elif
(voir ci-dessus) pour essayer des emplacements connus, ou simplement utiliser le PATH
pour toujours résoudre la commande:
if command -v figlet >/dev/null
then
SAY=figlet
Elif command -v banner >/dev/null
then
SAY=banner
else
SAY=echo
fi
En général, lors de l'écriture de scripts, je préfère appeler uniquement des commandes dans des emplacements spécifiques spécifiés par moi. Je n'aime pas l'incertitude/le risque de ce que l'utilisateur final aurait pu mettre dans son PATH
, peut-être dans son propre ~/bin
.
Si, par exemple, j'écrivais un script compliqué pour d'autres qui pourrait supprimer des fichiers en fonction de la sortie d'une commande particulière que j'appelle, je ne voudrais pas accidentellement récupérer quelque chose dans leur ~/bin
qui pourrait ou non être la commande que j'attendais.
type -p figlet > /dev/null && figlet "foo"
La commande bash type
recherche une commande, une fonction, un alias, un mot-clé ou une fonction intégrée (voir help type
) et imprime l'emplacement ou la définition. Il renvoie également un code retour représentant le résultat de la recherche; true (0) si trouvé. Nous essayons donc de trouver figlet
dans le chemin (-p
signifie uniquement rechercher des fichiers, pas des fonctions intégrées ou des fonctions, et supprime également les messages d'erreur), rejetant la sortie (c'est ce que > /dev/null
le fait), et s'il renvoie vrai (&&
), il exécutera figlet
.
C'est plus simple si figlet
est dans un emplacement fixe:
[ -x /usr/bin/figlet ] && /usr/bin/figlet "foo"
Ici, nous utilisons la commande test
(a.k.a. [
) Pour voir si /usr/bin/figlet
est exécutable (-x
) et si oui (&&
) l'exécuter. Je pense que cette solution est plus portable que d'utiliser type
qui est un bashisme je crois.
Vous pouvez créer une fonction qui le fait pour vous:
function x() {
if type -p "$1" >/dev/null; then
cmd="$1"
shift
"$cmd" "$@"
fi
}
(Les devis sont nécessaires en raison d'espaces potentiels)
Il vous suffirait alors de faire:
x figlet "foo"
Vous pouvez effectuer une exécution de test, supprimer toute sortie et tester le code de réussite/échec. Choisissez des arguments à figlet pour rendre ce test peu coûteux. -? ou --help ou --version sont des possibilités évidentes.
if figlet --help >/dev/null 2>&1 ; then
# figlet is available
echo "foo"
figlet "starting"
#etc
else
rc=$?
echo "figlet is not installed or not working correctly (return code ${rc})"
fi
Ajouté en réponse au commentaire ci-dessous: si vous voulez vraiment tester que la figlet existe, pas qu'elle soit utilisable, alors vous feriez
figlet --help >/dev/null 2>&1
rc=$?
if [ $rc -eq 127 ] ; then # 127 is "command not found" on linux bash 4.4.23(1)
echo "command figlet not found"
else
figlet "whatever" # use figlet
fi
o /, je dirais quelque chose comme
#!/usr/bin/env bash
# if figlet is installed :
if [ "$(which figlet 2>/dev/null)" ]; then
# do what you wanted to do
echo "foo"
figlet "Starting"
echo "moo"
figlet "Working"
echo "foo moo"
figlet "Finished"
# if not
else
# exit program with an error
echo "please install figlet"
exit 1
fi