interrompere il piping in caso di errore

Linguaggi di programmazione: php, perl, python, C, bash e tutti gli altri.
Scrivi risposta
Avatar utente
london3
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 471
Iscrizione: domenica 19 gennaio 2014, 16:22
Desktop: ubuntu:GNOME
Distribuzione: Ubuntu 22.04.1 LTS

interrompere il piping in caso di errore

Messaggio da london3 »

Codice: Seleziona tutto

$ test1 | test2 | test3
test1: comando non trovato
test2: comando non trovato
test3: comando non trovato
Però vorrei che l'esecuzione, o meglio, il piping si interrompesse dopo il primo errore (dopo il primo comando): addirittura sono stati eseguiti tutti e tre.

Ho fatto un paio di esperimenti

Codice: Seleziona tutto

$ test1 &| test2 &| test3
bash: errore di sintassi vicino al token non atteso "|"

$ test1 &&| test2 &&| test3
bash: errore di sintassi vicino al token non atteso "|"
ma come vedete la sintassi non è accettata.
Computer: Lenovo ThinkPad L480
Avatar utente
UbuNuovo
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 4347
Iscrizione: sabato 12 dicembre 2009, 20:58
Desktop: Mate
Distribuzione: Ubuntu Mate 22.04.1 LTS
Sesso: Maschile
Contatti:

Re: interrompere il piping in caso di errore

Messaggio da UbuNuovo »

Interessante, credevo che bastasse fare test1 || exit | test2 || exit |... invece non funziona!
Ho trovato qua: https://stackoverflow.com/questions/155 ... shell-pipe

In bash you can use set -e and set -o pipefail at the beginning of your file.
A subsequent command ./a | ./b | ./c will fail when any of the three scripts fails.
The return code will be the return code of the first failed script.

Note that pipefail isn't available in standard sh

--------------------------------------
You can also check the ${PIPESTATUS[]} array after the full execution

Then ${PIPESTATUS} will be an array of error codes from each command in the pipe, so if the middle command failed, echo ${PIPESTATUS[@]} would contain something like:
0 1 0
and something like this run after the command:

Codice: Seleziona tutto

test ${PIPESTATUS[0]} -eq 0 -a ${PIPESTATUS[1]} -eq 0 -a ${PIPESTATUS[2]} -eq 0
ho trovato anche questo esempio:

Codice: Seleziona tutto

COMMAND1 &&
 COMMAND2 | COMMAND3 | COMMAND4 &&
 ($PST=("${PIPESTATUS[@]}") && (exit ${PST[0]}) && (exit ${PST[1]})) &&
 COMMAND5
Non li ho ancora provati.
Salva l'Ucraina! 🇺🇦
Avatar utente
london3
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 471
Iscrizione: domenica 19 gennaio 2014, 16:22
Desktop: ubuntu:GNOME
Distribuzione: Ubuntu 22.04.1 LTS

Re: interrompere il piping in caso di errore

Messaggio da london3 »

Codice: Seleziona tutto

$ cat test 
set -e
test1 | test2 | test3

$ bash test 
test: riga 2: test1: comando non trovato
test: riga 2: test2: comando non trovato
test: riga 2: test3: comando non trovato
boh...
Computer: Lenovo ThinkPad L480
Avatar utente
london3
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 471
Iscrizione: domenica 19 gennaio 2014, 16:22
Desktop: ubuntu:GNOME
Distribuzione: Ubuntu 22.04.1 LTS

Re: interrompere il piping in caso di errore

Messaggio da london3 »

niente?
Computer: Lenovo ThinkPad L480
rai
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2516
Iscrizione: domenica 11 maggio 2008, 18:03
Desktop: plasma
Distribuzione: 20.04
Località: Palermo

Re: interrompere il piping in caso di errore

Messaggio da rai »

london3 ha scritto:
sabato 17 settembre 2022, 11:16

Codice: Seleziona tutto

$ cat test 
set -e
test1 | test2 | test3

