$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
Εδώ fortune
πρόγραμμα υπό όρους χωρίς exit(rand())
.
Μπορείς να εξηγήσεις? τι φταίει εδώ?
Λυρικοϊστορική παρέκβαση
Γνώρισα για πρώτη φορά αυτό το Heisenbug πριν από ένα τέταρτο του αιώνα. Στη συνέχεια, για την πύλη στο FaxNET ήταν απαραίτητο να δημιουργηθούν πολλά βοηθητικά προγράμματα μέσω
Η προηγούμενη εμπειρία μου στην αντιμετώπιση σφαλμάτων στο sendmail και στο uucp/uupc συνέβαλε στην επιμέλειά μου στον «ενδελεχή χειρισμό σφαλμάτων». Δεν έχει νόημα να βουτήξω στις λεπτομέρειες αυτής της ιστορίας, αλλά πάλεψα με αυτό το Heisenbug για δύο εβδομάδες για 10-14 ώρες. Ως εκ τούτου, το θυμήθηκαν, και χθες αυτός ο παλιός γνώριμος σταμάτησε να επισκεφθεί ξανά.
TL;DR Απάντηση
Χρησιμότητα head
κουτί κλείστε το κανάλι από fortune
сразу μόλις διαβάσει την πρώτη γραμμή. Αν fortune
εξάγει περισσότερες από μία γραμμές και μετά την αντίστοιχη κλήση write()
θα επιστρέψει ένα σφάλμα ή θα αναφέρει ότι εξάγονται λιγότερα byte από αυτά που ζητήθηκαν. Με τη σειρά του, γραμμένο με προσεκτικό χειρισμό σφαλμάτων fortune
έχει το δικαίωμα να αντικατοπτρίζει αυτή την κατάσταση στο καθεστώς εξόδου του. Μετά λόγω εγκατάστασης set -o pipefail
θα δουλέψω || echo "Вы проиграли"
.
Ωστόσο, head
μπορεί να μην τα καταφέρει εγκαίρως κλείστε πριν fortune
θα ολοκληρώσει την έξοδο δεδομένων. Τότε θα λειτουργήσει && echo "Повезло!"
.
Σε ένα από τα σημερινά μου GNUMakefile
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'
Μεταφρασμένο σε ανθρώπινο
Είναι συνηθισμένο εδώ για --version
ρωτά ποιος είναι και εάν η επιλογή δεν υποστηρίζεται, τότε εισάγεται ένα στέλεχος "Παρακαλώ χρησιμοποιήστε μεταγλωττιστή συμβατό με GCC ή CLANG".
σαν
#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"
Ειλικρινά, δεν αναγνώρισα αμέσως τον παλιό μου «γνωστό». Επιπλέον, το έργο έχει ήδη δοκιμαστεί πολλές φορές στο Elbrus και σε πολλές διαφορετικές διανομές, συμπεριλαμβανομένου του Alt. Με διάφορους μεταγλωττιστές, εκδόσεις του GNU Make και bash. Επομένως, δεν ήθελα να δω το λάθος μου εδώ.
Όταν προσπαθούσαμε να αναπαράγω το πρόβλημα ή/και να καταλάβω τι συνέβαινε, άρχισαν να συμβαίνουν πιο περίεργα πράγματα.
Ξόρκι γραμμής εντολών:
echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"
Κάθε τόσο θα έβγαζε επιπλέον κείμενο, μετά όχι... Συχνά μια από τις επιλογές θα κολλούσε για αρκετή ώρα, αλλά αν τρυπούσες περισσότερο, έπαιρνες πάντα και τα δύο!
Φυσικά, strace
Παρεμπιπτόντως, όπως κάθε άλλος που σέβεται τον εαυτό του strace
προτιμά να μην αναπαράγει.
Λοιπόν τι συμβαίνει?
- Χρησιμότητα
head
έχει το δικαίωμα (ή μάλλον, αναγκάζεται) να κλείσει το κανάλι που διαβάζεται μόλις διαβάσει τον αριθμό των γραμμών που ζητήθηκε. - Το πρόγραμμα-εγγραφής που δημιουργεί δεδομένα (σε αυτή την περίπτωση
cc
) κουτί εκτύπωση πολλαπλών γραμμών και Ελεύθερος το κάνετε αυτό μέσω πολλαπλών κλήσεωνwrite()
. - αν ο αναγνώστης θα έχει χρόνο να κλείσει το κανάλι από την πλευρά του πριν από το τέλος της ηχογράφησης από την πλευρά του συγγραφέα, τότε ο συγγραφέας θα λάβει ένα σφάλμα.
- Πρόγραμμα συγγραφέα με τίτλο Και οι δύο αγνοούν το σφάλμα εγγραφής καναλιού και το αντικατοπτρίζουν στον κωδικό ολοκλήρωσής σας.
- Λόγω εγκατάστασης
set -o pipefail
ο κωδικός ολοκλήρωσης του αγωγού θα είναι μη μηδενικός (λανθασμένος) εάν το αποτέλεσμα είναι μη μηδενικό από τουλάχιστον ένα στοιχείο και, στη συνέχεια, θα λειτουργήσει|| echo "Please use GCC or CLANG compatible compiler"
.
Μπορεί να υπάρχουν παραλλαγές ανάλογα με τον τρόπο λειτουργίας του προγράμματος εγγραφής με τα σήματα. Για παράδειγμα, το πρόγραμμα μπορεί να τερματιστεί ασυνήθιστα (με αυτόματη δημιουργία κατάστασης τερματισμού μη μηδενικού/λάθους) ή write()
θα επιστρέψει το αποτέλεσμα της εγγραφής λιγότερων byte από αυτά που ζητήθηκαν και ορίστηκαν errno = EPIPE
.
Ποιος φταίει;
Στην περιγραφόμενη περίπτωση λίγο από όλα. Σφάλμα χειρισμού cc
(lcc:1.23.20:Sep—4-2019:e2k-v3-linux) δεν είναι περιττός. Σε πολλές περιπτώσεις, είναι προτιμότερο να είστε προσεκτικοί, αν και αυτό αποκαλύπτει ξαφνικά ελαττώματα σε μια πλάκα λέβητα που έχει σχεδιαστεί για παραδοσιακή συμπεριφορά.
Τι να κάνω;
Λανθασμένος:
fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"
Σωστά:
-
(fortune && echo "Успешно" || echo "Ошибка") | head -1
Εδώ, το πρόωρο κλείσιμο ενός σωλήνα θα διεκπεραιωθεί από τον διερμηνέα εντολών κατά τη συντήρηση του ένθετου αγωγού ("εντός" των παρενθέσεων). Αντίστοιχα, εάν
fortune
θα αναφέρει ένα σφάλμα εγγράφως σε ένα κλειστό κανάλι στην κατάσταση και μετά στην έξοδο|| echo "Ошибка"
δεν θα φτάσει πουθενά, αφού το κανάλι είναι ήδη κλειστό. -
fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"
Εδώ είναι το βοηθητικό πρόγραμμα
cat
λειτουργεί ως αποσβεστήρας επειδή αγνοεί το σφάλμαEPIPE
κατά την απόσυρση. Αυτό αρκεί για τώρα το συμπέρασμαfortune
μικρό (πολλές γραμμές) και χωράει στην προσωρινή μνήμη καναλιού (από 512 byte έως ≈64K, στα περισσότερα λειτουργικά συστήματα ≥4K). Διαφορετικά το πρόβλημα μπορεί να επανέλθει.
Πώς να επεξεργαστείτε σωστά EPIPE
και άλλα σφάλματα εγγραφής;
Δεν υπάρχει ενιαία σωστή λύση, αλλά υπάρχουν απλές συστάσεις:
EPIPE
απαιτείται πρέπει να υποβληθεί σε επεξεργασία (και αντικατοπτρίζεται στην κατάσταση εξόδου) κατά την έξοδο δεδομένων που απαιτούν ακεραιότητα. Για παράδειγμα, κατά τη λειτουργία αρχειοθέτησης ή βοηθητικών προγραμμάτων δημιουργίας αντιγράφων ασφαλείας.EPIPE
καλύτερα να αγνοήσετε κατά την εμφάνιση πληροφοριών και βοηθητικών μηνυμάτων. Για παράδειγμα, όταν εμφανίζονται πληροφορίες για επιλογές--help
ή--version
.- Εάν ο κώδικας που αναπτύσσεται μπορεί να χρησιμοποιηθεί σε μια διοχέτευση πριν
| head
, Στη συνέχειαEPIPE
Είναι καλύτερα να αγνοήσετε, διαφορετικά είναι καλύτερο να επεξεργαστείτε και να αναλογιστείτε στην κατάσταση εξόδου.
Με αυτή την ευκαιρία θα ήθελα να εκφράσω τις ευχαριστίες μου στις ομάδες
Συνεχίστε έτσι Καμαράδες, επάνω
σας ευχαριστώ
KDPV από
Πηγή: www.habr.com