Έχουμε δει πώς να γράφουμε απλές εντολές, όπου μια εντολή ακολουθείται από ορίσματα. Τώρα θα δούμε πώς να φτιάχνουμε πιο σύνθετες εντολές, συνθέτοντας απλές εντολές.
Πριν ξεκινήσουμε, μια γρήγορη εισαγωγή στην είσοδο/έξοδο.
Οι διεργασίες έχουν "τυποποιημένα κανάλια εισόδου/εξόδου".
Η εντολή tr είναι ένα πολύ καθαρό παράδειγμα αυτού.
Το μόνο που κάνει είναι να διαβάζει κείμενο από την είσοδό της, να εκτελεί μεταγραφές χαρακτήρων και να εκτυπώνει το κείμενο που προκύπτει στην έξοδό της.
Θα δούμε περισσότερα για τον χειρισμό των καναλιών "stdio" αργότερα στο πρόγραμμα σπουδών.
Αυτό είναι ένα από τα πιο δυνατά σημεία του προγραμματισμού στο κέλυφος. Οι σωληνώσεις σου επιτρέπουν να δημιουργείς περίπλοκους μετασχηματισμούς σε μια ροή κειμένου.
Για να παράγεις μια ταξινομημένη λίστα χρηστών:
cat /etc/passwd | cut -d : -f 1 | sort
Το σύμβολο του σωλήνα (|) συνδέει την έξοδο μιας εντολής με την είσοδο μιας άλλης.
Το cut διαβάζει την έξοδο του cat και το sort διαβάζει την έξοδο του cut.
Από προεπιλογή, κάθε εντολή σε μια σωλήνωση εκτελείται σε ξεχωριστό υποκέλυφος. (Ένα υποκέλυφος είναι μια θυγατρική διεργασία που αποτελεί αντίγραφο του κελύφους που εκτελείται εκείνη τη στιγμή.)
Υπάρχει ένα κόστος επιδόσεων όταν εκτελείς σωληνώσεις.
Αν βρεθείς με μακροσκελείς σωληνώσεις από παρόμοιες εντολές, σκέψου να τις συνδυάσεις σε μία εντολή.
Για παράδειγμα, οι σωληνώσεις που χρησιμοποιούν πολλαπλές εμφανίσεις των grep, cut, sed, awk και tr μπορούν συνήθως να συνδυαστούν σε μία εντολή awk για λόγους αποδοτικότητας.
Η κατάσταση εξόδου μιας σωλήνωσης είναι η κατάσταση εξόδου της τελευταίας εντολής της σωλήνωσης. Αυτό σημαίνει ότι αν κάποια προηγούμενη εντολή απέτυχε αλλά η τελευταία πέτυχε, η κατάσταση εξόδου ολόκληρης της σωλήνωσης είναι μηδέν.
Υπάρχει μια ρύθμιση του κελύφους που μπορεί να το αλλάξει αυτό·
αν η ρύθμιση "pipefail" είναι ενεργοποιημένη (με set -o pipefail), τότε η κατάσταση εξόδου της σωλήνωσης θα είναι:
Μια λίστα εντολών είναι μια ακολουθία σωληνώσεων που χωρίζονται με ; (ή αλλαγή γραμμής), && ή ||.
A; B είναι μια λίστα εντολών όπου το B εκτελείται αφού ολοκληρωθεί το A.A && B, όπου το B εκτελείται μόνο αν το A πετύχει (βγει με κατάσταση μηδέν).A || B, όπου το B εκτελείται μόνο αν το A αποτύχει (βγει με μη μηδενική κατάσταση).Η κατάσταση εξόδου μιας λίστας εντολών είναι η κατάσταση εξόδου της τελευταίας εντολής που εκτελέστηκε.
Οι τελεστές && και || μπορούν να αλυσοδεθούν, ώστε η επόμενη εντολή να εκτελεστεί υπό συνθήκη με βάση την κατάσταση των προηγούμενων εντολών.
Για παράδειγμα
A && B && C && D || E
Χρησιμοποίησε αυτούς τους λογικούς τελεστές με φειδώ. Μπορούν γρήγορα να οδηγήσουν σε δυσανάγνωστο κώδικα ή σε λογική που είναι δύσκολο να κατανοήσεις.
Για παράδειγμα, μπορεί να νομίζεις ότι αυτά είναι τα ίδια:
if A; then B; else C; fi
A && B || C
Η διαφορά είναι: πότε εκτελείται το C;
if), το C θα εκτελεστεί μόνο αν αποτύχει το A.Να μερικά παραδείγματα όπου οι λίστες εντολών μπορούν να απλοποιήσουν τον κώδικα.
Ας υποθέσουμε ότι έχεις ένα αρχείο δεδομένων που περιέχει δεδομένα για τρίγωνα, και ότι κάθε τρίγωνο παριστάνεται από τρεις ξεχωριστές γραμμές που περιέχουν τις πλευρές του τριγώνου.
$ cat triangle.dat
3
4
5
9
12
14
Μπορείς να χρησιμοποιήσεις έναν βρόχο while όπου η συνθήκη είναι τρεις ξεχωριστές εντολές read:
while read a && read b && read c; do
if is_pythagorean "$a" "$b" "$c"; then
echo "$a:$b:$c is pythagorean"
else
echo "$a:$b:$c is not pythagorean"
fi
done < triangle.dat
Αν υποθέσουμε ότι το is_pythagorean είναι μια εντολή που προσδιορίζει αν οι τρεις πλευρές ικανοποιούν την πυθαγόρεια εξίσωση, η έξοδος θα ήταν:
3:4:5 is pythagorean
9:12:14 is not pythagorean
Πολλές γλώσσες προγραμματισμού έχουν μια μορφή ισχυρισμού, όπου πετιέται μια εξαίρεση αν αποτύχει κάποια συνθήκη
assert(user == "Administrator", "wrong user");
Μπορούμε να χρησιμοποιήσουμε έναν τελεστή OR στη Bash για να προσομοιώσουμε αυτή τη συνάρτηση:
die () { # a little convenience function
echo "$*" >&2
exit 1
}
[[ $user == "Administrator" ]] || die "wrong user"
[[ $password == "secret!" ]] || die "incorrect password"
Οι μεγάλες λίστες εντολών γίνονται γρήγορα δυσανάγνωστες. Η άφθονη χρήση αλλαγών γραμμής μπορεί να βοηθήσει πολύ.
Δες αυτό το παράδειγμα, όπου μια λέξη προστίθεται σε έναν πίνακα αν πληρούνται δύο συνθήκες.
[[ "$word" != "$topic" ]] && [[ "$key" == "$(sorted "$topic")" ]] && anagrams+=("$candidate")
Η Bash σου επιτρέπει να προσθέσεις μια αλλαγή γραμμής μετά από έναν σωλήνα ή έναν λογικό τελεστή.
[[ "$word" != "$topic" ]] &&
[[ "$key" == "$(sorted "$topic")" ]] &&
anagrams+=("$candidate")
Ωστόσο, ο τελεστής μπορεί εύκολα να περάσει απαρατήρητος στο τέλος της γραμμής. Χρησιμοποιώντας μια συνέχεια γραμμής, μπορείς να βάλεις πρώτα τον τελεστή, κάτι που κάνει πιο φανερό ότι η λίστα συνεχίζεται:
[[ "$word" != "$topic" ]] \
&& [[ "$key" == "$(sorted "$topic")" ]] \
&& anagrams+=("$candidate")
Μια συνέχεια γραμμής είναι η ακολουθία δύο χαρακτήρων "ανάποδη κάθετος" και "αλλαγή γραμμής" (\ + \n).
Όταν η Bash βλέπει αυτή την ακολουθία, την αφαιρεί απλώς από τον κώδικα, συνεχίζοντας έτσι την τρέχουσα γραμμή με την επόμενη γραμμή.
Πρόσεξε να μην αφήσεις κενά ανάμεσα στην ανάποδη κάθετο και στην αλλαγή γραμμής.
Ας δούμε άλλο ένα παράδειγμα
printf "%s\n" "${numbers[@]}" | bc --mathlib | sort --general-numeric-sort
ή
printf "%s\n" "${numbers[@]}" \
| bc --mathlib \
| sort --general-numeric-sort