3.3 Μάθημα 2
Πιστοποιητικό: |
Linux Essentials |
---|---|
Έκδοση: |
1.6 |
Θέμα: |
3 Η Δύναμη της Γραμμής Εντολών |
Σκοπός: |
3.3 Μετατροπή Εντολών σε Script |
Μάθημα: |
2 απο 2 |
Εισαγωγή
Στην τελευταία ενότητα, χρησιμοποιήσαμε αυτό το απλό παράδειγμα για να δείξουμε τη σεναριογραφή στο Bash:
#!/bin/bash # A simple script to greet a single user. if [ $# -eq 1 ] then username=$1 echo "Hello $username!" else echo "Please enter only one argument." fi echo "Number of arguments: $#."
-
Όλα τα scripts πρέπει να ξεκινούν με ένα shebang, το οποίο ορίζει το path προς τον interpreter.
-
Όλα τα scripts πρέπει να περιλαμβάνουν σχόλια για να περιγράφουν τη χρήση τους.
-
Το συγκεκριμένο script λειτουργεί με ένα όρισμα, το οποίο δίνεται στο script όταν αυτό καλείται.
-
Αυτό το script περιέχει μια δήλωση if, η οποία ελέγχει τις συνθήκες μιας ενσωματωμένης μεταβλητής
$#
. Αυτή η μεταβλητή ορίζεται στον αριθμό των ορισμάτων. -
Εάν ο αριθμός των ορισμάτων που διαβιβάζονται στο script ισούται με 1, τότε η τιμή του πρώτου ορίσματος μεταβιβάζεται σε μια νέα μεταβλητή που ονομάζεται
username
και το script ηχεί έναν χαιρετισμό στον χρήστη. Διαφορετικά, εμφανίζεται ένα μήνυμα σφάλματος. -
Τέλος, το script ηχεί τον αριθμό των ορισμάτων. Αυτό είναι χρήσιμο για τον εντοπισμό σφαλμάτων.
Αυτό είναι ένα χρήσιμο παράδειγμα για αν αρχίσουμε την εξήγηση μερικών από τις άλλες δυνατότητες της σεναριογραφής στο Bash.
Κωδικοί Εξόδου
Θα παρατηρήσετε ότι το σενάριό μας έχει δύο πιθανές καταστάσεις: είτε εκτυπώνει "Hello <user>!"
είτε εκτυπώνει ένα μήνυμα σφάλματος. Αυτό είναι απολύτως φυσιολογικό για πολλά από τα βασικά βοηθητικά προγράμματα μας. Σκεφτείτε την εντολή cat
, με την οποία αναμφίβολα εξοικειώνεστε πολύ.
Ας συγκρίνουμε μια επιτυχημένη χρήση της cat
με μια κατάσταση όπου αποτυγχάνει. Μια υπενθύμιση ότι το παραπάνω παράδειγμά μας είναι ένα script που ονομάζεται new_script.sh
.
$ cat -n new_script.sh 1 #!/bin/bash 2 3 # A simple script to greet a single user. 4 5 if [ $# -eq 1 ] 6 then 7 username=$1 8 9 echo "Hello $username!" 10 else 11 echo "Please enter only one argument." 12 fi 13 echo "Number of arguments: $#."
Αυτή η εντολή επιτυγχάνει και θα παρατηρήσετε ότι η σημαία -n
έχει επίσης εκτυπώσει αριθμούς γραμμής. Αυτά είναι πολύ χρήσιμα κατά τον εντοπισμό σφαλμάτων στα scripts, αλλά σημειώστε ότι δεν αποτελούν μέρος του script.
Τώρα θα ελέγξουμε την τιμή μιας νέας ενσωματωμένης μεταβλητής $?
. Προς το παρόν, απλώς παρατηρήστε την έξοδο:
$ echo $? 0
Τώρα ας εξετάσουμε μια κατάσταση όπου η cat
θα αποτύχει. Πρώτα θα δούμε ένα μήνυμα σφάλματος και μετά θα ελέγξουμε την τιμή του $?
.
$ cat -n dummyfile.sh cat: dummyfile.sh: No such file or directory $ echo $? 1
Η εξήγηση για αυτήν τη συμπεριφορά είναι η εξής: οποιαδήποτε εκτέλεση του βοηθητικού προγράμματος cat
θα επιστρέψει έναν κωδικό εξόδου. Ένας κωδικός εξόδου θα μας πει εάν η εντολή πέτυχε ή αν παρουσιάστηκε σφάλμα. Ένας κωδικός εξόδου μηδέν υποδεικνύει ότι η εντολή ολοκληρώθηκε με επιτυχία. Αυτό ισχύει σχεδόν για κάθε εντολή Linux με την οποία εργάζεστε. Οποιοσδήποτε άλλος κωδικός εξόδου θα υποδεικνύει κάποιου είδους σφάλμα. Ο κωδικός εξόδου της τελευταία εντολής προς εκτέλεση θα αποθηκευτεί στη μεταβλητή $?
.
Οι κωδικοί εξόδου συνήθως δεν βλέπονται από τους ανθρώπινους χρήστες, αλλά είναι πολύ χρήσιμοι όταν γράφετε scripts. Σκεφτείτε ένα script όπου ενδέχεται να αντιγράφουμε αρχεία σε μια απομακρυσμένη μονάδα δίσκου δικτύου. Υπάρχουν πολλοί τρόποι με τους οποίους η εργασία αντιγραφής μπορεί να απέτυχε: για παράδειγμα το τοπικό μας μηχάνημα μπορεί να μην είναι συνδεδεμένο στο δίκτυο ή η απομακρυσμένη μονάδα δίσκου να είναι πλήρης. Ελέγχοντας τον κωδικό εξόδου του βοηθητικού προγράμματος αντιγραφής, μπορούμε να ειδοποιήσουμε τον χρήστη για προβλήματα κατά την εκτέλεση του script.
Είναι πολύ καλή πρακτική η εφαρμογή κωδικών εξόδου, οπότε θα το κάνουμε τώρα. Έχουμε δύο paths στο σενάριό μας, μια επιτυχία και μια αποτυχία. Ας χρησιμοποιήσουμε το μηδέν για να δηλώσουμε την επιτυχία και το ένα για να υποδείξουμε την αποτυχία.
1 #!/bin/bash 2 3 # A simple script to greet a single user. 4 5 if [ $# -eq 1 ] 6 then 7 username=$1 8 9 echo "Hello $username!" 10 exit 0 11 else 12 echo "Please enter only one argument." 13 exit 1 14 fi 15 echo "Number of arguments: $#."
$ ./new_script.sh Carol Hello Carol! $ echo $? 0
Παρατηρήστε ότι η εντολή echo
στη γραμμή 15 αγνοήθηκε εντελώς. Η χρήση του exit
θα τερματίσει αμέσως το script, επομένως αυτή η γραμμή δεν εμφανίζεται ποτέ.
Χειρισμός Πολλών Ορισμάτων
Μέχρι στιγμής το σενάριό μας μπορεί να χειριστεί ένα μόνο όνομα χρήστη τη φορά. Οποιοσδήποτε αριθμός ορισμάτων εκτός από ένα θα προκαλέσει σφάλμα. Ας εξερευνήσουμε πώς μπορούμε να κάνουμε αυτό το script πιο ευέλικτο.
Το πρώτο ένστικτο ενός χρήστη μπορεί να είναι να χρησιμοποιήσει περισσότερες μεταβλητές θέσης όπως $2
, $3
και ούτω καθεξής. Δυστυχώς, δεν μπορούμε να προβλέψουμε τον αριθμό των ορισμάτων που μπορεί να επιλέξει να χρησιμοποιήσει ένας χρήστης. Για να λύσετε αυτό το ζήτημα, θα είναι χρήσιμο να εισαγάγετε περισσότερες ενσωματωμένες μεταβλητές.
Θα τροποποιήσουμε τη λογική του script μας. Η ύπαρξη μηδενικών ορισμάτων θα πρέπει να προκαλεί σφάλμα, αλλά οποιοσδήποτε άλλος αριθμός ορισμάτων θα πρέπει να είναι επιτυχής. Αυτό το νέο script θα ονομάζεται friendly2.sh
.
1 #!/bin/bash 2 3 # a friendly script to greet users 4 5 if [ $# -eq 0 ] 6 then 7 echo "Please enter at least one user to greet." 8 exit 1 9 else 10 echo "Hello $@!" 11 exit 0 12 fi
$ ./friendly2.sh Carol Dave Henry Hello Carol Dave Henry!
Υπάρχουν δύο ενσωματωμένες μεταβλητές που περιέχουν όλα τα ορίσματα που μεταβιβάζονται στο script: $@
και $*
. Ως επί το πλείστον, και οι δύο συμπεριφέρονται το ίδιο. Το Bash θα αναλύσει τα ορίσματα και θα διαχωρίσει κάθε όρισμα όταν συναντήσει ένα κενό διάστημα μεταξύ τους. Στην πραγματικότητα, τα περιεχόμενα του $@
μοιάζουν με αυτό:
|
|
|
|
|
|
Εάν είστε εξοικειωμένοι με άλλες γλώσσες προγραμματισμού, μπορεί να αναγνωρίσετε αυτόν τον τύπο μεταβλητής ως πίνακα [array]. Οι πίνακες στο Bash μπορούν να δημιουργηθούν απλά βάζοντας ένα κενό διάστημα μεταξύ στοιχείων όπως η μεταβλητή FILES
στο script arraytest
παρακάτω:
FILES="/usr/sbin/accept /usr/sbin/pwck/ usr/sbin/chroot"
Περιέχει μια λίστα με πολλά στοιχεία. Μέχρι στιγμής αυτό δεν είναι πολύ χρήσιμο, επειδή δεν έχουμε ακόμη εισαγάγει κανέναν τρόπο χειρισμού αυτών των αντικειμένων μεμονωμένα.
Βρόγχοι For
Ας αναφερθούμε στο παράδειγμα arraytest
που παρουσιάστηκε προηγουμένως. Αν θυμάστε, σε αυτό το παράδειγμα καθορίζουμε έναν δικό μας πίνακα που ονομάζεται FILES
. Αυτό που χρειαζόμαστε είναι ένας τρόπος για να "αποσυσκευάσουμε"
αυτήν τη μεταβλητή και να αποκτήσουμε πρόσβαση σε κάθε μεμονωμένη τιμή, τη μία μετά την άλλη. Για να γίνει αυτό, θα χρησιμοποιήσουμε μια δομή που ονομάζεται βρόγχος for [for loop], η οποία υπάρχει σε όλες τις γλώσσες προγραμματισμού. Υπάρχουν δύο μεταβλητές στις οποίες θα αναφερθούμε: η μία είναι το εύρος και η άλλη για τη μεμονωμένη τιμή στην οποία εργαζόμαστε αυτήν τη στιγμή. Αυτό είναι ολόκληρο το script:
#!/bin/bash FILES="/usr/sbin/accept /usr/sbin/pwck/ usr/sbin/chroot" for file in $FILES do ls -lh $file done
$ ./arraytest lrwxrwxrwx 1 root root 10 Apr 24 11:02 /usr/sbin/accept -> cupsaccept -rwxr-xr-x 1 root root 54K Mar 22 14:32 /usr/sbin/pwck -rwxr-xr-x 1 root root 43K Jan 14 07:17 /usr/sbin/chroot
Εάν ανατρέξετε ξανά στο παράδειγμα friendly2.sh
παραπάνω, μπορείτε να δείτε ότι εργαζόμαστε με ένα εύρος τιμών που περιέχονται σε μια μεμονωμένη μεταβλητή $@
. Για λόγους σαφήνειας, θα ονομάσουμε την τελευταία μεταβλητή username
. Το σενάριό μας τώρα μοιάζει με αυτό:
1 #!/bin/bash 2 3 # a friendly script to greet users 4 5 if [ $# -eq 0 ] 6 then 7 echo "Please enter at least one user to greet." 8 exit 1 9 else 10 for username in $@ 11 do 12 echo "Hello $username!" 13 done 14 exit 0 15 fi
Θυμηθείτε ότι η μεταβλητή που ορίζετε εδώ μπορεί να ονομαστεί όπως θέλετε και ότι όλες οι γραμμές μέσα στο do… done
θα εκτελούνται μία φορά για κάθε στοιχείο του πίνακα. Ας παρατηρήσουμε το αποτέλεσμα από το σενάριό μας:
$ ./friendly2.sh Carol Dave Henry Hello Carol! Hello Dave! Hello Henry!
Τώρα ας υποθέσουμε ότι θέλουμε να κάνουμε την έξοδο του script μας να φαίνεται λίγο πιο ανθρώπινη. Θέλουμε ο χαιρετισμός μας να είναι σε μία γραμμή.
1 #!/bin/bash 2 3 # a friendly script to greet users 4 5 if [ $# -eq 0 ] 6 then 7 echo "Please enter at least one user to greet." 8 exit 1 9 else 10 echo -n "Hello $1" 11 shift 12 for username in $@ 13 do 14 echo -n ", and $username" 15 done 16 echo "!" 17 exit 0 18 fi
Μερικές σημειώσεις:
-
Η χρήση του
-n
με τοecho
θα καταστείλει τη νέα γραμμή μετά την εκτύπωση. Αυτό σημαίνει ότι όλες οι ηχώ θα εκτυπωθούν στην ίδια γραμμή και η νέα γραμμή θα εκτυπωθεί μόνο μετά το!
στη γραμμή 16. -
Η εντολή
shift
θα αφαιρέσει το πρώτο στοιχείο του πίνακα μας, έτσι ώστε:
|
|
|
|
|
|
Γίνεται αυτό:
|
|
|
|
Ας παρατηρήσουμε την έξοδο:
$ ./friendly2.sh Carol Hello Carol! $ ./friendly2.sh Carol Dave Henry Hello Carol, and Dave, and Henry!
Χρήση Regular Expressions για την Εκτέλεση Ελέγχου Σφαλμάτων
Είναι πιθανό να θέλουμε να επαληθεύσουμε όλα τα ορίσματα που εισάγει ο χρήστης. Για παράδειγμα, ίσως θέλουμε να διασφαλίσουμε ότι όλα τα ονόματα που μεταβιβάζονται στο friendly2.sh
περιέχουν μόνο γράμματα και τυχόν ειδικοί χαρακτήρες ή αριθμοί θα προκαλέσουν σφάλμα. Για να εκτελέσουμε αυτόν τον έλεγχο σφάλματος, θα χρησιμοποιήσουμε το grep
.
Θυμηθείτε ότι μπορούμε να χρησιμοποιήσουμε regular expressions με το grep
.
$ echo Animal | grep "^[A-Za-z]*$" Animal $ echo $? 0
$ echo 4n1ml | grep "^[A-Za-z]*$" $ echo $? 1
Το ^
και το $
υποδεικνύουν την αρχή και το τέλος της γραμμής αντίστοιχα. Το [A-Za-z]
υποδηλώνει μια σειρά από γράμματα, κεφαλαία ή πεζά. Το *
είναι ένας ποσοτικοποιητής, και τροποποιεί το εύρος των γραμμάτων μας έτσι ώστε να ταιριάζουμε απο μηδέν έως πολλά γράμματα. Συνοπτικά, το grep
μας θα πετύχει εάν η είσοδος είναι μόνο γράμματα. Διαφορετικά θα αποτύχει.
Το επόμενο πράγμα που πρέπει να σημειωθεί είναι ότι το grep
επιστρέφει κωδικούς εξόδου με βάση το αν υπήρξε αντιστοιχία ή όχι. Μια επιτυχής αντιστοιχία επιστρέφει 0
και η μη αντιστοιχεία επιστρέφει 1
. Μπορούμε να το χρησιμοποιήσουμε αυτό για να ελέγξουμε τα ορίσματά μας μέσα στο σενάριό μας.
1 #!/bin/bash 2 3 # a friendly script to greet users 4 5 if [ $# -eq 0 ] 6 then 7 echo "Please enter at least one user to greet." 8 exit 1 9 else 10 for username in $@ 11 do 12 echo $username | grep "^[A-Za-z]*$" > /dev/null 13 if [ $? -eq 1 ] 14 then 15 echo "ERROR: Names must only contains letters." 16 exit 2 17 else 18 echo "Hello $username!" 19 fi 20 done 21 exit 0 22 fi
Στη γραμμή 12, ανακατευθύνουμε την τυπική έξοδο στο /dev/null
, που είναι ένας απλός τρόπος για να το καταστείλετε. Δεν θέλουμε να δούμε κανένα αποτέλεσμα από την εντολή grep
, θέλουμε μόνο να δοκιμάσουμε τον κωδικό εξόδου της, κάτι που συμβαίνει στη γραμμή 13. Σημειώστε επίσης ότι χρησιμοποιούμε έναν κωδικό εξόδου του 2
για να υποδείξουμε ένα μη έγκυρο όρισμα. Είναι γενικά καλή πρακτική η χρήση διαφορετικών κωδικών εξόδου για την ένδειξη διαφορετικών σφαλμάτων. Με αυτόν τον τρόπο, ένας έμπειρος χρήστης μπορεί να χρησιμοποιήσει αυτούς τους κωδικούς εξόδου για την αντιμετώπιση προβλημάτων.
$ ./friendly2.sh Carol Dave Henry Hello Carol! Hello Dave! Hello Henry! $ ./friendly2.sh 42 Carol Dave Henry ERROR: Names must only contains letters. $ echo $? 2
Καθοδηγούμενες Ασκήσεις
-
Διαβάστε τα περιεχόμενα του
script1.sh
παρακάτω:#!/bin/bash if [ $# -lt 1 ] then echo "This script requires at least 1 argument." exit 1 fi echo $1 | grep "^[A-Z]*$" > /dev/null if [ $? -ne 0 ] then echo "no cake for you!" exit 2 fi echo "here's your cake!" exit 0
Ποια είναι η έξοδος αυτών των εντολών;
-
./script1.sh
-
echo $?
-
./script1.sh cake
-
echo $?
-
./script1.sh CAKE
-
echo $?
-
-
Διαβάστε τα περιεχόμενα του αρχείου
script2.sh
:for filename in $1/*.txt do cp $filename $filename.bak done
Περιγράψτε τον σκοπό αυτού του script όπως τον καταλαβαίνετε.
Ασκήσεις Εξερεύνησης
-
Δημιουργήστε ένα script που θα παίρνει οποιονδήποτε αριθμό ορισμάτων από τον χρήστη και θα εκτυπώσει μόνο εκείνα τα ορίσματα που είναι αριθμοί μεγαλύτεροι από 10.
Σύνοψη
Σε αυτή την ενότητα, μάθατε:
-
Τι είναι οι κωδικοί εξόδου, τι σημαίνουν και πώς να τους εφαρμόσετε
-
Πώς να ελέγξετε τον κωδικό εξόδου μιας εντολής
-
Τι είναι οι βρόχοι
for
και πώς να τους χρησιμοποιήσετε με πίνακες -
Πώς να χρησιμοποιήσετε το
grep
, τις regular expressions και τους κωδικούς εξόδου για να ελέγξετε την είσοδο χρήστη σε scripts.
Εντολές που χρησιμοποιήθηκαν στις ασκήσεις:
shift
-
Αυτό θα αφαιρέσει το πρώτο στοιχείο ενός πίνακα.
Ειδικές Μεταβλητές:
$?
-
Περιέχει τον κωδικό εξόδου της τελευταίας εντολής που εκτελέστηκε.
$@
,$*
-
Περιέχουν όλα τα ορίσματα που μεταβιβάζονται στο script, ως πίνακα.
Απαντήσεις στις Καθοδηγούμενες Ασκήσεις
-
Διαβάστε τα περιεχόμενα του
script1.sh
παρακάτω:#!/bin/bash if [ $# -lt 1 ] then echo "This script requires at least 1 argument." exit 1 fi echo $1 | grep "^[A-Z]*$" > /dev/null if [ $? -ne 0 ] then echo "no cake for you!" exit 2 fi echo "here's your cake!" exit 0
Ποια είναι η έξοδος αυτών των εντολών;
-
Εντολή:
./script1.sh
Έξοδος:
This script requires at least 1 argument.
-
Εντολή:
echo $?
Έξοδος:
1
-
Εντολή:
./script1.sh cake
Έξοδος:
no cake for you!
-
Εντολή:
echo $?
Έξοδος:
2
-
Εντολή:
./script1.sh CAKE
Έξοδος:
here’s your cake!
-
Command:
echo $?
Έξοδος:
0
-
-
Διαβάστε τα περιεχόμενα του αρχείου
script2.sh
:for filename in $1/*.txt do cp $filename $filename.bak done
Περιγράψτε τον σκοπό αυτού του script όπως τον καταλαβαίνετε.
Αυτό το script θα δημιουργήσει αντίγραφα ασφαλείας όλων των αρχείων που τελειώνουν με
.txt
σε έναν υποκατάλογο που ορίζεται στο πρώτο όρισμα.
Απαντήσεις στις Ασκήσεις Εξερεύνησης
-
Δημιουργήστε ένα script που θα παίρνει οποιονδήποτε αριθμό ορισμάτων από τον χρήστη και θα εκτυπώσει μόνο εκείνα τα ορίσματα που είναι αριθμοί μεγαλύτεροι από 10.
#!/bin/bash for i in $@ do echo $i | grep "^[0-9]*$" > /dev/null if [ $? -eq 0 ] then if [ $i -gt 10 ] then echo -n "$i " fi fi done echo ""