web-dev-qa-db-fra.com

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?

15

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
30
Jeff Schaller

Vous pouvez tester pour voir si figlet existe

if type figlet >/dev/null 2>&1
then
    echo Figlet is installed
fi
14
roaima

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 PATHbunzip.

6
kasperd

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.

5
Chris

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.

5
rrauenza
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"
3
eewanco

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
0
nigel222

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
0
Jus de Patate_