Script di shell Linux: esegui un programma solo se esiste, ignoralo se non esiste


15

Sto programmando uno script di shell Linux che stamperà i banner di stato durante la sua esecuzione solo se figletè installato lo strumento appropriato, ad esempio : raggiungibile sul percorso di sistema .

Esempio:

#!/usr/bin/env bash
echo "foo"
figlet "Starting"
echo "moo"
figlet "Working"
echo "foo moo"
figlet "Finished"

Vorrei che il mio script funzionasse senza errori anche quando nonfiglet è installato .

Quale potrebbe essere un metodo pratico ?



@sudodus: ignorare semplicemente il comando 'figlet' (e i suoi parametri) sarebbe OK. Esecuzione continua, ovviamente.
Sopalajo de Arrierez,

2
Il titolo di questa domanda mi ha messo in ogni sorta di problemi metafisici
g_uint

2
Vuoi ignorare tutti gli errori? Basta usare figlet ... || true.
Giacomo Alzetta,

Se non ti interessano i codici di uscita, devi usare una scorciatoia figlet || true, ma nel tuo caso probabilmente una funzione shell che è Echos in chiaro Se nessun Banner può essere stampato è più probabile quello che vuoi.
Verifica il

Risposte:


30

La mia interpretazione userebbe una funzione wrapper denominata come lo strumento; in quella funzione, esegui lo strumento reale se esiste:

figlet() {
  command -v figlet >/dev/null && command figlet "$@"
}

Quindi puoi rimanere figlet arg1 arg2...invariato nella tua sceneggiatura.

@Olorin ha escogitato un metodo più semplice: definire una funzione wrapper solo se necessario (se lo strumento non esiste):

if ! command -v figlet > /dev/null; then figlet() { :; }; fi

Se desideri che gli argomenti figletvengano stampati anche se il figlet non è installato, modifica il suggerimento di Olorin come segue:

if ! command -v figlet > /dev/null; then figlet() { printf '%s\n' "$*"; }; fi

1
Da dove commandviene Non ce l'ho nelle mie installazioni (Red Hat 6.8 Enterprise e Cygwin64).
eewanco,

1
@eewanco, vedi unix.stackexchange.com/a/85250/117549 ; per farla breve, è integrato in bash, che è probabilmente la tua shell.
Jeff Schaller

4
commandè una shell POSIX Bourne integrata. Normalmente esegue il comando fornito, ma il -vflag lo fa comportare più come typeun altro shell incorporato.
wyrm,

@eewanco type -a commandti mostrerà. whichmostrerà solo file eseguibili sui tuoi $PATH, non integrati, parole chiave, funzioni o alias.
l0b0

