Il debug ti aiuta a correggere gli errori nel tuo programma. In questo articolo, discuteremo vari metodi per eseguire il debug degli script bash nei sistemi operativi Linux e Unix.
Introduzione
Nei miei primi giorni di programmazione, ho passato ore a cercare di trovare l'errore nel mio codice e, alla fine, potrebbe essere qualcosa di semplice. Potresti aver affrontato la stessa situazione anche tu.
Sapere come utilizzare la tecnica di debug corretta ti aiuterebbe a risolvere rapidamente gli errori. A differenza di altri linguaggi come Python, Java, ecc. non esiste uno strumento di debugger per bash in cui puoi impostare punti di interruzione, eseguire il passaggio del codice, ecc.
Ci sono alcune funzionalità integrate che aiutano a eseguire il debug degli script della shell bash. Vedremo in dettaglio queste funzionalità nelle prossime sezioni.
Tre modi per utilizzare le opzioni di debug
Quando vuoi abilitare le opzioni di debug nei tuoi script, puoi farlo in tre modi.
1 . Abilita le opzioni di debug dalla shell del terminale quando chiami lo script.
$ bash [ debugging flags ] scriptname
2 . Abilita le opzioni di debug passando i flag di debug alla riga shebang nello script.
#!/bin/bash [ debugging flags ]
3 . Abilita le opzioni di debug usando il set
comando dallo script.
set -o nounset
set -u
A cosa serve il comando Imposta?
Il set
command è un comando integrato nella shell che può essere utilizzato per controllare i parametri bash e modificare il comportamento di bash in determinati modi.
Normalmente non eseguirai i comandi set dal terminale per alterare il comportamento della shell. Sarà ampiamente utilizzato all'interno degli script di shell sia per il debug che per abilitare la modalità bash strict.
$ type -a set
set is a shell builtin
Puoi accedere alla sezione della guida del comando set per sapere quali flag supporta e cosa fa ogni flag.
$ set --help
Esegui il debug di una parte dello script o dello script completo
Prima di conoscere le opzioni di debug, devi capire che puoi eseguire il debug dell'intero script o solo di una determinata parte del codice. Devi usare il comando set per abilitare e disabilitare le opzioni di debug.
set -<debugging-flag>
abiliterà la modalità di debug.set +<debugging-flag>
disabiliterà la modalità di debug.
Dai un'occhiata al codice qui sotto. set -x
abiliterà la modalità xtrace per lo script e set +x
disabiliterà la modalità xtrace. Tutto ciò che si trova tra set -x
e set +x
verrà eseguito in modalità di debug xtrace.
Imparerai la modalità xtrace nella prossima sezione. Quindi, per qualsiasi flag di debug, l'unica cosa che devi ricordare è, set -
abiliterà la modalità e set +
disabiliterà la modalità.
#!/bin/bash set -x read -p "Pass Dir name : " D_OBJECT read -p "Pass File name : " F_OBJECT set +x touch ${D_OBJECT}/${F_OBJECT}
Fallito se non è definita alcuna variabile
Quando lavori con variabili in bash , lo svantaggio è che se proviamo a utilizzare una variabile non definita, lo script non fallirà con alcuni messaggi di errore come "Variabile non definita" . Invece stamperà una stringa vuota.
Dai un'occhiata al codice seguente in cui ricevo input dall'utente e lo memorizzo nella variabile $OBJECT
. Ho provato a eseguire l'operatore di test (-f
e -d
) sul $OBJECT1
variabile non definita.
#!/bin/bash read -p "Please provide the object name : " OBJECT if [[ -f $OBJECT1 ]] then echo "$OBJECT is a file" elif [[ -d $OBJECT1 ]] then echo "$OBJECT is a directory" fi
Quando eseguo questo codice, dovrebbe avermi generato un errore ma non è stato così e anche lo script è uscito con il codice di ritorno zero.
Per ignorare questo comportamento, usa -u
flag che genererà un errore quando viene utilizzata una variabile non definita.
Eseguirò di nuovo lo stesso codice con il nome della variabile sbagliato ma questa volta genererà una "Variabile non associata" errore.
Puoi anche impostare il -u
opzione utilizzando il set
comandalo o passalo come argomento allo shebang.
set -u
set -o nounset
(o)
#! /bin/bash -u
Modalità Xtrace in soccorso
Questa è la modalità che uso ampiamente quando eseguo il debug degli script bash per errori logici. Xtrace la modalità visualizzerà il codice riga per riga ma con i parametri espansi.
Nella sezione precedente, quando ho eseguito il codice senza -u
flag, è stato completato con successo ma mi aspettavo l'output nel terminale. Ora posso eseguire lo stesso script in modalità xtrace e vedere esattamente dove si verifica il problema nello script.
Dai un'occhiata al seguente codice di esempio.
#!/bin/bash read -p "Please provide the object name : " OBJECT if [[ -f $OBJECT1 ]] then echo "$OBJECT is a file" elif [[ -d $OBJECT1 ]] then echo "$OBJECT is a directory" fi
Quando eseguo il codice sopra, non mi restituisce alcun output.
Per eseguire il debug di questo problema, posso eseguire lo script in xtrace mode passando il -x
bandiera.
Nell'output sottostante, puoi vedere le variabili espanse e stampate. Questo mi dice che ci sono stringhe vuote assegnate alle istruzioni condizionali -f
e -d
. In questo modo posso logicamente controllare e correggere gli errori.
Il segno più che vedi nell'output può essere modificato impostando il PS4
variabile nello script. Per impostazione predefinita, PS4 è impostata su (+
).
$ echo $PS4
+
$ PS4=" ==> " bash -x debugging.sh
Puoi anche impostare la modalità Xtrace usando il comando set o passarlo come argomento allo shebang.
set -x
set -o xtrace
(o)
#! /bin/bash -x
Allo stesso modo, durante il debug è possibile reindirizzare i log di debug di Xtrace su un file invece di stamparli sul terminale.
Dai un'occhiata al codice qui sotto. Sto assegnando un descrittore di file 6 al .log
file e BASH_XTRACEFD="6"
reindirizzerà i log di debug di xtrace al descrittore di file 6.
#!/bin/bash exec 6> redirected_debug.log PS4=' ==> ' BASH_XTRACEFD="6" read -p "Please provide the object name : " OBJECT if [[ -f $OBJECT1 ]] then echo "$OBJECT is a file" elif [[ -d $OBJECT1 ]] then echo "$OBJECT is a directory" fi
Quando eseguo questo codice invece di stampare l'output di xtrace nel terminale, verrà reindirizzato a .log
file.
$ cat redirected_debug.log ==> read -p 'Please provide the object name : ' OBJECT ==> [[ -f '' ]] ==> [[ -d '' ]]
Stato uscita TUBO
Il comportamento predefinito quando si utilizza una pipe è che prenderà il codice di uscita dell'ultimo comando di esecuzione nella pipe. Anche se i comandi precedenti nella pipe non sono riusciti, eseguirà il resto della pipe.
Dai un'occhiata all'esempio qui sotto. Ho provato ad aprire un file che non è disponibile e a inviarlo tramite pipe con un conteggio parole programma. Anche se il cat
comando genera un errore, il programma di conteggio delle parole viene eseguito.
Se provi a controllare il codice di uscita dell'ultimo comando run pipe utilizzando $?
, otterrai zero come codice di uscita che proviene dal programma di conteggio delle parole.
$ cat nofile.txt | wc -l cat: nofile.txt: No such file or directory 0
$ echo $? 0
Quando pipefail è abilitato nello script, se un comando genera un codice di ritorno diverso da zero nella pipe, verrà considerato come il codice di ritorno per l'intera pipeline. Puoi abilitare pipefail aggiungendo la seguente proprietà set nel tuo script.
set -o pipefail
C'è ancora un problema con questo approccio. Normalmente ciò che dovrebbe essere previsto è che se un comando nella pipe non riesce, lo script dovrebbe uscire senza eseguire il resto del comando nella pipe.
Ma sfortunatamente, anche se un comando non riesce, il comando successivo nella pipe viene eseguito. Questo perché ogni comando nella pipe viene eseguito nella propria subshell. La shell attenderà il completamento di tutti i processi nella pipe, quindi restituirà il risultato.
Modalità Bash Strict
Per eliminare tutti i possibili errori che abbiamo visto nelle sezioni precedenti si consiglia di aggiungere le seguenti opzioni in ogni script.
Abbiamo già discusso in dettaglio tutte queste opzioni nella sezione precedente.
-e flag
=> Esci dallo script se un comando genera un codice di uscita diverso da zero.-u flag
=> Fai fallire lo script se viene utilizzato un nome di variabile non definito.pipefail
=> Se un comando nella pipeline non riesce, il codice di uscita verrà considerato per l'intera pipeline.IFS
=> Separatore di campo interno, impostandolo su newline(\n) e (\t) la divisione avviene solo in newline e tab.
set -e
set -u
set -o pipefail
Oppure
set -euo pipefail
IFS=$'\n\t'
Cattura segnali usando TRAP
Trappola ti consente di acquisire segnali nel tuo script bash e di intraprendere alcune azioni di conseguenza.
Pensa a uno scenario in cui attivi lo script ma desideri annullare lo script utilizzando CTRL+C
battitura. In tal caso, SIGINT
verrà inviato al tuo script. Puoi catturare questo segnale ed eseguire alcuni comandi o funzioni.
Dai un'occhiata allo pseudocodice riportato di seguito. Ho creato una funzione chiamata cleanup che verrà eseguita quando SIGINT
viene passato allo script.
trap 'cleanup' TERM INT function cleanup(){ echo "Running cleanup since user initiated CTRL + C" <some logic> }
È possibile utilizzare la trap "DEBUG", che può essere utilizzata per eseguire ripetutamente un'istruzione nello script. Il modo in cui si comporta è che ogni istruzione eseguita nello script trap eseguirà la funzione o l'istruzione associata.
Puoi capirlo usando l'esempio seguente.
#!/bin/bash trap 'printf "${LINENO} ==> DIR_NAME=${D_OBJECT} ; FILE_NAME=${F_OBJECT}; FILE_CREATED=${FILE_C} \n"' DEBUG read -p "Pass Dir name : " D_OBJECT read -p "Pass File name : " F_OBJECT touch ${D_OBJECT}/${F_OBJECT} && FILE_C="Yes" exit 0
Questo è un semplice programma che ottiene l'input dell'utente e crea un file e una directory. Il comando trap verrà eseguito per ogni istruzione nello script e stamperà gli argomenti passati e lo stato di creazione del file.
Dai un'occhiata all'output di seguito. Per ogni riga dello script, il trap viene attivato e le variabili vengono aggiornate di conseguenza.
Stampa il codice utilizzando la modalità dettagliata
In modalità dettagliata, il codice verrà stampato prima di restituire il risultato. Se il programma richiede un input interattivo, in tal caso verrà stampata da sola quella riga seguita da un blocco di codici.
Dai un'occhiata al seguente programma. È un semplice programma che ottiene un oggetto dall'utente e controlla se l'oggetto passato è un file o una directory utilizzando un'istruzione condizionale .
#!/bin/bash read -p "Please provide the object name : " OBJECT if [[ -f $OBJECT ]] then echo "$OBJECT is a file" elif [[ -d $OBJECT ]] then echo "$OBJECT is a directory" fi
Quando eseguo il codice sopra, prima stamperà il codice, quindi attenderà l'input dell'utente come mostrato di seguito.
Una volta passato l'oggetto, verrà stampato il resto del codice seguito dall'output.
Puoi anche impostare la modalità dettagliata usando set
o in shebang
.
set -v
set -o verbose
(o)
#! /bin/bash -v
Puoi anche combinare la modalità dettagliata con altre modalità.
set -vx # Verbose and Xtrace Mode
set -uv # Verbose and Unset Mode
Convalida della sintassi - modalità noexec
Finora abbiamo visto come lavorare con gli errori logici nello script. In questa sezione, discutiamo degli errori di sintassi.
Gli errori di sintassi sono molto comuni nei programmi. Potresti aver perso una citazione o non essere riuscito a uscire dal ciclo, ecc. Puoi usare "-n
" flag che si chiama noexec mode
per convalidare la sintassi prima di eseguire il programma.
Eseguirò il codice seguente e convaliderò la sintassi.
#!/bin/bash TOOLS=( htop peek tilix vagrant shutter ) for TOOL in "${TOOLS[@]" do echo "--------------INSTALLING: ${TOOL}---------------------------" apt install ${TOOL} -y #done
Ci sono due errori in questo programma. In primo luogo, non sono riuscito a chiudere le parentesi graffe nel "for loop
" e in secondo luogo done
la parola chiave è commentata e dovrebbe segnare la fine del ciclo.
Quando eseguo questo programma, ricevo i seguenti messaggi di errore che indicano la mancanza di parentesi graffe e parola chiave completata . A volte il numero di riga indicato nel messaggio di errore non conterrà alcun errore che dovresti cercare per trovare l'errore effettivo.
$ bash -n ./debugging.sh ./debugging.sh: line 6: unexpected EOF while looking for matching `"' ./debugging.sh: line 8: syntax error: unexpected end of file
Va notato che, per impostazione predefinita, quando esegui lo script, bash convaliderà la sintassi e genererà questi errori anche senza utilizzare la modalità noexec.
In alternativa, puoi anche utilizzare il set
comando o shebang per usare noexec
modalità.
set -n set -o noexec
Oppure,
#! /bin/bash -n
Ci sono alcuni strumenti esterni là fuori che vale la pena dare un'occhiata usati per il debug degli script. Uno di questi strumenti è Shellcheck . Shellcheck può anche essere integrato con i più diffusi editor di testo come vscode, sublime text, Atom.
Conclusione
In questo articolo, ti ho mostrato alcuni dei modi per eseguire il debug degli script bash. A differenza di altri linguaggi di programmazione, bash non ha strumenti di debug oltre ad alcune opzioni integrate. A volte queste opzioni di debug integrate saranno più che sufficienti per portare a termine il lavoro.
Guide agli script di Bash:
- Scripting Bash:analisi degli argomenti negli script Bash utilizzando getopts
- Come creare finestre di dialogo della GUI negli script Bash con Zenity in Linux e Unix
- Scripting Bash – Case Statement
- Scripting Bash – Dichiarazioni condizionali
- Scripting Bash – Manipolazione di stringhe
- Scripting Bash:comando Printf spiegato con esempi
- Scripting Bash:array indicizzato spiegato con esempi
- Scripting Bash:array associativo spiegato con esempi
- Scripting Bash:il ciclo For viene spiegato con esempi
- Scripting di Bash:spiegazione del ciclo While e Until con esempi
- Il reindirizzamento di Bash spiegato con esempi
- Scripting Bash:variabili spiegate con esempi
- Scripting Bash:funzioni spiegate con esempi
- Comando Bash Echo spiegato con esempi in Linux
- Tutorial Bash Heredoc per principianti