Τι είναι τα stdin, stdout και stderr στο Linux;

Τα 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». Αλλά στην πραγματικότητα, εκεί αποτελούν περισσότερες εξαιρέσεις σε αυτόν τον κανόνα παρά υπάρχουν περιπτώσεις που το υπακούουν.

  Πώς να κλωνοποιήσετε την εγκατάσταση Linux με το Clonezilla

Με παρόμοιο τρόπο, όταν μιλάμε για stdin, stdout και stderr, είναι βολικό να ξεκαθαρίσουμε το αποδεκτό αξίωμα ότι μια διαδικασία ούτε γνωρίζει ούτε ενδιαφέρεται πού τερματίζονται οι τρεις τυπικές ροές της. Πρέπει μια διεργασία να ενδιαφέρεται αν η έξοδος της πηγαίνει στο τερματικό ή θα ανακατευθύνεται σε ένα αρχείο; Μπορεί να πει αν η είσοδος του προέρχεται από το πληκτρολόγιο ή αν διοχετεύεται σε αυτό από άλλη διαδικασία;

Στην πραγματικότητα, μια διεργασία γνωρίζει —ή τουλάχιστον μπορεί να ανακαλύψει, εάν επιλέξει να ελέγξει— και μπορεί να αλλάξει τη συμπεριφορά της ανάλογα εάν ο συγγραφέας του λογισμικού αποφασίσει να προσθέσει αυτήν τη λειτουργικότητα.

Μπορούμε να δούμε αυτή την αλλαγή στη συμπεριφορά πολύ εύκολα. Δοκιμάστε αυτές τις δύο εντολές:

ls

ls | cat

Η εντολή ls συμπεριφέρεται διαφορετικά εάν η έξοδος της (stdout) διοχετεύεται σε άλλη εντολή. Είναι το ls που αλλάζει σε έξοδο μίας στήλης, δεν είναι μια μετατροπή που εκτελείται από τη γάτα. Και το ls κάνει το ίδιο αν η έξοδος του ανακατευθύνεται:

ls > capture.txt

ls > capture.txt σε παράθυρο τερματικού” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<pre>cat capture.txt</pre>
<p><img loading=

Ανακατεύθυνση 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

./error.sh > capture.txt σε παράθυρο τερματικού” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Το μήνυμα σφάλματος που παραδίδεται μέσω stderr εξακολουθεί να αποστέλλεται στο παράθυρο του τερματικού.  Μπορούμε να ελέγξουμε τα περιεχόμενα του αρχείου για να δούμε αν η έξοδος stdout πήγε στο αρχείο.</p>
<pre>cat capture.txt</pre>
<p><img loading=

Η έξοδος από το stdin ανακατευθύνθηκε στο αρχείο όπως αναμενόταν.

Το σύμβολο ανακατεύθυνσης > λειτουργεί με το stdout από προεπιλογή. Μπορείτε να χρησιμοποιήσετε έναν από τους περιγραφείς αριθμητικών αρχείων για να υποδείξετε ποια τυπική ροή εξόδου θέλετε να ανακατευθύνετε.

  Πώς να εγκαταστήσετε το 0 AD σε Linux

Για να ανακατευθύνετε ρητά το stdout, χρησιμοποιήστε αυτήν την οδηγία ανακατεύθυνσης:

1>

Για να ανακατευθύνετε ρητά το stderr, χρησιμοποιήστε αυτήν την οδηγία ανακατεύθυνσης:

2>

Ας δοκιμάσουμε ξανά τη δοκιμή μας και αυτή τη φορά θα χρησιμοποιήσουμε 2>:

./error.sh 2> capture.txt

./error.sh 2> capture.txt σε παράθυρο τερματικού” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Το μήνυμα σφάλματος ανακατευθύνεται και το μήνυμα echo stdout αποστέλλεται στο παράθυρο τερματικού:</p>
<p ><img class=

Το μήνυμα stderr βρίσκεται στο capture.txt όπως αναμενόταν.

Ανακατεύθυνση τόσο του stdout όσο και του stderr

Σίγουρα, εάν μπορούμε να ανακατευθύνουμε είτε το stdout είτε το stderr σε ένα αρχείο ανεξάρτητα το ένα από το άλλο, θα έπρεπε να μπορούμε να τα ανακατευθύνουμε και τα δύο ταυτόχρονα, σε δύο διαφορετικά αρχεία;

Ναι μπορούμε. Αυτή η εντολή θα κατευθύνει το stdout σε ένα αρχείο που ονομάζεται capture.txt και το stderr σε ένα αρχείο που ονομάζεται error.txt.

./error.sh 1> capture.txt 2> error.txt

./error.sh 1> capture.txt 2> error.txt σε παράθυρο τερματικού” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Επειδή και οι δύο ροές εξόδου – τυπική έξοδος και τυπικό σφάλμα – ανακατευθύνονται σε αρχεία, δεν υπάρχει ορατή έξοδος στο το παράθυρο του τερματικού.  Επιστρέφουμε στη γραμμή εντολών σαν να μην έχει συμβεί τίποτα.</p>
<p><img loading=

Ας ελέγξουμε τα περιεχόμενα κάθε αρχείου:

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.”

./error.sh > capture.txt 2&>1 σε παράθυρο τερματικού” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Δεν υπάρχει ορατή έξοδος.  Αυτό είναι ενθαρρυντικό.</p>
<p><img loading=

Ας ελέγξουμε το αρχείο 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.

  Πώς να παίξετε το Riftbreaker στο Linux

Εάν το stdin είναι συνδεδεμένο σε ένα παράθυρο τερματικού, η δοκιμή θα αποδειχθεί αληθής. Εάν το stdin είναι συνδεδεμένο σε αρχείο ή σωλήνα, η δοκιμή θα αποτύχει.

Μπορούμε να χρησιμοποιήσουμε οποιοδήποτε βολικό αρχείο κειμένου για να δημιουργήσουμε είσοδο στο σενάριο. Εδώ χρησιμοποιούμε ένα που ονομάζεται dummy.txt.

./input.sh 

The 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

Και αυτό ακριβώς βλέπουμε.

Ρεύματα Συνείδησης

Γνωρίζοντας πώς να καταλάβετε εάν τα σενάρια σας είναι συνδεδεμένα στο παράθυρο τερματικού ή σε σωλήνα ή ανακατευθύνονται, σας επιτρέπει να προσαρμόσετε τη συμπεριφορά τους ανάλογα.

Η καταγραφή και η διαγνωστική έξοδος μπορεί να είναι περισσότερο ή λιγότερο λεπτομερής, ανάλογα με το αν πηγαίνει στην οθόνη ή σε ένα αρχείο. Τα μηνύματα σφάλματος μπορούν να καταγραφούν σε διαφορετικό αρχείο από την κανονική έξοδο του προγράμματος.

Όπως συμβαίνει συνήθως, η περισσότερη γνώση φέρνει περισσότερες επιλογές.