$ bash test 
test: riga 2: test1: comando non trovato
test: riga 2: test2: comando non trovato
test: riga 2: test3: comando non trovato
boh...
Qui si capisce perché non può funzionare: per disegno il set -o errexit viene ignorato nelle pipe, nei test condizionali, e in una serie di altri casi
Avatar utente
london3
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 471
Iscrizione: domenica 19 gennaio 2014, 16:22
Desktop: ubuntu:GNOME
Distribuzione: Ubuntu 22.04.1 LTS

Re: interrompere il piping in caso di errore

Messaggio da london3 »

OK, allora, come suggerisce l'articolo è il caso di usare set ... con molta parsimonia, e cercare di "controllare" gli errori manualmente.
Computer: Lenovo ThinkPad L480
rai
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2516
Iscrizione: domenica 11 maggio 2008, 18:03
Desktop: plasma
Distribuzione: 20.04
Località: Palermo

Re: interrompere il piping in caso di errore

Messaggio da rai »

london3 ha scritto:
sabato 1 ottobre 2022, 10:43
OK, allora, come suggerisce l'articolo è il caso di usare set ... con molta parsimonia, e cercare di "controllare" gli errori manualmente.
Non sono abbastanza esperto da dare giudizi su questo aspetto. In sostanza io mi limito a non trattare bash come se fosse un linguaggio di alto livello che mi gestisce le eccezioni e cerco di ricordarmi dei suoi trabocchetti (in cui spesso finisco lo stesso :-D).
Non sono un fan di set -e però lo tengo in considerazione come strumento dato che parte dei problemi sollevati giustamente in quella wiki sono risolvibili, ma uno deve saperlo prima di cadere nel bug: per es. se so che per disegno COMANDO mi ritorna non-zero scriverò COMANDO || true;
oppure per una sezione di codice posso disattivare l'error checking da parte di bash con

Codice: Seleziona tutto

set +e
[blocco di codice in cui non voglio verifica degli errori]
set -e
Avatar utente
UbuNuovo
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 4347
Iscrizione: sabato 12 dicembre 2009, 20:58
Desktop: Mate
Distribuzione: Ubuntu Mate 22.04.1 LTS
Sesso: Maschile
Contatti:

Re: interrompere il piping in caso di errore

Messaggio da UbuNuovo »

Quello è pseudo codice, dovresti fare delle prove in uno script con codice vero, vanno inserite entrambe le opzioni.
Salva l'Ucraina! 🇺🇦
Avatar utente
london3
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 471
Iscrizione: domenica 19 gennaio 2014, 16:22
Desktop: ubuntu:GNOME
Distribuzione: Ubuntu 22.04.1 LTS

Re: interrompere il piping in caso di errore

Messaggio da london3 »

Vero anche questo.
Sempre meglio non fidarsi di tutto quello che si trova in giro.
Quanto detesto quelli che quando esponi un problema, ti mandano su google: spesso ti ritrovi con soluzioni contrastanti e anche dannose.
Computer: Lenovo ThinkPad L480
Avatar utente
london3
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 471
Iscrizione: domenica 19 gennaio 2014, 16:22
Desktop: ubuntu:GNOME
Distribuzione: Ubuntu 22.04.1 LTS

Re: interrompere il piping in caso di errore

Messaggio da london3 »

rai ha scritto:
sabato 1 ottobre 2022, 12:35

Codice: Seleziona tutto

set +e
[blocco di codice in cui non voglio verifica degli errori]
set -e
Si solito io faccio

Codice: Seleziona tutto

(
   set +e
   [blocco di codice in cui non voglio verifica degli errori]
)
Computer: Lenovo ThinkPad L480
Avatar utente
UbuNuovo
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 4347
Iscrizione: sabato 12 dicembre 2009, 20:58
Desktop: Mate
Distribuzione: Ubuntu Mate 22.04.1 LTS
Sesso: Maschile
Contatti:

