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.