@ l0b0: su RedHat-famiglia con bash e il profilo predefinito (s) which non trovare un alias del caso, perché alias whichstesso per eseguire /usr/bin/which(!) e il tubo è un elenco di alias della shell di guardare a (Naturalmente \whichsopprime l'alias e utilizza solo il programma, che non mostra alias.)
dave_thompson_085

14

Puoi provare per vedere se figletesiste

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

6

Un modo comune per farlo è con test -xaka [ -x. Ecco un esempio tratto da /etc/init.d/ntpun sistema Linux:

if [ -x /usr/bin/lockfile-create ]; then
    lockfile-create $LOCKFILE
    lockfile-touch $LOCKFILE &
    LOCKTOUCHPID="$!"
fi

Questa variante si basa sulla conoscenza del percorso completo dell'eseguibile. In /bin/lesspipeho trovato un esempio che aggira quello combinando -xe il whichcomando:

if [ -x "`which bunzip`" ]; then bunzip -c "$1"
else echo "No bunzip available"; fi ;;

In questo modo funzionerà senza sapere in anticipo dove si trova PATHl' bunzipeseguibile.


4
Non usarewhich . E anche se ha whichfunzionato, usare test -xil suo output è stupido: se ottieni un percorso da which, esiste.
Gilles 'SO- smetti di essere malvagio' il

1
@Gilles: tecnicamente parlando, test -xfa molto di più che controllare se esiste un file (ecco a cosa test -eserve). Verifica inoltre se il file ha le autorizzazioni di esecuzione impostate.
comfreak

@comfreak Anche così which.
Gilles 'SO- smetti di essere malvagio' il

5

All'inizio dello script, controlla se figletesiste e, in caso contrario, definisci una funzione shell che non fa nulla:

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

typecontrolla se figletesiste come shell integrata, funzione, alias o parola chiave, >/dev/null 2>&1elimina stdin e stdout in modo da non ottenere alcun output e, se non esiste, figlet() { :; }definisce figletcome una funzione che non fa nulla.

In questo modo non è necessario modificare ogni riga dello script che utilizza figleto verificare se esiste ogni volta che figletviene chiamato.

Puoi aggiungere un messaggio diagnostico, se ti piace:

type figlet >/dev/null 2>&1 || { echo 'figlet not installed.' ; figlet() { :; } ; }

Come bonus, dal momento che non hai menzionato quale shell stai usando, credo che questo sia conforme a POSIX, quindi dovrebbe funzionare su quasi tutte le shell.


Mi piace la semplicità di questa risposta. Puoi anche sostituirlo type figlet >/dev/null 2>&1con hash figlet 2>/dev/nullse stai usando bash. (L'OP ha detto "se è nel mio PERCORSO".)
Joe

5

Un'altra alternativa - un modello che ho visto negli script di configurazione automatica del progetto:

if [ -x /usr/bin/figlet ]
then
    FIGLET=/usr/bin/figlet
else
    FIGLET=:
fi

$FIGLET "Hello, world!"

Nel tuo caso specifico potresti persino fare,

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!"

Se non conosci il percorso specifico, puoi provare più elif(vedi sopra) per provare posizioni conosciute o semplicemente usare il PATHper risolvere sempre il comando:

if command -v figlet >/dev/null
then
    SAY=figlet
elif command -v banner >/dev/null
then
    SAY=banner
else
    SAY=echo
fi

In generale, quando scrivo gli script, preferisco chiamare solo i comandi in posizioni specifiche da me specificate. Non mi piace l'incertezza / il rischio di ciò che l'utente finale avrebbe potuto mettere nel proprio PATH, forse nel proprio ~/bin.

Se, ad esempio, stavo scrivendo uno script complicato per altri che potrebbe rimuovere i file in base all'output di un particolare comando che sto chiamando, non vorrei prendere accidentalmente qualcosa nel loro ~/binche potrebbe essere o non essere il comando Mi aspettavo


3
E se figletfosse dentro /usr/local/bino /home/bob/stuff/programs/executable/figlet?
Gilles 'SO- smetti di essere malvagio' il

3
type -p figlet > /dev/null && figlet "foo"

Il typecomando bash trova un comando, una funzione, un alias, una parola chiave o un builtin (vedihelp type ) e stampa la posizione o la definizione. Restituisce anche un codice di ritorno che rappresenta il risultato della ricerca; vero (0) se trovato. Quindi quello che stiamo facendo qui sta provando a trovare figletnel percorso ( -psignifica solo cercare file, non incorporati o funzioni, e sopprime anche i messaggi di errore), scartando l'output (è quello che > /dev/nullfa) e se restituisce true ( &&) , verrà eseguito figlet.

Questo è più semplice se si figlettrova in una posizione fissa:

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

Qui stiamo usando il testcomando (aka[ ) per vedere se /usr/bin/figletè eseguibile ( -x) e in tal caso ( &&) eseguirlo. Credo che questa soluzione sia più portatile dell'uso, il typeche è un basismo.

Potresti creare una funzione che faccia questo per te:

function x() {
    if type -p "$1" >/dev/null; then
        cmd="$1"
        shift
        "$cmd" "$@"
    fi
}

(Le virgolette sono necessarie a causa di spazi potenziali)

Quindi faresti semplicemente:

x figlet "foo"

0

o / direi qualcosa del genere

#!/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

È possibile eseguire un'esecuzione di test, sopprimendo qualsiasi output e testare il codice di successo / fallimento. Scegli argomenti da figlet per rendere questo test economico. -? oppure --help o --version sono ovvie possibilità.

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

Aggiunto in risposta al commento qui sotto: se vuoi davvero testare che il figlet esiste, non che sia utilizzabile, allora lo faresti

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

Il problema qui è che figlet può fallire per motivi oltre a non essere installato, quindi solo testare il codice di uscita non è sufficiente.
Chris

Se vuoi davvero testare solo per la sua installazione, allora vuoi testare se è $?uguale a 127 (sul mio sistema Linux). 127 è "comando non trovato". Ma il mio pensiero è che per i comandi razionali, se command --helpfallisce, allora l'installazione è sufficientemente estesa da non poter essere lì !.
nigel222

1
126 è "comando trovato ma non eseguibile", quindi ti consigliamo di provare anche quello. Sfortunatamente, non puoi dipendere dalla tua --helpdisponibilità. Le utility Posix non ce l'hanno, per uno, e le linee guida posix in realtà sconsigliano . at --helpfallisce at: invalid option -- '-', ad esempio, con uno stato di uscita 130sul mio sistema.
Chris

Inoltre, ovviamente, se figlet può uscire con lo stato 127, anche questo potrebbe essere un problema.
Chris,

Punto giusto circa 126. Le utility più sane hanno una sorta di opzioni di comando innocue leggere, come -? --help --version... o potresti scoprire cosa figlet -0 --illegalesce e trattarlo come indicatore di successo (purché non sia 127, che classificherei come sabotaggio).
nigel222
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.