Re: interrompere il piping in caso di errore

Messaggio da UbuNuovo »

Ho fatto due scrip, uno con e uno senza le due opzioni:

Codice: Seleziona tutto

8-) cat 1_normale.sh 
#!/bin/bash



echo '# 1 tutte vere'
test 1 -eq 1 && echo '1 ok' |& grep -q '1 ok' && echo '2 ok' |& grep -q '2 ok' && echo '3 ok 1'; echo -ne "uscita $?\n"
echo '# 2 terza falsa'
test 1 -eq 1 && echo '1 ok' |& grep -q '1 ok' && echo '2 ok' |& grep -q '2 ko' && echo '3 ok 2'; echo -ne "uscita $?\n"
echo '# 3 seconda falsa'
test 1 -eq 1 && echo '1 ok' |& grep -q '1 ko' && echo '2 ok' |& grep -q '2 ok' && echo '3 ok 3'; echo -ne "uscita $?\n"
echo '# 4 prima falsa'
test 1 -eq 1 && echo '1 ko' |& grep -q '1 ok' && echo '2 ok' |& grep -q '2 ok' && echo '3 ok 4'; echo -ne "uscita $?\n"

echo -e '\ncon exit\n'
echo '# 5 tutte vere'
test 1 -eq 1 && echo '1 ok' || exit 1 |& grep -q '1 ok' && echo '2 ok' || exit 2 |& grep -q '2 ok' && echo '3 ok 5' || exit 3; echo -ne "uscita $?\n"
echo '# 6 terza falsa'
test 1 -eq 1 && echo '1 ok' || exit 1 |& grep -q '1 ok' && echo '2 ok' || exit 2 |& grep -q '2 ko' && echo '3 ok 6' || exit 3; echo -ne "uscita $?\n"
echo '# 7 seconda falsa'
test 1 -eq 1 && echo '1 ok' || exit 1 |& grep -q '1 ko' && echo '2 ok' || exit 2 |& grep -q '2 ok' && echo '3 ok 7' || exit 3; echo -ne "uscita $?\n"
echo '# 8 prima falsa'
test 1 -eq 1 && echo '1 ko' || exit 1 |& grep -q '1 ok' && echo '2 ok' || exit 2 |& grep -q '2 ok' && echo '3 ok 8' || exit 3; echo -ne "uscita $?\n"

8-) 
e

Codice: Seleziona tutto

8-) cat 2_con_set_e_pipefail.sh 
#!/bin/bash
set -e
set -o pipefail

echo '# 1 tutte vere'
test 1 -eq 1 && echo '1 ok' |& grep -q '1 ok' && echo '2 ok' |& grep -q '2 ok' && echo '3 ok 1'; echo -ne "uscita $?\n"
echo '# 2 terza falsa'
test 1 -eq 1 && echo '1 ok' |& grep -q '1 ok' && echo '2 ok' |& grep -q '2 ko' && echo '3 ok 2'; echo -ne "uscita $?\n"
echo '# 3 seconda falsa'
test 1 -eq 1 && echo '1 ok' |& grep -q '1 ko' && echo '2 ok' |& grep -q '2 ok' && echo '3 ok 3'; echo -ne "uscita $?\n"
echo '# 4 prima falsa'
test 1 -eq 1 && echo '1 ko' |& grep -q '1 ok' && echo '2 ok' |& grep -q '2 ok' && echo '3 ok 4'; echo -ne "uscita $?\n"

