Τα stdin, stdout και stderr είναι τρεις ροές δεδομένων που δημιουργούνται όταν εκκινείτε μια εντολή Linux. Μπορείτε να τα χρησιμοποιήσετε για να διαπιστώσετε εάν τα σενάρια σας διοχετεύονται ή ανακατευθύνονται. Σας δείχνουμε πώς.
Πίνακας περιεχομένων
Οι ροές ενώνουν δύο σημεία
Μόλις αρχίσετε να μαθαίνετε για λειτουργικά συστήματα Linux και Unix, θα συναντήσετε τους όρους stdin, stdout και stederr. Αυτά είναι τρία τυπικά ρεύματα που καθορίζονται όταν εκτελείται μια εντολή Linux. Στην πληροφορική, μια ροή είναι κάτι που μπορεί να μεταφέρει δεδομένα. Στην περίπτωση αυτών των ροών, αυτά τα δεδομένα είναι κείμενο.
Οι ροές δεδομένων, όπως και οι ροές νερού, έχουν δύο άκρα. Έχουν μια πηγή και μια εκροή. Όποια εντολή Linux χρησιμοποιείτε παρέχει ένα άκρο κάθε ροής. Το άλλο άκρο καθορίζεται από το κέλυφος που εκτόξευσε την εντολή. Αυτό το άκρο θα συνδεθεί στο παράθυρο τερματικού, θα συνδεθεί σε έναν σωλήνα ή θα ανακατευθυνθεί σε ένα αρχείο ή άλλη εντολή, σύμφωνα με τη γραμμή εντολών που εκκίνησε την εντολή.
Οι τυπικές ροές Linux
Στο Linux, το stdin είναι η τυπική ροή εισόδου. Αυτό δέχεται κείμενο ως είσοδο. Η έξοδος κειμένου από την εντολή στο κέλυφος παραδίδεται μέσω της ροής stdout (standard out). Τα μηνύματα σφάλματος από την εντολή αποστέλλονται μέσω της ροής stderr (τυπικό σφάλμα).
Έτσι, μπορείτε να δείτε ότι υπάρχουν δύο ροές εξόδου, η stdout και η stderr, και μία ροή εισόδου, η stdin. Επειδή τα μηνύματα σφάλματος και η κανονική έξοδος έχουν το καθένα τον δικό του αγωγό για να τα μεταφέρει στο παράθυρο του τερματικού, μπορούν να αντιμετωπιστούν ανεξάρτητα το ένα από το άλλο.
Ο χειρισμός των ροών γίνεται σαν αρχεία
Οι ροές στο Linux—όπως σχεδόν όλα τα άλλα— αντιμετωπίζονται σαν να ήταν αρχεία. Μπορείτε να διαβάσετε κείμενο από ένα αρχείο και μπορείτε να γράψετε κείμενο σε ένα αρχείο. Και οι δύο αυτές ενέργειες περιλαμβάνουν μια ροή δεδομένων. Επομένως, η έννοια του χειρισμού μιας ροής δεδομένων ως αρχείου δεν είναι τόσο περίπλοκη.
Σε κάθε αρχείο που σχετίζεται με μια διεργασία εκχωρείται ένας μοναδικός αριθμός για την αναγνώρισή της. Αυτό είναι γνωστό ως περιγραφέας αρχείου. Κάθε φορά που απαιτείται να εκτελεστεί μια ενέργεια σε ένα αρχείο, ο περιγραφέας αρχείου χρησιμοποιείται για την αναγνώριση του αρχείου.
Αυτές οι τιμές χρησιμοποιούνται πάντα για τα stdin, stdout και stderr:
0: stdin
1: stdout
2: stderr
Αντιδρώντας σε σωλήνες και ανακατευθύνσεις
Για να διευκολυνθεί η εισαγωγή κάποιου σε ένα θέμα, μια κοινή τεχνική είναι η διδασκαλία μιας απλοποιημένης έκδοσης του θέματος. Για παράδειγμα, με τη γραμματική, μας λένε ότι ο κανόνας είναι «I πριν το E, εκτός από το C». Αλλά στην πραγματικότητα, εκεί αποτελούν περισσότερες εξαιρέσεις σε αυτόν τον κανόνα παρά υπάρχουν περιπτώσεις που το υπακούουν.
Με παρόμοιο τρόπο, όταν μιλάμε για stdin, stdout και stderr, είναι βολικό να ξεκαθαρίσουμε το αποδεκτό αξίωμα ότι μια διαδικασία ούτε γνωρίζει ούτε ενδιαφέρεται πού τερματίζονται οι τρεις τυπικές ροές της. Πρέπει μια διεργασία να ενδιαφέρεται αν η έξοδος της πηγαίνει στο τερματικό ή θα ανακατευθύνεται σε ένα αρχείο; Μπορεί να πει αν η είσοδος του προέρχεται από το πληκτρολόγιο ή αν διοχετεύεται σε αυτό από άλλη διαδικασία;
Στην πραγματικότητα, μια διεργασία γνωρίζει —ή τουλάχιστον μπορεί να ανακαλύψει, εάν επιλέξει να ελέγξει— και μπορεί να αλλάξει τη συμπεριφορά της ανάλογα εάν ο συγγραφέας του λογισμικού αποφασίσει να προσθέσει αυτήν τη λειτουργικότητα.
Μπορούμε να δούμε αυτή την αλλαγή στη συμπεριφορά πολύ εύκολα. Δοκιμάστε αυτές τις δύο εντολές:
ls
ls | cat
Η εντολή ls συμπεριφέρεται διαφορετικά εάν η έξοδος της (stdout) διοχετεύεται σε άλλη εντολή. Είναι το ls που αλλάζει σε έξοδο μίας στήλης, δεν είναι μια μετατροπή που εκτελείται από τη γάτα. Και το ls κάνει το ίδιο αν η έξοδος του ανακατευθύνεται:
ls > capture.txt
Ανακατεύθυνση stdout και stderr
Υπάρχει ένα πλεονέκτημα να έχετε μηνύματα σφάλματος που παραδίδονται από μια αποκλειστική ροή. Σημαίνει ότι μπορούμε να ανακατευθύνουμε την έξοδο μιας εντολής (stdout) σε ένα αρχείο και να συνεχίσουμε να βλέπουμε τυχόν μηνύματα σφάλματος (stderr) στο παράθυρο του τερματικού. Μπορείτε να αντιδράσετε στα σφάλματα εάν χρειάζεται, καθώς συμβαίνουν. Σταματά επίσης τα μηνύματα σφάλματος να μολύνουν το αρχείο στο οποίο έχει ανακατευθυνθεί το stdout.
Πληκτρολογήστε το ακόλουθο κείμενο σε ένα πρόγραμμα επεξεργασίας και αποθηκεύστε το σε ένα αρχείο που ονομάζεται error.sh.
#!/bin/bash echo "About to try to access a file that doesn't exist" cat bad-filename.txt
Κάντε το σενάριο εκτελέσιμο με αυτήν την εντολή:
chmod +x error.sh
Η πρώτη γραμμή του σεναρίου αντηχεί κείμενο στο παράθυρο του τερματικού, μέσω της ροής stdout. Η δεύτερη γραμμή προσπαθεί να αποκτήσει πρόσβαση σε ένα αρχείο που δεν υπάρχει. Αυτό θα δημιουργήσει ένα μήνυμα σφάλματος που παραδίδεται μέσω του stderr.
Εκτελέστε το σενάριο με αυτήν την εντολή:
./error.sh
Μπορούμε να δούμε ότι και οι δύο ροές εξόδου, stdout και stderr, έχουν εμφανιστεί στα παράθυρα του τερματικού.
Ας προσπαθήσουμε να ανακατευθύνουμε την έξοδο σε ένα αρχείο:
./error.sh > capture.txt
Η έξοδος από το stdin ανακατευθύνθηκε στο αρχείο όπως αναμενόταν.
Το σύμβολο ανακατεύθυνσης > λειτουργεί με το stdout από προεπιλογή. Μπορείτε να χρησιμοποιήσετε έναν από τους περιγραφείς αριθμητικών αρχείων για να υποδείξετε ποια τυπική ροή εξόδου θέλετε να ανακατευθύνετε.
Για να ανακατευθύνετε ρητά το stdout, χρησιμοποιήστε αυτήν την οδηγία ανακατεύθυνσης:
1>
Για να ανακατευθύνετε ρητά το stderr, χρησιμοποιήστε αυτήν την οδηγία ανακατεύθυνσης:
2>
Ας δοκιμάσουμε ξανά τη δοκιμή μας και αυτή τη φορά θα χρησιμοποιήσουμε 2>:
./error.sh 2> capture.txt
Το μήνυμα stderr βρίσκεται στο capture.txt όπως αναμενόταν.
Ανακατεύθυνση τόσο του stdout όσο και του stderr
Σίγουρα, εάν μπορούμε να ανακατευθύνουμε είτε το stdout είτε το stderr σε ένα αρχείο ανεξάρτητα το ένα από το άλλο, θα έπρεπε να μπορούμε να τα ανακατευθύνουμε και τα δύο ταυτόχρονα, σε δύο διαφορετικά αρχεία;
Ναι μπορούμε. Αυτή η εντολή θα κατευθύνει το stdout σε ένα αρχείο που ονομάζεται capture.txt και το stderr σε ένα αρχείο που ονομάζεται error.txt.
./error.sh 1> capture.txt 2> error.txt
Ας ελέγξουμε τα περιεχόμενα κάθε αρχείου:
cat capture.txt
cat error.txt
Ανακατεύθυνση stdout και stderr στο ίδιο αρχείο
Αυτό είναι καθαρό, έχουμε καθεμία από τις τυπικές ροές εξόδου που πηγαίνει στο δικό της αποκλειστικό αρχείο. Ο μόνος άλλος συνδυασμός που μπορούμε να κάνουμε είναι να στείλουμε και το stdout και το stderr στο ίδιο αρχείο.
Μπορούμε να το πετύχουμε με την ακόλουθη εντολή:
./error.sh > capture.txt 2>&1
Ας το αναλύσουμε.
./error.sh: Εκκινεί το αρχείο σεναρίου error.sh.
> capture.txt: Ανακατευθύνει τη ροή stdout στο αρχείο capture.txt. > είναι συντομογραφία για το 1>.
2>&1: Χρησιμοποιεί την εντολή ανακατεύθυνσης &>. Αυτή η οδηγία σάς επιτρέπει να πείτε στο κέλυφος να κάνει μια ροή να φτάσει στον ίδιο προορισμό με μια άλλη ροή. Σε αυτήν την περίπτωση, λέμε “ανακατεύθυνση ροής 2, stderr, στον ίδιο προορισμό στον οποίο ανακατευθύνεται η ροή 1, stdout.”
Ας ελέγξουμε το αρχείο capture.txt και ας δούμε τι περιέχει.
cat capture.txt
Και οι δύο ροές stdout και stderr έχουν ανακατευθυνθεί σε ένα μόνο αρχείο προορισμού.
Για να ανακατευθυνθεί η έξοδος μιας ροής και να απορριφθεί σιωπηλά, κατευθύνετε την έξοδο στο /dev/null.
Ανίχνευση ανακατεύθυνσης μέσα σε ένα σενάριο
Συζητήσαμε πώς μια εντολή μπορεί να ανιχνεύσει εάν κάποια από τις ροές ανακατευθύνεται και μπορεί να επιλέξει να αλλάξει τη συμπεριφορά της ανάλογα. Μπορούμε να το πετύχουμε αυτό στα δικά μας σενάρια; Ναι μπορούμε. Και είναι μια πολύ εύκολη τεχνική στην κατανόηση και την εφαρμογή.
Πληκτρολογήστε το παρακάτω κείμενο σε ένα πρόγραμμα επεξεργασίας και αποθηκεύστε το ως input.sh.
#!/bin/bash if [ -t 0 ]; then echo stdin coming from keyboard else echo stdin coming from a pipe or a file fi
Χρησιμοποιήστε την ακόλουθη εντολή για να το κάνετε εκτελέσιμο:
chmod +x input.sh
Το έξυπνο μέρος είναι το δοκιμή εντός των αγκύλων. Η επιλογή -t (τερματικό) επιστρέφει true (0) εάν το αρχείο σχετίζεται με τον περιγραφέα αρχείου τερματίζει στο παράθυρο τερματικού. Χρησιμοποιήσαμε τον περιγραφέα αρχείου 0 ως όρισμα στο τεστ, το οποίο αντιπροσωπεύει το stdin.
Εάν το stdin είναι συνδεδεμένο σε ένα παράθυρο τερματικού, η δοκιμή θα αποδειχθεί αληθής. Εάν το stdin είναι συνδεδεμένο σε αρχείο ή σωλήνα, η δοκιμή θα αποτύχει.
Μπορούμε να χρησιμοποιήσουμε οποιοδήποτε βολικό αρχείο κειμένου για να δημιουργήσουμε είσοδο στο σενάριο. Εδώ χρησιμοποιούμε ένα που ονομάζεται dummy.txt.
./input.shThe output shows that the script recognizes that the input isn’t coming from a keyboard, it is coming from a file. If you chose to, you could vary your script’s behavior accordingly.
That was with a file redirection, let’s try it with a pipe.
cat dummy.txt | ./input.shΤο σενάριο αναγνωρίζει ότι η εισαγωγή του διοχετεύεται σε αυτό. Ή ακριβέστερα, αναγνωρίζει για άλλη μια φορά ότι η ροή stdin δεν είναι συνδεδεμένη σε παράθυρο τερματικού.
Ας τρέξουμε το σενάριο χωρίς σωλήνες ούτε ανακατευθύνσεις.
./input.shΗ ροή stdin συνδέεται με το παράθυρο τερματικού και το σενάριο το αναφέρει αναλόγως.
Για να ελέγξουμε το ίδιο πράγμα με τη ροή εξόδου, χρειαζόμαστε ένα νέο σενάριο. Πληκτρολογήστε τα παρακάτω σε ένα πρόγραμμα επεξεργασίας και αποθηκεύστε το ως output.sh.
#!/bin/bash if [ -t 1 ]; then echo stdout is going to the terminal window else echo stdout is being redirected or piped fiΧρησιμοποιήστε την ακόλουθη εντολή για να το κάνετε εκτελέσιμο:
chmod +x input.shΗ μόνη σημαντική αλλαγή σε αυτό το σενάριο είναι στη δοκιμή στις αγκύλες. Χρησιμοποιούμε το ψηφίο 1 για να αντιπροσωπεύσουμε τον περιγραφέα αρχείου για το stdout.
Ας το δοκιμάσουμε. Θα διοχετεύσουμε την έξοδο μέσω της γάτας.
./output | catΤο σενάριο αναγνωρίζει ότι η έξοδός του δεν πηγαίνει απευθείας σε ένα παράθυρο τερματικού.
Μπορούμε επίσης να δοκιμάσουμε το σενάριο ανακατευθύνοντας την έξοδο σε ένα αρχείο.
./output.sh > capture.txtΔεν υπάρχει έξοδος στο παράθυρο του τερματικού, επιστρέφουμε σιωπηλά στη γραμμή εντολών. Όπως θα περιμέναμε.
Μπορούμε να κοιτάξουμε μέσα στο αρχείο capture.txt για να δούμε τι καταγράφηκε. Χρησιμοποιήστε την ακόλουθη εντολή για να το κάνετε.
cat capture.shΚαι πάλι, η απλή δοκιμή στο σενάριό μας ανιχνεύει ότι η ροή stdout δεν αποστέλλεται απευθείας σε ένα παράθυρο τερματικού.
Εάν εκτελέσουμε το σενάριο χωρίς αγωγούς ή ανακατευθύνσεις, θα πρέπει να ανιχνεύσει ότι το stdout παραδίδεται απευθείας στο παράθυρο του τερματικού.
./output.shΚαι αυτό ακριβώς βλέπουμε.
Ρεύματα Συνείδησης
Γνωρίζοντας πώς να καταλάβετε εάν τα σενάρια σας είναι συνδεδεμένα στο παράθυρο τερματικού ή σε σωλήνα ή ανακατευθύνονται, σας επιτρέπει να προσαρμόσετε τη συμπεριφορά τους ανάλογα.
Η καταγραφή και η διαγνωστική έξοδος μπορεί να είναι περισσότερο ή λιγότερο λεπτομερής, ανάλογα με το αν πηγαίνει στην οθόνη ή σε ένα αρχείο. Τα μηνύματα σφάλματος μπορούν να καταγραφούν σε διαφορετικό αρχείο από την κανονική έξοδο του προγράμματος.
Όπως συμβαίνει συνήθως, η περισσότερη γνώση φέρνει περισσότερες επιλογές.