Script shell Linux: exécutez un programme uniquement s'il existe, ignorez-le s'il n'existe pas

15

Je programme un script shell Linux qui imprimera des bannières d'état pendant son exécution uniquement si l'outil approprié, par exemple figlet, est installé (c'est-à-dire: accessible sur le chemin système ).

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 erreur même lorsqu'il figletn'est pas installé .

Quelle pourrait être une méthode pratique ?

Sopalajo de Arrierez
la source
@sudodus: simplement ignorer la commande 'figlet' (et ses paramètres) serait OK. Poursuite de l'exécution, bien sûr.
Sopalajo de Arrierez
2
Le titre de cette question m'a mis dans toutes sortes de problèmes métaphysiques
g_uint
2
Voulez-vous ignorer toutes les erreurs? Utilisez simplement figlet ... || true.
Giacomo Alzetta
Si vous ne vous souciez pas des codes de sortie, un raccourci doit être utilisé figlet || true, mais dans votre cas, probablement une fonction shell qui peut être imprimée en clair Echos Si aucune bannière ne peut être imprimée est plus probablement ce que vous voulez.
vérifie le

Réponses:

30

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 "$@"
}

Ensuite, vous pouvez 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 figletsoient 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
Jeff Schaller
la source
1
D'où commandvient-il? Je ne l'ai pas dans mes installations (Red Hat 6.8 Enterprise et Cygwin64).
eewanco
1
@eewanco, voir unix.stackexchange.com/a/85250/117549 ; Pour faire court, il est intégré à bash, qui est probablement votre shell.
Jeff Schaller
4
commandest un shell intégré POSIX Bourne. Il exécute normalement la commande fournie, mais l' -vindicateur le fait se comporter davantage comme typeun autre shell intégré.
wyrm
@eewanco type -a commandvous le montrera. whichsera seulement montrer sur vos executables $PATH, pas Encastrements, mots - clés, des fonctions ou des alias.
l0b0
@ l0b0: sur RedHat-famille avec bash et le profil par défaut (s) which ne trouve un alias applicable, car il alias whichlui - même à courir /usr/bin/which(!) et redirigez une liste des noms d' emprunt à regarder dans (Bien sûr du shell \whichsupprime l'alias et utilise uniquement le programme, qui n'affiche pas les alias.)
dave_thompson_085
14

Vous pouvez tester pour voir s'il figletexiste

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

Une façon courante de le faire est avec test -xaka [ -x. Voici un exemple tiré d' /etc/init.d/ntpun 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/lesspipej'ai trouvé un exemple qui contourne cela en combinant -xet la whichcommande:

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 PATHl' bunzipexécutable.

kasperd
la source
4
Ne pas utiliserwhich . Et même si cela whichfonctionne, l'utilisation test -xde sa sortie est idiote: si vous obtenez un chemin which, il existe.
Gilles 'SO- arrête d'être méchant'
1
@ Gilles: Techniquement parlant, test -xfait plus que simplement vérifier si un fichier existe (c'est pour ça test -e). Il vérifie également si le fichier a les autorisations d'exécution définies.
comfreak
@comfreak Il en va de même which.
Gilles 'SO- arrête d'être méchant'
5

Au début de votre script, vérifiez s'il figletexiste, et si ce n'est pas le cas, définissez une fonction shell qui ne fait rien:

type figlet >/dev/null 2>&1 || figlet() { :; }

typevérifie s'il figletexiste en tant que shell intégré, fonction, alias ou mot clé, >/dev/null 2>&1supprime stdin et stdout pour que vous n'obteniez aucune sortie, et s'il n'existe pas, figlet() { :; }définit figletcomme une fonction qui ne fait rien.

De cette façon, vous n'avez pas à modifier chaque ligne de votre script qui utilise figletou à vérifier si elle existe à chaque figletappel.

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.

Chris
la source
J'aime la simplicité de cette réponse. Vous pouvez également remplacer type figlet >/dev/null 2>&1par hash figlet 2>/dev/nullsi vous utilisez bash. (L'OP a dit "si c'est dans mon CHEMIN".)
Joe
5

Une autre alternative - un modèle que j'ai vu dans les scripts de configuration automatique de 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 PATHpour 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 pourrait avoir mis dans leur PATH, peut-être dans le leur ~/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 ramasser quelque chose dans leur ~/binqui pourrait ou non être la commande J'esperais.

rrauenza
la source
3
Et si figletc'était dans /usr/local/binou /home/bob/stuff/programs/executable/figlet?
Gilles 'SO- arrête d'être méchant'
3
type -p figlet > /dev/null && figlet "foo"

La typecommande bash 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é. Donc, ce que nous faisons ici est d'essayer de trouver figletdans le chemin ( -psignifie seulement rechercher des fichiers, pas des fonctions intégrées ou, et supprime également les messages d'erreur), rejeter la sortie (c'est ce qui > /dev/nullfait), et si elle retourne true ( &&) , il s'exécutera figlet.

Ceci est plus simple si se figlettrouve dans un emplacement fixe:

[ -x /usr/bin/figlet ] && /usr/bin/figlet "foo"

Ici, nous utilisons la testcommande (aka [) pour voir si /usr/bin/figletest exécutable ( -x) et si oui ( &&) l'exécuter. Je pense que cette solution est plus portable que l'utilisation, typece 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"
eewanco
la source
0

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

Vous pouvez effectuer une exécution de test, supprimer toute sortie et tester le code de réussite / échec. Choisissez les 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
nigel222
la source
Le problème ici est que figlet peut échouer pour des raisons autres que son installation, il ne suffit donc pas de tester le code de sortie.
Chris
Si vous voulez vraiment tester uniquement pour son installation, alors vous voulez tester si $?est égal à 127 (sur mon système Linux). 127 est "commande introuvable". Mais ma pensée est que pour les commandes rationnelles, en cas d' command --helpéchec, l'installation est suffisamment borkée pour qu'elle ne soit pas là!
nigel222
1
126 est «commande trouvée mais non exécutable», vous devriez donc également tester cela. Malheureusement, vous ne pouvez pas dépendre de votre --helpdisponibilité. Les utilitaires Posix ne l'ont pas, pour commencer, et les directives posix le déconseillent . at --helpéchoue avec at: invalid option -- '-', par exemple, et un état de sortie de 130sur mon système.
Chris
Bien sûr, si figlet peut sortir avec le statut 127, cela pourrait aussi être un problème.
Chris
Juste point 126. La plupart des utilitaires sensés ont une sorte d'options de commande légères et inoffensives, telles que -? --help --version... ou vous pouvez découvrir ce qui se figlet -0 --illegaltermine avec, et le traiter comme votre indicateur de succès (tant qu'il n'est pas 127, que je classerais comme sabotage).
nigel222