echo -e '\ncon exit\n'
echo '# 5 tutte vere'
test 1 -eq 1 && echo '1 ok' || exit 1 |& grep -q '1 ok' && echo '2 ok' || exit 2 |& grep -q '2 ok' && echo '3 ok 5' || exit 3; echo -ne "uscita $?\n"
echo '# 6 terza falsa'
test 1 -eq 1 && echo '1 ok' || exit 1 |& grep -q '1 ok' && echo '2 ok' || exit 2 |& grep -q '2 ko' && echo '3 ok 6' || exit 3; echo -ne "uscita $?\n"
echo '# 7 seconda falsa'
test 1 -eq 1 && echo '1 ok' || exit 1 |& grep -q '1 ko' && echo '2 ok' || exit 2 |& grep -q '2 ok' && echo '3 ok 7' || exit 3; echo -ne "uscita $?\n"
echo '# 8 prima falsa'
test 1 -eq 1 && echo '1 ko' || exit 1 |& grep -q '1 ok' && echo '2 ok' || exit 2 |& grep -q '2 ok' && echo '3 ok 8' || exit 3; echo -ne "uscita $?\n"

8-) 
e un terzo che esegue i due script e salva i risultati

Codice: Seleziona tutto

8-) cat 0_esegue_scrip.sh 
#!/bin/bash
clear
echo -e '-------------------------------\nScript normale\n-------------------------------' > risultati.txt
./1_normale.sh >> risultati.txt

echo -e '-------------------------------\nScript con -e pipefail\n-------------------------------'>> risultati.txt
./2_con_set_e_pipefail.sh >> risultati.txt

cat risultati.txt
8-) 
invece del pipe normale ho usato |& che lascia passare lo stderror.
ottengo

Codice: Seleziona tutto

8-) cat risultati.txt 
-------------------------------
Script normale
-------------------------------
# 1 tutte vere
3 ok 1
uscita 0
# 2 terza falsa
uscita 1
# 3 seconda falsa
uscita 1
# 4 prima falsa
uscita 1

con exit

# 5 tutte vere
1 ok
2 ok
3 ok 5
uscita 0
# 6 terza falsa
1 ok
2 ok
3 ok 6
uscita 0
# 7 seconda falsa
1 ok
2 ok
3 ok 7
uscita 0
# 8 prima falsa
1 ko
2 ok
3 ok 8
uscita 0
-------------------------------
Script con -e pipefail
-------------------------------
# 1 tutte vere
3 ok 1
uscita 0
# 2 terza falsa
uscita 1
# 3 seconda falsa
uscita 1
# 4 prima falsa
uscita 1

con exit

# 5 tutte vere
1 ok
2 ok
3 ok 5
uscita 0
# 6 terza falsa
1 ok
2 ok
3 ok 6
uscita 0
# 7 seconda falsa
1 ok
2 ok
3 ok 7
uscita 0
# 8 prima falsa
1 ko
2 ok
3 ok 8
uscita 0
8-) 

guardando i risultati... mi si è ingrippato il cervello, forse ho messo troppa roba. :muro:
Se per te può essere utile... io lo riguardo tra qualche ora.
Salva l'Ucraina! 🇺🇦
rai
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2516
Iscrizione: domenica 11 maggio 2008, 18:03
Desktop: plasma
Distribuzione: 20.04
Località: Palermo

Re: interrompere il piping in caso di errore

Messaggio da rai »

guardando i risultati... mi si è ingrippato il cervello, forse ho messo troppa roba. :muro:
:hypno: per ora io passo, prometto che torno a leggere quando sarò più fresco e capisco di più
Avatar utente
crap0101
Rampante Reduce
Rampante Reduce
Messaggi: 8214
Iscrizione: martedì 30 ottobre 2007, 6:33
Desktop: LXDE
Distribuzione: Ubuntu 18.04.1 LTS
Sesso: Maschile
Località: TO
Contatti:

Re: interrompere il piping in caso di errore

Messaggio da crap0101 »

secondo me il problema è che si sta cercando di mettere insieme due cose differenti... un conto è l'exit status, un altro il piping dello stdout nello stdin di un altro programma. Verosimilmente quando il primo (o comunque il precedente) programma fallisce, il successivo è già in secuzione, per cui è già troppo tardi per interrompere qualsiasi csa. La soluzione (che però non mi sembra sia semplicemente implementabile con set o altri magheggi propri della bash) è eseguire un comando esplicitamente e attendere che termini per vedere lo stato d'uscita (e salvarsi da qualche parte i dati da passare al successivo comando), solo a quel punto si esegue il successivo e via dicendo. Certo, non è comodo come scrivere

