
$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проигралиQui fortune programma condizionale senza exit(rand()).
Puoi spiegare? cosa c'è che non va qui?
Digressione lirico-storica
Conobbi per la prima volta questo Heisenbug un quarto di secolo fa. Quindi per il gateway in FaxNET è stato necessario creare diverse utilità tramite "giocare a dama" sotto FreeBSD. Come previsto, mi consideravo un programmatore avanzato e abbastanza esperto. Pertanto, intendevo fare tutto con la massima attenzione e attenzione possibile, prestando particolare attenzione alla gestione degli errori...
La mia precedente esperienza nella gestione dei bug in sendmail e uucp/uupc si è aggiunta alla mia diligenza nella "gestione approfondita degli errori". Non ha senso immergermi nei dettagli di quella storia, ma ho lottato con questo Heisenbug per due settimane per 10-14 ore. Pertanto, è stato ricordato, e ieri questa vecchia conoscenza è passata a trovarci di nuovo.
TL; DR Risposta
Utilità head может chiudere il canale da fortune сразу non appena legge la prima riga. Se fortune emette più di una riga, quindi la chiamata corrispondente write() restituirà un errore o segnalerà che vengono emessi meno byte di quelli richiesti. A sua volta, scritto con un'attenta gestione degli errori fortune ha il diritto di riflettere questa situazione nel suo stato di uscita. Quindi a causa dell'installazione set -o pipefail funzionerà || echo "Вы проиграли".
Tuttavia, head potrebbe non arrivare in tempo chiudere prima fortune finirà di emettere i dati. Allora funzionerà && echo "Повезло!".
In uno dei miei oggi c'è tale :
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'Tradotto in umano
È comune qui per и nel modo del compilatore utilizzando l'opzione --version chiede chi è e se l'opzione non è supportata viene inserito uno stub "Utilizzare un compilatore compatibile con GCC o CLANG".
come Lo puoi trovare ovunque. È apparso in questo posto molto tempo fa e ha funzionato perfettamente ovunque (LinuxSolaris, OSX, FreeBSD, eccetera.). Ma ieri dentro sulla piattaforma Ho notato:
#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"Francamente non ho riconosciuto subito la mia vecchia "conoscenza". Inoltre, il progetto è già stato testato molte volte su Elbrus e con molte distribuzioni diverse, inclusa Alt. Con vari compilatori, versioni di GNU Make e bash. Pertanto, non volevo vedere il mio errore qui.
Durante il tentativo di riprodurre il problema e/o capire cosa stava succedendo, iniziarono ad accadere cose più strane.
Incantesimo da riga di comando:
echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"Ogni tanto produceva testo extra, poi no... Spesso una delle opzioni rimaneva per un periodo piuttosto lungo, ma se si premeva più a lungo, si ottenevano sempre entrambe!
Ovviamente, il nostro tutto! E dopo aver digitato una tirata strace, ma non avendo il tempo di premere Invio, ho riconosciuto il mio vecchio amico Mr. Heisenbug e gli sviluppatori me stesso 25 anni fa, E ho deciso di essere triste e scrivere questo biglietto 😉
A proposito, come ogni che si rispetti sotto strace preferisce non riprodursi.
Allora cosa sta succedendo?
- Utilità
headha il diritto (o meglio, è addirittura obbligato) a chiudere il canale letto non appena legge il numero di righe richieste. - Il programma di scrittura dei dati (in questo caso
cc) может stampare più righe e gratuito farlo attraverso più chiamatewrite(). - se il lettore avrà il tempo di chiudere il canale dalla sua parte prima della fine della registrazione da parte dello scrittore, quindi lo scrittore riceverà un errore.
- Programma per scrittori ha il diritto entrambi ignorano l'errore di scrittura del canale e lo riflettono nel codice di completamento.
- A causa dell'installazione
set -o pipefailil codice di completamento della pipeline sarà diverso da zero (errato) se il risultato è diverso da zero da almeno un elemento, quindi funzionerà|| echo "Please use GCC or CLANG compatible compiler".
Potrebbero esserci delle variazioni a seconda di come il programma di scrittura funziona con i segnali. Ad esempio, il programma potrebbe terminare in modo anomalo (con generazione automatica di uno stato di terminazione diverso da zero/errore), oppure write() restituirà il risultato della scrittura di meno byte di quelli richiesti e impostati errno = EPIPE.
Di chi è la colpa?
Nel caso descritto un po' di tutto. Gestione degli errori in cc (lcc:1.23.20:Sep—4-2019:e2k-v3-linux) non è ridondante. In molti casi è meglio peccare per eccesso di cautela, anche se ciò mette in luce improvvisi difetti in un modello progettato per un comportamento tradizionale.
Cosa fare?
sbagliato:
fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"correggere:
(fortune && echo "Успешно" || echo "Ошибка") | head -1In questo caso, la chiusura anticipata di una pipe verrà gestita dall'interprete dei comandi durante la manutenzione della pipeline nidificata ("entro" le parentesi). Di conseguenza, se
fortuneriporterà un errore di scrittura su un canale chiuso nello stato, quindi sull'uscita|| echo "Ошибка"non arriverà da nessuna parte, poiché il canale è già chiuso.fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"Ecco l'utilità
catfunge da smorzatore perché ignora l'erroreEPIPEal momento del ritiro. Per ora basta questo conclusionefortunepiccolo (diverse righe) e si adatta al buffer del canale (da 512 byte a ≈64K, nella maggior parte dei sistemi operativi ≥4K). Altrimenti il problema potrebbe ripresentarsi.
Come elaborare correttamente EPIPE e altri errori di registrazione?
Non esiste un’unica soluzione giusta, ma ci sono semplici consigli:
EPIPErichiesto deve essere elaborato (e riflesso nello stato di uscita) durante l'emissione di dati che richiedono integrità. Ad esempio, durante il funzionamento di archiviatori o utilità di backup.EPIPEmeglio ignorare durante la visualizzazione di informazioni e messaggi ausiliari. Ad esempio, quando si visualizzano informazioni sulle opzioni--helpo--version.- Se il codice in fase di sviluppo può essere utilizzato prima in una pipeline
| headpoiEPIPEÈ meglio ignorare, altrimenti è meglio elaborare e riflettere nell'exit status.
Vorrei cogliere l’occasione per esprimere la mia gratitudine alle squadre и per un grande lavoro produttivo. La tua determinazione è sorprendente!
Continuate così, compagni, su !
Grazie per correggere errori di battitura ed errori.
KDPV da
Fonte: habr.com
