Come fare in modo che bash interrompa l'esecuzione di uno script in caso di errore di sintassi?


15

Per essere al sicuro, vorrei che bash interrompesse l'esecuzione di uno script se si verifica un errore di sintassi.

Con mia sorpresa, non riesco a raggiungere questo obiettivo. ( set -enon è abbastanza.) Esempio:

#!/bin/bash

# Do exit on any error:
set -e

readonly a=(1 2)

# A syntax error is here:

if (( "${a[#]}" == 2 )); then
    echo ok
else
    echo not ok
fi

echo status $?

echo 'Bad: has not aborted execution on syntax error!'

Risultato (bash-3.2.39 o bash-3.2.51):

$ ./sh-on-syntax-err
./sh-on-syntax-err: line 10: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

Bene, non possiamo controllare $?dopo ogni istruzione per rilevare errori di sintassi.

(Mi aspettavo un comportamento così sicuro da un linguaggio di programmazione ragionevole ... forse questo deve essere segnalato come un bug / desiderio di colpire gli sviluppatori)

Altri esperimenti

if non fa differenza.

Rimozione if:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
(( "${a[#]}" == 2 ))
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Risultato:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

Forse, è legato all'esercizio 2 di http://mywiki.wooledge.org/BashFAQ/105 e ha qualcosa a che fare con (( )). Ma trovo ancora irragionevole continuare a eseguire dopo un errore di sintassi.

No, (( ))non fa differenza!

Si comporta male anche senza il test aritmetico! Solo uno script semplice e di base:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
echo "${a[#]}"
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Risultato:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

set -enon è sufficiente perché l'errore di sintassi è in ifun'istruzione. In qualsiasi altro posto dovrebbe interrompere la sceneggiatura.
Giordania,

@jordanm Ok, questa può essere una spiegazione del perché set -enon ha funzionato. Ma la mia domanda ha ancora senso. È possibile interrompere qualsiasi errore di sintassi?
imz - Ivan Zakharyaschev

@jordanm Rimosso "if"; non fa alcuna differenza (aggiornata la mia domanda).
imz - Ivan Zakharyaschev

Risposte:


9

Avvolgere il tutto in una funzione sembra fare il trucco:

#!/bin/bash -e

main () {
readonly a=(1 2)
    # A syntax error is here:
    if (( "${a[#]}" == 2 )); then
        echo ok
    else
        echo not ok
    fi
    echo status $?
    echo 'Bad: has not aborted execution on syntax error!'
}

main "$@"

Risultato:

$ ./sh-on-syntax-err 
$ ./sh-on-syntax-err line 6: #: syntax error: operand expected (error token is "#")
$ 

Anche se non ho idea del perché - forse qualcun altro può spiegare?


2
Ora la definizione della funzione viene analizzata e valutata e non riesce.
triplo

Bella soluzione! A proposito, in questo caso non interrompe l'intero programma. Ho aggiunto echo 'Bad2: has not aborted the execution after bad main!'l'ultimo esempio e l'output è: $ LC_ALL = C ./sh-on-syntax-err ./sh-on-syntax-err: riga 6: #: errore di sintassi: operando previsto ( il token di errore è "#") Bad2: non ha interrotto l'esecuzione dopo un errore principale! $
imz - Ivan Zakharyaschev

Ma non dovremmo aggiungere una linea semplicemente allora, dovremmo mettere tutto all'interno di una funzione.
imz - Ivan Zakharyaschev

@tripleee Sì, sembra che l'analisi della funzione fallisca, quindi non è completa, ma in questo caso l'intero programma non viene interrotto in realtà (quindi probabilmente non è l'effetto dell'uscita all'errore.
imz - Ivan Zakharyaschev,

6

Probabilmente sei fuorviante sul vero significato di set -e. Una lettura attenta dell'output degli help setspettacoli:

-e  Exit immediately if a command exits with a non-zero status.

Quindi -elo stato di uscita dei comandi è diverso da zero, non gli errori di sintassi nello script.

In generale, è considerato una cattiva pratica da utilizzare set -e, poiché tutti gli errori (ovvero tutti i rendimenti diversi da zero dai comandi) devono essere gestiti in modo intelligente dallo script (si pensi a uno script robusto, non a quelli che diventano selvaggi dopo aver inserito un nome file con un spazio o che inizia con un hypen).

A seconda del tipo di errore di sintassi, lo script potrebbe non essere nemmeno eseguito affatto. Non sono abbastanza informato in bash per dire esattamente quale classe di errori di sintassi (se solo possono essere classificati) potrebbe portare ad un aborto immediato o meno dello script. Forse alcuni guru dei Bash si uniranno e chiariranno tutto.

Spero solo di aver chiarito la set -edichiarazione!

A proposito del tuo desiderio:

Mi aspettavo un comportamento così sicuro da un linguaggio di programmazione ragionevole ... forse questo deve essere segnalato come un bug / desiderio di colpire gli sviluppatori

La risposta è decisamente no! poiché ciò che hai osservato ( set -enon rispondere come ti aspetti) è in realtà molto ben documentato.


Volevo dire che l'assenza di tale funzionalità è un problema. Non volevo concentrarmi set -e- è solo un po 'vicino ai miei obiettivi, ecco perché è menzionato e usato qui. La mia domanda non riguarda set -e: non è sicura la sicurezza di bash se non si può fare per interrompere errori di sintassi. Sto cercando un modo per interrompere sempre gli errori di sintassi.
imz - Ivan Zakharyaschev

4

Puoi fare in modo che lo script controlli se stesso mettendo qualcosa di simile

bash -n "$0"

vicino alla parte superiore dello script - dopo set -ema prima di qualsiasi pezzo di codice significativo.

Devo dire che questo non sembra molto robusto, ma se funziona per te, forse è accettabile.


Eccellente! Oppure, senza set -e: bash -n "$0" || exit
Daniel S

0

Innanzitutto, (( ))in bash è usato come calcolo aritmetico, non da usare in if ... usa []per quello.

In secondo luogo, ${a[#]}è strano ed è per questo che sta dando errori ... il #non ha alcun significato di matrice

Non so che cosa vuoi fare con questo, ma presumo che tu voglia conoscere il numero di campi, quindi vuoi ${#a[*]}invece

Infine, quando si confrontano numeri interi, -eqsi consiglia di over ==(utilizzato per le stringhe). l' ==sarà anche funzionare, ma -eqè raccomandato.

quindi tu vuoi:

if [ ${#a[*]} -eq 2 ]; then 

4
Non è vero. È comune utilizzare la ((parola chiave con la ifparola chiave. Ad esempio if (( 5 * $b > 53 )),. A meno che tu non stia mirando alla portabilità con gusci più vecchi, [[è generalmente preferibile rispetto a [.

2
Sì, sono d'accordo con @Evan - [[e ((sono stati progettati specificamente come test leggeri da utilizzare con "if" ecc. - diversamente dal fatto [che non generano mai un sottoprocesso per valutare la condizione.
imz - Ivan Zakharyaschev

1
[è un built-in bash. Le shell più vecchie si aspettano che sia il suo programma.
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.