Codice: Seleziona tutto

cmd1 | cmd2 | cmd3
ma al momento non vedo alternative.

@UbuNuovo pure io come @rai dovrei rivedere le tue prove in un altro momento :-D ....comunque qui ho scritto un esempio più minimale che dovrebbe comunque essere abbastanza chiarificatore sul perchè e il percome :-)

Codice: Seleziona tutto

crap0101@orange:/tmp$ fcat ./a.sh  ./b.sh  ./c.sh
@@@ ./a.sh
echo "a.sh"
sleep 1 || exit 1
@@@ ./b.sh
echo "b.sh"
sleep "err" || exit 11
@@@ ./c.sh
echo "c.sh"
sleep 2 || exit 111
crap0101@orange:/tmp$ ./a.sh | ./b.sh | ./c.sh
sleep: intervallo di tempo non valido: "err"
Try 'sleep --help' for more information.
c.sh
crap0101@orange:/tmp$ echo $?
0
crap0101@orange:/tmp$ cat t.sh
set -e -o pipefail
./a.sh | ./b.sh | ./c.sh
crap0101@orange:/tmp$ bash t.sh
c.sh
sleep: intervallo di tempo non valido: "err"
Try 'sleep --help' for more information.
crap0101@orange:/tmp$ echo $?
11
crap0101@orange:/tmp$ 

http://www.gnu.org/ http://boinc.berkeley.edu/ http://www.python-it.org/
- Ricorda le ultime parole di suo padre: «Sta' alla larga dalle chiese, figlio. La sola cosa per cui hanno la chiave è il merdaio. E giurami che non porterai mai un distintivo della legge» - W.S. Burroughs
Avatar utente
UbuNuovo
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 4347
Iscrizione: sabato 12 dicembre 2009, 20:58
Desktop: Mate
Distribuzione: Ubuntu Mate 22.04.1 LTS
Sesso: Maschile
Contatti:

Re: interrompere il piping in caso di errore

Messaggio da UbuNuovo »

Ciao Crap grazie, ci hai salvato dalla fusione cerebrale :D , i miei file d'esempio sono da dimenticare!

Per far vedere la differenza, ho fatto anche uno script uguale a t.sh ma senza le impostazioni '-e' e 'pipefail' che ho chiamato t_norm.sh
eseguendo i due script si nota che le opzioni aggiunte, fanno "uscire" l'exit status dell'errore.

Codice: Seleziona tutto

8-) bash ./t.sh 
c.sh
sleep: intervallo di tempo non valido: "err"
Try 'sleep --help' for more information.
8-) echo "$?"
11
8-) bash ./t_norm.sh 
c.sh
sleep: intervallo di tempo non valido: "err"
Try 'sleep --help' for more information.
8-) echo "$?"
0
notare gli 'exit status, 11 e 0
Riassumendo:
1) si ottiene lo stesso output sia con le impostazioni '-e' e 'pipefail', che senza.
2) usando le impostazioni '-e' e 'pipefail' si ottiene l'exit status di un eventuale primo errore interno ai pipe.
3) in ogni caso, tutti gli "elementi" del pipe vengono eseguiti.
4) per interrompere una sequenza di comandi in caso di errore, i comandi vanno eseguiti uno per volta.
Quelle opzioni possono essere utile per fermare uno script, in caso di errore interno al pipe, ma non possono fermarlo prima della fine del pipe.

P.s.:Rileggendo il quote del mio primo post... avevo capito male! Si parla solo di exit status :muro:
Salva l'Ucraina! 🇺🇦
Scrivi risposta

Ritorna a “Programmazione”

Chi c’è in linea

Visualizzano questa sezione: 0 utenti iscritti e 4 ospiti