Menu
ΠΡΟΗΓ. ΚΕΦ.   ΕΠΟΜΕΝΟ ΚΕΦ.
Menu and Table of Contents


Εισαγωγή στα σενάρια κελύφους

Κέλυφος ή Φλοιός (shell)

Το κέλυφος (shell) είναι το πρόγραμμα που διερμηνεύει (interpret) τις εντολές που εισάγονται από το πληκτρολόγιο. Εκτός από την ανάγνωση εντολών από το πληκτρολόγιο, οι εντολές είναι δυνατόν να τοποθετηθούν σε ένα αρχείο και να αναγνωστούν από αυτό. Ένα αρχείο που περιέχει εντολές κελύφους ονομάζεται σενάριο κελύφους (shell script). Ο όρος σενάριο (script) χρησιμοποιείται για όλες τις γλώσσες προγραμματισμού που είναι διερμηνευτές (interpreters), όπως για παράδειγμα: php script, perl script κτλ.

Το κέλυφος στην πραγματικότητα είναι ένα πρόγραμμα το οποίο από τη μια πλευρά διαβάζει τις εντολές του χρήστη και από την άλλη επικοινωνεί με τον πυρήνα του Λειτουργικού Συστήματος για να «στείλει» τις εντολές προς εκτέλεση.

Ιεράρχηση στα τμήματα του Unix.
Bourne Shell (sh) C-Shell Bourne Again Shell (bash)

Σε κάθε σύστημα UNIX υπάρχουν τουλάχιστον δύο διαθέσιμα κελύφη και αυτά συνήθως είναι το Bourne shell (sh) και το C-shell (csh). Ωστόσο, η γλώσσα και η σύνταξη που χρησιμοποιείται για την επικοινωνία με καθένα από αυτά είναι διαφορετική. Όπως βλέπουμε στη λίστα που ακολουθεί, υπάρχουν πολλά διαφορετικά κελύφη:

Κατά τη συγγραφή του παρόντος το περισσότερο διαδεδομένο κέλυφος είναι το Bourne again shell (bash). Το bash είναι βελτιωνένη έκδοση του sh και είναι πλέον ο εξ ορισμού φλοιός σε συστήματα linux. Μάλιστα σε αρκετές περιπτώσεις η βασική έκδοση Bourne Shell δεν υπάρχει καν, και η εντολή sh είναι σύνδεσμος στην εντολή bash.

OK Ανεξάρτητα από το κέλυφος που χρησιμοποιείται, όλα τα κελύφη έχουν ως στόχο να παρέχουν στο UNIX μια διεπαφή χρήστη (user interface).

Σχεδόν όλα τα κελύφη έχουν τις παρακάτω δυνατότητες-ιδιότητες:

Το πρώτο μου σενάριο κελύφους

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

Φτιάχνουμε, λοιπόν, το αρχείο file1 που περιέχει εντολές, όπως φαίνεται παρακάτω:

bash-2.05a$ cat file1
ls -l
whoami
date
bash-2.05a$ sh file1
total 24
-rw-r--r--    1 asidirop it           314 Jan 11  2003 cc
-rw-r--r--    1 asidirop it            18 Nov 20 15:22 file1
-rw-r--r--    1 asidirop it           183 Jan  8  2003 list
asidirop
Mon Nov 20 15:23:05 EET 2006
bash-2.05a$

Εκτελώντας την εντολή "sh file1" εκτελείται το sh (το κέλυφός μας) με όρισμα το αρχείο file1. Όταν θα δημιουργηθεί η διεργασία του sh, θα αναγνωρίσει ότι δόθηκε όρισμα, οπότε και δεν θα περιμένει να διαβάσει εντολές από την κανονική είσοδο, αλλά θα ανοίξει το αρχείο file1 για ανάγνωση και θα διαβάζει από εκεί γραμμή-γραμμή. Κάθε γραμμή αντιστοιχεί σε μια εντολή. Συνεπώς, μετά την ανάγνωση μιας γραμμής, το κέλυφος εκτελεί την εντολή που περιέχεται σε αυτήν. Έτσι, είναι το ίδιο με το να έγραφε ο χρήστης τις εντολές στο τερματικό. Αντί να διαβαστούν οι εντολές από το τερματικό, διαβάζονται από το αρχείο.

OK Όταν ένα αρχείο περιέχει text το οποίο είναι εντολές, τότε αυτό το αρχείο ονομάζεται σενάριο (script).
OK Όταν το σενάριο περιέχει εντολές για το κέλυφος (shell), ονομάζεται σενάριο κελύφους (shell script).

Μειονέκτημα στον παραπάνω τρόπο είναι ότι πρέπει να γνωρίζει ο χρήστης αν το σενάριο κελύφους είναι γραμμένο για το κέλυφος sh, ή για το κέλυφος bash, ή για το csh κτλ. Όπως αναφέραμε πριν, τα διάφορα κελύφη έχουν ασυμβατότητες μεταξύ τους. Συνεπώς, αν ο χρήστης προσπαθήσει να εκτελέσει ένα σενάριο που είναι γραμμένο για csh χρησιμοποιώντας το bash, το πιο πιθανό είναι να υπάρξει αποτυχία στην εκτέλεση.

Όπως αναφέραμε πριν, στο Unix τα σενάρια χρησιμοποιούνται συχνά (shell scripts, perl scripts, python scripts, awk scripts κ.α.). Επειδή, λοιπόν, δεν είναι δυνατό να γνωρίζει ο χρήστης για κάθε σενάριο που προσπαθεί να εκτελέσει τη γλώσσα προγραμματισμού για την οποία είναι γραμμένο, υπάρχει η εξής σύμβαση: Στην αρχή του σεναρίου μπαίνουν οι χαρακτήρες #! ακολουθούμενοι από την απόλυτη διαδρομή του αντίστοιχου διερμηνευτή. Έτσι, ένα σενάριο για bash θα πρέπει να ξεκινά με:

#!/bin/bash

Αντίστοιχα, ένα σενάριο για perl ξεκινά με #!/usr/bin/perl, ένα σενάριο για csh ξεκινά με #!/bin/csh κτλ.

Ο πυρήνας του Unix, όταν προσπαθεί να εκτελέσει ένα πρόγραμμα, αρχικά ελέγχει αν είναι binary και αντιστοιχεί στην αρχιτεκτονική του τρέχοντος επεξεργαστή. Εάν ναι, τότε ξεκινά τη διεργασία φορτώνοντας τον δυαδικό κώδικα (binary code) στη μνήμη. Εάν όχι, τότε ελέγχει εάν είναι αρχείο κειμένου (text file). Όλα τα σενάρια είναι αρχεία κειμένου. Εάν, λοιπόν, είναι αρχείο κειμένου, τότε ελέγχει τους δυο πρώτους χαρακτήρες του αρχείου. Εάν αυτοί οι δυο πρώτοι χαρακτήρες είναι οι #! τότε αντιλαμβάνεται ότι πρόκειται για σενάριο, και διαβάζει την υπόλοιπη πρώτη γραμμή για να βρει τον διερμηνευτή που πρέπει να εκτελέσει.

Έστω ένα αρχείο με όνομα file1 που περιέχει:

#!/path/command
Lala
Foo

Όταν προσπαθήσουμε να «εκτελέσουμε» το file1 με την εντολή:

./file1

τότε το UNIX θα αναγνωρίσει ότι δεν πρόκειται για αρχείο με δυαδικό κώδικα (binary file) αλλά για αρχείο κειμένου (text). Θα διαβάσει την πρώτη γραμμή και θα εκτελέσει την εντολή:

/path/command ./file1

Άρα, αν η πρώτη γραμμή του αρχείου file2 είναι #!/usr/bin/perl, τότε εκτελώντας το file2 ο πυρήνας εκτελεί την εντολή:

/usr/bin/perl  ./file2

Αν η πρώτη γραμμή του file3 είναι #!/bin/bash, τότε εκτελώντας το file3 ο πυρήνας εκτελεί την εντολή:

/bin/bash  ./file3

Με το παραπάνω, ξεκινά ουσιαστικά μια νέα διεργασία κελύφους με όρισμα το αρχείο file3. Συνεπώς, ξεκινά ένα κέλυφος το οποίο θα διαβάζει εντολές από το αρχείο file3.

Εκτέλεση Σεναρίων Script Execution

Εκτέλεση σεναρίων και άδειες χρήσης

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

OK Όταν δημιουργούμε σενάρια, πρέπει να τους δώσουμε την άδεια πρόσβασης “execute”. Η άδεια “execute”, όπως έχουμε αναφέρει στην , δεν τίθεται από μόνη της, όταν δημιουργούνται αρχεία, αλλά πρέπει να δοθεί εκ των υστέρων.

Αν το αρχείο δεν έχει άδεια “execute”, θα πάρουμε το μήνυμα “Permission denied”, όπως κάθε φορά που γίνεται προσπάθεια εκτέλεσης προγράμματος για το οποίο δεν υπάρχουν τα κατάλληλα δικαιώματα:

aetos_test_27_$ ./file1
./file1: Permission denied.
aetos_test_27_$ ls -l
total 24
-rw-r--r--    1 asidirop it           314 Jan 11  2003 cc
-rw-------    1 asidirop it            28 Nov 20 18:40 file1
-rw-r--r--    1 asidirop it           183 Jan  8  2003 list

Σε αντίθεση με τα προγράμματα τύπου binary, για τα οποία αρκεί το δικαίωμα “execute” για την εκτέλεσή τους χωρίς να χρειάζεται το δικαίωμα “read”, στην περίπτωση των σεναρίων υπάρχουν επιπλέον περιορισμοί.

Έστω, λοιπόν, στο προηγούμενο παράδειγμα έχουμε δώσει στο file1 την άδεια “execute” αλλά όχι την άδεια “read”. Όταν προσπαθήσουμε να εκτελέσουμε το file1, θα πάρουμε το διαφορετικό από την προηγούμενη περίπτωση μήνυμα “cannot open: Permission denied”. Αυτό που συνέβη είναι ότι το file1 πέρασε τον έλεγχο άδειας “execute”, ο πυρήνας έλεγξε τον τύπο του αρχείου και διάβασε την πρώτη γραμμή και μετά εκτέλεσε την εντολή "/bin/sh file1". Η εντολή εκτελείται κανονικά, ξεκινά το κέλυφος και (το κέλυφος) προσπαθεί να ανοίξει το αρχείο file1, για την ανάγνωση των εντολών. Επειδή, όμως δεν υπάρχει το δικαίωμα ανάγνωσης (read), το κέλυφος εμφανίζει το μήνυμα σφάλματος.

aetos_test_27_$ ./file1
/bin/sh: ./file1: cannot open: Permission denied
aetos_test_45_$ /bin/sh file1
/bin/sh: file1: cannot open: Permission denied
aetos_test_27_$ ls -l
total 24
-rw-r--r--    1 asidirop it           314 Jan 11  2003 cc
--wx------    1 asidirop it            28 Nov 20 18:48 file1
-rw-r--r--    1 asidirop it           183 Jan  8  2003 list
Script Debugging Debugging Αποσφαλμάτωση σεναρίων κελύφους Αποσφαλμάτωση

Αποσφαλμάτωση σεναρίων κελύφους

Όλα τα κελύφη παρέχουν την επιλογή –v, για να εμφανίζεται στην οθόνη κάθε γραμμή του σεναρίου, καθώς αυτό διαβάζεται και την επιλογή –x, για να εμφανίζονται οι εντολές, καθώς εκτελούνται. Οι επιλογές αυτές είναι πολύ χρήσιμες για τη συντακτική ανάλυση των εντολών. Μπορούμε να ενσωματώσουμε αυτές τις επιλογές στην πρώτη γραμμή του σεναρίου, όπως και κάθε άλλη επιλογή με τον ίδιο τρόπο.

Στο παρακάτω παράδειγμα προσθέτουμε στο σενάριο file2 το όρισμα -v, όπως φαίνεται παρακάτω. Κατά την εκτέλεση του αρχείου ο πυρήνας θα διαβάσει την πρώτη γραμμή και αυτό που θα εκτελέσει είναι /bin/sh -v file2. Παρακάτω βλέπουμε ποιο θα είναι το αποτέλεσμα χρησιμοποιώντας το -v. Το κέλυφος θα τυπώνει κάθε γραμμή του αρχείου πριν την εκτελέσει. Θα τυπώσει ακόμη και τις γραμμές που περιέχουν σχόλια και όχι εντολές.

bash-2.05a$ cat file2
#!/bin/sh -v
whoami # dixnei poios eimai
#Twra typonoyme tin hmeromhnia
date
bash-2.05a$ ./file2
#!/bin/sh -v
whoami # dixnei poios eimai
asidirop
#Twra typonoyme tin hmeromhnia
date
Mon Nov 20 19:16:30 EET 2006
bash-2.05a$

Αν αλλάξουμε το -v σε -x, τότε το κέλυφος δεν θα εμφανίζει όλες τις γραμμές αλλά μόνο τις εντολές που πρόκειται να εκτελεστούν με έναν σταυρό από μπροστά:

bash-2.05a$ cat file2
#!/bin/sh -x
whoami # dixnei poios eimai
#Twra typonoyme tin hmeromhnia
date
bash-2.05a$ ./file2
+ whoami
asidirop
+ date
Mon Nov 20 19:17:13 EET 2006
bash-2.05a$
PATH

Εκτέλεση σεναρίων και η μεταβλητή PATH

Για να εκτελέσουμε ένα πρόγραμμα ή σενάριο (script) πρέπει να αναφερθούμε στο όνομα αρχείου είτε με σχετική είτε με απόλυτη διαδρομή. Έτσι στο παρακάτω παράδειγμα για το file1 που βρίσκεται στον τρέχοντα φάκελο εργασίας (cwd) μπορούμε είτε να εκτελέσουμε ./file1, δηλαδή να χρησιμοποιήσουμε τη σχετική διαδρομή προς το αρχείο, είτε ~/test/file1, δηλαδή να χρησιμοποιήσουμε την απόλυτη διαδρομή προς το αρχείο.

aetos_test_51_$ cat file1
#!/bin/sh
whoami
date
aetos_test_52_$ ./file1
asidirop
Mon Nov 20 18:54:36 EET 2006
aetos_test_53_$ ~/test/file1
asidirop
Mon Nov 20 18:54:42 EET 2006
aetos_test_54_$ pwd
/usr/people/staff/ektaktoi/it/asidirop/test

Αν δεν δώσουμε διαδρομή για το αρχείο file1, τότε θα πάρουμε το μήνυμα σφάλματος:

bash-2.05a$ ./file1
asidirop
Mon Nov 20 18:57:17 EET 2006
bash-2.05a$ file1
bash: file1: command not found
bash-2.05a$

Αυτό συμβαίνει, διότι το αρχείο-σενάριο file1 δεν βρίσκεται σε κάποιον κατάλογο που περιέχεται στη μεταβλητή περιβάλλοντος PATH. Όπως αναφέραμε στη για να μπορούμε να εκτελούμε ένα πρόγραμμα-εντολή χωρίς να γράφουμε τη διαδρομή σε αυτό, πρέπει ο κατάλογος μέσα στον οποίο είναι αποθηκευμένο να έχει συμπεριληφθεί στη μεταβλητή περιβάλλοντος PATH.

Μεταβλητές Μεταβλητές Κελύφους Variables Shell Variables

Μεταβλητές Κελύφους

Στο κέλυφος υπάρχουν μεταβλητές (εκτός των μεταβλητών περιβάλλοντος). Ο χρήστης μπορεί να ορίσει οποιαδήποτε μεταβλητή. Όλες οι μεταβλητές είναι αλφαριθμητικού τύπου (string) και δεν μπορεί ο χρήστης να καθορίσει κάποιον διαφορετικό τύπο.

Ως ονόματα μεταβλητών ο χρήστης μπορεί να θέσει οποιονδήποτε συνδυασμό γραμμάτων και αριθμών (αρχίζοντας από γράμμα), ενώ από τους ειδικούς χαρακτήρες ο μόνος που μπορεί να χρησιμοποιείται με ασφάλεια είναι η υπογράμμιση “_”. Δεν επιτρέπονται κενά και άλλοι ειδικοί χαρακτήρες που μπορεί να έχουν κάποια ειδική σημασία για το κέλυφος (π.χ. $ \ # ; κ.ο.κ.).

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

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

Ο ορισμός μιας μεταβλητής γίνεται με την αρχικοποίηση της:

a=5

Με το παραπάνω ορίζεται η μεταβλητή με όνομα a και αποθηκεύεται η τιμή 5 ως αλφαριθμητικό (string). Απόλυτα ισοδύναμο με το προηγούμενο είναι και το:

a="5"
OK Πριν και μετά τον χαρακτήρα "=" δεν πρέπει να υπάρχουν κενά.
OK Εάν στην τιμή προς εκχώρηση περιέχονται ειδικοί χαρακτήρες (πχ: space * ; < > | κ.ο.κ.) τότε πρέπει να χρησιμοποιηθούν εισαγωγικά (μονά ή διπλά) ή να αναιρεθεί η ειδική σημασία αυτών των χαρακτήρων με το \ (ανάποδη κάθετος - back slash).

Σφάλματα κατά την εκχώρηση

Στην περίπτωση που προσπαθεί ο χρήστης να κάνει εκχώρηση αλλά χρησιμοποιηθούν με λάθος τρόπο τα κενά διαστήματα (space), τότε προφανώς δεν θα γίνει εκχώρηση, αλλά θα επιστραφεί μήνυμα σφάλματος.

asidirop@dellpc:~$ a=5 Β   # Εκχώρηση 1
Β: command not found
asidirop@dellpc:~$ a="5 Β" # Εκχώρηση 2
asidirop@dellpc:~$ a= 5    # Εκχώρηση 3
5: command not found
asidirop@dellpc:~$ a=" 5"  # Εκχώρηση 4
asidirop@dellpc:~$ a =5    # Εκχώρηση 5
a: command not found

Παραπάνω παρουσιάζονται τρεις περιπτώσεις σφάλματος. Στην πρώτη περίπτωση (εκχώρηση 1) υπάρχει κενό διάστημα μεταξύ του 5 και του Β. Εφόσον η τιμή προς εκχώρηση περιέχει κενό διάστημα, θα πρέπει να χρησιμοποιούνται εισαγωγικά όπως στην εκχώρηση 2. Στην περίπτωση που δεν έχουν χρησιμοποιηθεί εισαγωγικά, εμφανίζεται το μήνυμα “Β: command not found”. Αυτό σημαίνει ότι το κέλυφος προσπάθησε να εκτελέσει μια εντολή με όνομα Β η οποία όμως προφανώς δεν βρέθηκε. Αυτό οφείλεται στο ότι το κέλυφος υποστηρίζει την εξής σύνταξη:

ENV_VAR1=VAL1 ENV_VAR2=VAL2 .... command [args]

Με το παραπάνω δίνεται η εντολή εκτέλεσης command, όμως επιπλέον πριν την εκτέλεσή της τίθενται και οι μεταβλητές περιβάλλοντος ENV_VAR1, ENV_VAR2, κτλ.

Ακριβώς το ίδιο σφάλμα συμβαίνει και στην επόμενη περίπτωση (Εκχώρηση 3). Το κέλυφος προσπαθεί να εκτελέσει την εντολή με όνομα "5" με αρχικοποίηση της μεταβλητής περιβάλλοντος a σε κενή τιμή. Η λύση προφανώς είναι η χρήση εισαγωγικών (εκχώρηση 4).

Στην τελευταία περίπτωση (εκχώρηση 5) έχει μπει κενό διάστημα πριν από τον χαρακτήρα =. Το μήνυμα σφάλματος από το κέλυφος είναι: “a: command not found”. Είναι σχεδόν προφανές πως ερμήνευσε την εντολή το κέλυφος. Θεώρησε ότι δόθηκε η εντολή a με όρισμα το "=5".

OK Πριν και μετά τον χαρακτήρα "=" δεν πρέπει να υπάρχουν κενά.

Εκχώρηση τιμών

Η εκχώρηση τιμής σε μια μεταβλητή στο κέλυφος μπορεί να γίνει με δυο τρόπους:

  1. Ανάθεση τιμής με εκχώρηση, όπως περιγράφηκε παραπάνω (πχ: a=543245).
  2. Ανάγνωση τιμής από την κανονική είσοδο χρησιμοποιώντας την read.
read (εντολή)

Χρησιμοποιώντας την εντολή read

Η εντολή read εκχωρεί σε μια μεταβλητή οτιδήποτε εισάγεται από την κανονική είσοδο (συνήθως το τερματικό), ακολουθούμενο από μια αλλαγή γραμμής, δηλαδή διαβάζει από το τερματικό μια γραμμή.

bash-2.05a$ read b
foo         bar
bash-2.05a$ echo $b
foo bar
bash-2.05a$ echo "$b"
foo         bar
bash-2.05a$

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

echo (εντολή)

Είναι πολύ συνηθισμένο κατά την εκτέλεση ενός προγράμματος να υπάρχει η ανάγκη για είσοδο από τον χρήστη. Σε αυτήν την περίπτωση προφανώς χρησιμοποιείται η εντολή read. Είναι πολύ κλασικός συνδυασμός της εντολής read με την echo, όπου η πρώτη εμφανίζει την προτροπή για τον χρήστη:

Αρχείο: read_demo
#!/bin/bash echo -n "Enter some text > " read text echo "You entered: $text"

Παρακάτω φαίνεται η εκτέλεση του σεναρίου read_demo. Η χρήση του ορίσματος -n στην εντολή echo οδηγεί την εντολή να μην αλλάξει γραμμή μετά την εμφάνιση του μηνύματος, συνεπώς ο κέρσορας παραμένει στην ίδια γραμμή και ο χρήστης «γράφει» στην ίδια γραμμή με την προτροπή. Η εντολή θα διαβάσει, την είσοδο μέχρι να διαβαστεί ο χαρακτήρας αλλαγής γραμμής, ο οποίος βέβαια αντιστοιχεί στο πλήκτρο “Enter”.

asidirop@aetos:/tmp$ ./read_demo
Enter some text > My name is Antonis
You entered: My name is Antonis
asidirop@aetos:/tmp$
Μεταβλητές Μεταβλητές Κελύφους Variables Shell Variables

Χρήση μεταβλητών

Η χρήση μιας μεταβλητής (δηλαδή της τιμής που περιέχει) γίνεται χρησιμοποιώντας τον χαρακτήρα $.

OK Ο χαρακτήρας $ δίνει την οδηγία στο κέλυφος να θεωρήσει τη λέξη (string) που ακολουθεί ως όνομα μεταβλητής και να την αντικαταστήσει με την τιμή της.
asidirop@dellpc:/tmp$ a=Hello
asidirop@dellpc:/tmp$ echo $a
Hello
Quotes Single Quotes Double Quotes Back Quotes Εισαγωγικά Μονά Εισαγωγικά Διπλά Εισαγωγικά Ανάποδα Εισαγωγικά Strings Συμβολοσειρές

Χρήση Εισαγωγικών

Το σύνολο χαρακτήρων ASCII περιέχει τρεις τύπους εισαγωγικών. Για το κέλυφος και οι τρεις τύποι εισαγωγικών έχουν ειδική σημασία:

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

Η χρήση εισαγωγικών (μονά ή διπλά) είναι σχεδόν υποχρεωτική, όταν μέσα σε ένα αλφαριθμητικό περιλαμβάνονται ειδικοί χαρακτήρες. Ο περισσότερο συχνός ειδικός χαρακτήρας είναι το κενό διάστημα (space). Στο παρακάτω παράδειγμα η χρήση ή όχι εισαγωγικών στις τρεις πρώτες περιπτώσεις δεν έχει διαφορά. Είτε χρησιμοποιηθούν μονά εισαγωγικά, είτε διπλά, είτε καθόλου, το αποτέλεσμα είναι το ίδιο και σωστό, εφόσον στο αλφαριθμητικό δεν περιλαμβάνεται κάποιος ειδικός χαρακτήρας.

bash-2.05a$ echo "TEST"
TEST
bash-2.05a$ echo 'TEST'
TEST
bash-2.05a$ echo TEST
TEST
bash-2.05a$ echo "TEST     A"
TEST     A
bash-2.05a$ echo 'TEST     A'
TEST     A
bash-2.05a$ echo TEST     A
TEST A
bash-2.05a$ echo TEST\ \ \ \ \ A
TEST     A

Στις τρεις επόμενες εντολές το αλφαριθμητικό περιλαμβάνει πολλαπλά κενά διαστήματα (spaces). Για το κέλυφος (για όλα τα κελύφη) το κενό διάστημα είναι ειδικός χαρακτήρας και σημαίνει διαχωρισμός ορισμάτων μεταξύ τους ή διαχωρισμός εντολής από τα ορίσματα. Μάλιστα, το κέλυφος αγνοεί τα πολλαπλά κενά διαστήματα. Βλέπουμε, λοιπόν, στο παράδειγμα ότι το αποτέλεσμα είναι σωστό με τη χρήση εισαγωγικών (μονών ή διπλών), αλλά όχι ακριβές χωρίς τη χρήση εισαγωγικών. Ουσιαστικά αγνοούνται (χάνονται) τα πολλαπλά κενά διαστήματα. Άρα, η χρήση των εισαγωγικών είναι ενδεδειγμένη στην περίπτωση που το αλφαριθμητικό περιέχει έναν ή περισσότερους ειδικούς χαρακτήρες. Στην περίπτωση που ο χρήστης δεν επιθυμεί τη χρήση των εισαγωγικών, τότε θα πρέπει να ακυρωθεί η ειδική σημασία των ειδικών χαρακτήρων με τη χρήση της ανάποδης καθέτου (back slash). Θα πρέπει πριν από κάθε εμφάνιση ειδικού χαρακτήρα να χρησιμοποιείται η ανάποδη κάθετος (τελευταία εντολή). Αυτή η μέθοδος, βέβαια, δεν είναι ιδιαίτερα πρακτική, ειδικά όταν υπάρχουν πολλές εμφανίσεις ειδικών χαρακτήρων.

Εισαγωγικά και μεταβλητές κελύφους

Όταν συμπεριλαμβάνονται ειδικοί χαρακτήρες στην τιμή μιας μεταβλητής απαιτείται προσοχή ώστε να μην ερμηνευτούν αυτοί οι ειδικοί χαρακτήρες. Στο παρακάτω παράδειγμα έγινε εκχώρηση στη μεταβλητή a ενός αλφαριθμητικού το οποίο περιέχει πολλαπλά κενά διαστήματα. Αν χρησιμοποιηθεί το $a χωρίς εισαγωγικά, το αποτέλεσμα είναι το ίδιο με την προηγούμενη περίπτωση, δηλαδή καταστρέφονται τα πολλαπλά κενά διαστήματα. Θα πρέπει λοιπόν και οι μεταβλητές να χρησιμοποιούνται μέσα σε εισαγωγικά. Προφανώς, αν η μεταβλητή δεν περιείχε ειδικούς χαρακτήρες, δεν θα υπήρχε πρόβλημα στη χρήση της και χωρίς εισαγωγικά. Όμως αυτό δεν μπορεί να το γνωρίζει ο προγραμματιστής εκ των προτέρων. Συνεπώς, μέσα σε ένα σενάριο κελύφους οι μεταβλητές θα πρέπει να χρησιμοποιούνται μέσα σε διπλά εισαγωγικά. Εδώ να θυμίσουμε ότι μέσα στα διπλά εισαγωγικά ακυρώνονται όλοι οι ειδικοί χαρακτήρες εκτός από τους $, \, ". Άρα, μέσα σε διπλά εισαγωγικά οι μεταβλητές, οι οποίες σηματοδοτούνται με το $, ερμηνεύονται. Μέσα σε μονά εισαγωγικά ακυρώνονται όλοι οι ειδικοί χαρακτήρες, συνεπώς και ο χαρακτήρας $.

bash-2.05a$ a='test    1' # εντολή 1
bash-2.05a$ echo $a       # εντολή 2
test 1
bash-2.05a$ echo "$a"     # εντολή 3
test    1
bash-2.05a$ echo '$a'     # εντολή 4
$a
bash-2.05a$
OK Μέσα σε ένα σενάριο κελύφους οι μεταβλητές πρέπει να χρησιμοποιούνται μέσα σε διπλά εισαγωγικά.

Μάλιστα, όταν στις μεταβλητές περιέχονται περισσότερο «κρίσιμοι» χαρακτήρες από το κενό διάστημα, τότε τα αποτελέσματα μπορεί να είναι τραγικά άστοχα, ενδεχομένως και καταστροφικά για τη σωστή λειτουργία του σεναρίου κελύφους, όπως στο παρακάτω παράδειγμα. Εδώ ο ειδικός χαρακτήρας "*" αντικαθίσταται με όλα τα ονόματα αρχείων του τρέχοντος καταλόγου (θυμηθείτε ότι είναι wildcard - ).

bash-2.05a$ a='*'
bash-2.05a$ echo $a
file1 file5 test8
bash-2.05a$ echo "$a"
*
bash-2.05a$

Αν μέσα σε διπλά εισαγωγικά θέλουμε να χρησιμοποιήσουμε τον χαρακτήρα $, χωρίς αυτός να ερμηνευτεί ως ειδικός χαρακτήρας, παράδειγμα αν θέλουμε να εκτυπώσουμε το αλφαριθμητικό "x$x" τότε θα πρέπει να ακυρώσουμε την ειδική σημασία του με τη χρήση της ανάποδης καθέτου (back slash) ή εναλλακτικά να χρησιμοποιηθούν μονά εισαγωγικά:

bash-2.05a$ echo "x\$x"
x$x
bash-2.05a$ echo 'x$x'
x$x
bash-2.05a$ echo "x$x"   # Με " και χωρίς \$ το αποτέλεσμα δεν είναι το ζητούμενο.
x
bash-2.05a$
Μεταβλητές Περιβάλλοντος Environment Variables

Εισαγωγικά και μεταβλητές περιβάλλοντος

Στην περίπτωση των μεταβλητών περιβάλλοντος υπάρχει η ίδια συμπεριφορά και υφίσταται το ίδιο πρόβλημα με τις μεταβλητές του κελύφους. Οι μεταβλητές περιβάλλοντος χρησιμοποιούνται και αυτές με τη χρήση του δολαρίου ($). Στο παρακάτω παράδειγμα, η μεταβλητή περιβάλλοντος HOSTNAME (η οποία περιέχει το όνομα υπολογιστή στον οποίο είμαστε συνδεδεμένοι) ερμηνεύεται μέσα στα διπλά εισαγωγικά αλλά όχι μέσα στα μονά.

asidirop@aetos:/tmp$ echo "My host name is $HOSTNAME"
My host name is aetos
asidirop@aetos:/tmp$ echo 'My host name is $HOSTNAME'
My host name is $HOSTNAME
asidirop@aetos:/tmp$
String Concatenation Συνένωση Συμβολοσειρών

Συνένωση μεταβλητών

Το κέλυφος, όταν συναντά τον χαρακτήρα $, θεωρεί ότι αυτό που ακολουθεί είναι όνομα μεταβλητής (ή μεταβλητής περιβάλλοντος) και το αντικαθιστά με την τιμή του. Πού όμως τελειώνει «αυτό που ακολουθεί»; Σε ποιο σημείο τελειώνει το όνομα της μεταβλητής; Το όνομα μεταβλητής τελειώνει μόλις το κέλυφος συναντήσει κάποιον χαρακτήρα που απαγορεύεται στα ονόματα μεταβλητών, δηλαδή κάποιον ειδικό χαρακτήρα, ή κενό διάστημα, ή αλλαγή γραμμής.

Αν θέλουμε να ενώσουμε δυο αλφαριθμητικά, το μόνο που χρειάζεται είναι να τοποθετηθεί το ένα μετά το άλλο. Για παράδειγμα:

asidirop@aetos:/tmp$ a='test'
asidirop@aetos:/tmp$ b='foo'
asidirop@aetos:/tmp$ echo "$a$b"
testfoo
asidirop@aetos:/tmp$

Τι θα γίνει όμως, αν θέλω να συνενώσω μια μεταβλητή με μια σταθερή συμβολοσειρά;

asidirop@aetos:/tmp$ a='test'
asidirop@aetos:/tmp$ echo "$afoo"

asidirop@aetos:/tmp$

Στο παραπάνω παράδειγμα το κέλυφος αποτυγχάνει να τυπώσει τη μεταβλητή $a και αμέσως μετά τη συμβολοσειρά "foo", διότι θεωρεί ότι το όνομα της μεταβλητής είναι afoo. Τέτοια μεταβλητή δεν έχει οριστεί, συνεπώς εκτυπώνει κενό. Υπάρχουν πολλές λύσεις, για να παρακαμφθεί το παραπάνω πρόβλημα:

asidirop@aetos:/tmp$ a='test'
asidirop@aetos:/tmp$ echo "$a""foo"
testfoo
asidirop@aetos:/tmp$ echo "$a"foo
testfoo
asidirop@aetos:/tmp$ echo "$a"'foo'
testfoo
asidirop@aetos:/tmp$ echo $a'foo'   #όχι καλή λύση
testfoo
asidirop@aetos:/tmp$

Όλες οι παραπάνω λύσεις είναι του τύπου WorkAround (πρόχειρα μπαλώματα), δηλαδή πρόχειρη παράκαμψη του προβλήματος. Η ορθή και γενική λύση είναι να χρησιμοποιούνται τα άγκιστρα: {}.

asidirop@aetos:/tmp$ a='test'
asidirop@aetos:/tmp$ echo "${a}foo"
testfoo

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

OK Ο καλύτερος τρόπος, για να χρησιμοποιηθεί μια μεταβλητή a, είναι να χρησιμοποιείται μέσα σε διπλά εισαγωγικά και άγκιστρα "${a}".
Back Quotes Ανάποδα Εισαγωγικά

Τα ανάποδα εισαγωγικά ` και το $()

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

asidirop@aetos:~$ date
Mon Jul 27 10:54:27 EEST 2015
asidirop@aetos:~$ a=`date`
asidirop@aetos:~$ echo "$a"
Mon Jul 27 10:54:32 EEST 2015
asidirop@aetos:~$ echo "$a"
Mon Jul 27 10:54:32 EEST 2015
asidirop@aetos:~$

Για παράδειγμα η εντολή date εμφανίζει στην κανονική έξοδο την τρέχουσα ημερομηνία και ώρα. Αν γίνει ανάθεση της date με ανάποδα εισαγωγικά σε μια μεταβλητή a, τότε η μεταβλητή θα περιέχει αυτό που έστειλε η εντολή στην κανονική έξοδο την ώρα της ανάθεσης. Προφανώς, όσες φορές και να εμφανίσουμε τη μεταβλητή θα περιέχει την ώρα κατά τη στιγμή της ανάθεσης και όχι την ώρα κατά τη στιγμή της χρήσης. Δηλαδή η εντολή έχει εκτελεστεί μια φορά κατά τη στιγμή της ανάθεσης.

Επιπλέον, μέσα στα ανάποδα εισαγωγικά μπορεί να συμπεριληφθεί οποιοσδήποτε συνδυασμός εντολών με διασωλήνωση, ανακατεύθυνση και οτιδήποτε άλλο θα μπορούσε να γραφτεί στη γραμμή εντολών. Στην πραγματικότητα, όταν το κέλυφος βρει ανάποδα εισαγωγικά δημιουργεί ένα νέο κέλυφος (διεργασία), το οποίο είναι υπεύθυνο να εκτελέσει τις εντολές που περικλείονται στα εισαγωγικά. Η κανονική έξοδος από το νέο κέλυφος δεσμεύεται και γίνεται ανάθεση στη μεταβλητή. Όλες οι εντολές που θα εκτελεστούν από το νέο κέλυφος, κληρονομούν την κανονική έξοδο του νέου κελύφους.

asidirop@aetos:~$ a=`ls|wc`
asidirop@aetos:~$ echo "$a"
     33      33     257
asidirop@aetos:~$

Η έξοδος των εντολών μέσα στα ανάποδα εισαγωγικά μπορεί να περιέχει πολλές γραμμές, όπως για παράδειγμα η έξοδος από την εντολή ls -l:

bash-2.05a$ b=`ls -l`
bash-2.05a$ echo $b
total 32 -rw-r--r-- 1 asidirop it 314 Jan 11 2003 cc -rwxr--r-- 1 asidirop it 77 Nov 20 19:17 file1 -rwxr-xr-x 1 asidirop it 61 Nov 20 19:34 file2 -rw-r--r-- 1 asidirop it 183 Jan 8 2003 list
bash-2.05a$ echo "$b"
total 32
-rw-r--r--    1 asidirop it           314 Jan 11  2003 cc
-rwxr--r--    1 asidirop it            77 Nov 20 19:17 file1
-rwxr-xr-x    1 asidirop it            61 Nov 20 19:34 file2
-rw-r--r--    1 asidirop it           183 Jan  8  2003 list
bash-2.05a$

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

Στο κέλυφος bash την ίδια χρήση με τα ανάποδα εισαγωγικά έχει και ο συνδυασμός $(). Πρακτικά είναι ισοδύναμα με τα ανάποδα εισαγωγικά:

bash-2.05a$ b=$(ls –l)
bash-2.05a$ echo "$b"
total 32
-rw-r--r--    1 asidirop it           314 Jan 11  2003 cc
-rwxr--r--    1 asidirop it            77 Nov 20 19:17 file1
-rwxr-xr-x    1 asidirop it            61 Nov 20 19:34 file2
-rw-r--r--    1 asidirop it           183 Jan  8  2003 list
bash-2.05a$
OK Χρειάζεται ιδιαίτερη προσοχή! Όταν σε ένα κέλυφος χρησιμοποιούνται δυνατότητες του bash οι οποίες δεν υποστηρίζονται από το sh, θα πρέπει οπωσδήποτε το σενάριο να ξεκινά με #!/bin/bash και όχι με #!/bin/sh.

Αριθμητικές Πράξεις

Αριθμητικές Πράξεις expr (εντολή)

Αριθμητικές Πράξεις με χρήση της expr

Γενικά τα κελύφη δεν υποστηρίζουν μεταβλητές τύπου αριθμού και συνεπώς δεν υποστηρίζουν αριθμητικές πράξεις (το bash όμως υποστηρίζει: βλέπε ). Οι αριθμητικές πράξεις πραγματοποιούνται με τη χρήση εξωτερικών εντολών. Η πιο διαδεδομένη εντολή για αριθμητικές πράξεις είναι η expr. Η expr έχει τη δυνατότητα να πραγματοποιεί μόνο πράξεις ακέραιων αριθμών. Δέχεται ως ορίσματα την αριθμητική παράσταση και τυπώνει το αποτέλεσμα στην κανονική έξοδο.

asidirop@aetos:~$ expr 1 + 2
3
asidirop@aetos:~$ a=5
asidirop@aetos:~$ expr "$a" - 10
-5
asidirop@aetos:~$ b=4000
asidirop@aetos:~$ expr "$b" + "$a"
4005
asidirop@aetos:~$

Προφανώς στα ορίσματα της expr μπορούν να δοθούν είτε σταθερές τιμές αριθμών είτε μεταβλητές στις οποίες προηγούμενα έχει γίνει ανάθεση αριθμητική τιμή.

Απαιτείται αρκετή προσοχή, όταν χρησιμοποιείται η εντολή expr στον τρόπο με τον οποίο θα δοθούν τα ορίσματα καθώς και στην πράξη του πολλαπλασιασμού.

asidirop@aetos:~$ expr 6 * 10       # εντολή 1
expr: syntax error
asidirop@aetos:~$ expr 6 \* 10      # εντολή 2
60
asidirop@aetos:~$ expr 6 '*' 10     # εντολή 3
60
asidirop@aetos:~$ expr "6 * 10"     # εντολή 4
6 * 10
asidirop@aetos:~$

Εφόσον η πράξη του πολλαπλασιασμού συμβολίζεται με τον χαρακτήρα *, ο οποίος όμως είναι και ειδικός χαρακτήρας για το κέλυφος, θα πρέπει να ακυρωθεί η ειδική του σημασία. Διαφορετικά, όπως φαίνεται στην πρώτη εντολή παραπάνω η expr θα δώσει συντακτικό σφάλμα, διότι το "*" θα έχει αντικατασταθεί από το κέλυφος, πριν εκτελεστεί η expr με τα ονόματα αρχείων του τρέχοντος καταλόγου. Στη δεύτερη και τρίτη εντολή παραπάνω, έχει ακυρωθεί η ειδική σημασία του "*", στη μια με την ανάποδη κάθετο ενώ στην άλλη με τα εισαγωγικά. Προσοχή όμως (τέταρτη εντολή) δεν θα μπορούσαμε να βάλουμε σε εισαγωγικά ολόκληρη την αριθμητική παράσταση. Η expr απαιτεί κάθε τελεστής και τελεστέος να αποτελεί διαφορετικό όρισμα στην εντολή.

Εφόσον η expr στέλνει το αποτέλεσμα στην κανονική έξοδο, μπορούμε να δεσμεύσουμε την κανονική έξοδο με τη χρήση των ανάποδων εισαγωγικών και να την εκχωρήσουμε σε κάποια μεταβλητή.

asidirop@aetos:~$ c=`expr 7 / 2`
asidirop@aetos:~$ echo "$c"
3
asidirop@aetos:~$ a=4000
asidirop@aetos:~$ b=5
asidirop@aetos:~$ d=`expr "$a" + "$b"`
asidirop@aetos:~$ echo "$d"
4005
asidirop@aetos:~$

Ο προηγούμενος, λοιπόν, είναι ο γενικός τρόπος χρήσης της expr σε όλα τα κελύφη. Όμως χρειάζεται ιδιαίτερη προσοχή κατά τη χρήση. Στο παρακάτω παράδειγμα φαίνονται δυο πολύ συνηθισμένες περιπτώσεις σφάλματος. Στην πρώτη περίπτωση ολόκληρη η αριθμητική παράσταση αποτελεί ένα και μόνο όρισμα, κάτι που δεν έχει τη δυνατότητα να το διαχειριστεί η expr. Στη δεύτερη περίπτωση δεν διαχωρίζεται η πράξη από τον δεύτερο αριθμό, συνεπώς η εντολή θεωρεί το "+" ως πρόσημο του δεύτερου αριθμού, άρα λείπει η πράξη. Το αποτέλεσμα και στις δυο περιπτώσεις είναι εσφαλμένο.

asidirop@aetos:~$ expr 6+4
6+4
asidirop@aetos:~$ expr 6 +4
expr: syntax error
asidirop@aetos:~$

Επίσης, πρόβλημα μπορεί να δημιουργηθεί και στις περιπτώσεις που χρησιμοποιούνται μεταβλητές, αλλά αυτές δεν έχουν αρχικοποιηθεί. Στα παρακάτω παραδείγματα, στην πρώτη περίπτωση, έχει αρχικοποιηθεί η μεταβλητή a αλλά όχι η w. Συνεπώς, η εντολή expr $a + $w είναι σαν να έχουμε γράψει expr 1 +, το οποίο προφανώς είναι λανθασμένη αριθμητική παράσταση. Ακόμη και αν χρησιμοποιηθούν εισαγωγικά (εντολή τρίτη), το τρίτο όρισμα ("$w") θα είναι κενό (empty string), το οποίο η expr δεν μπορεί να το μετατρέψει σε αριθμό.

bash-2.05a$ a=1
bash-2.05a$ expr $a + $w          # περίπτωση 1
Syntax error
bash-2.05a$ expr 1 +              # περίπτωση 2
Syntax error
bash-2.05a$ expr "$a" + "$w"      # περίπτωση 3
non-numeric argument
bash-2.05a$ expr 0"$a" + 0"$w"    # περίπτωση 4
1
bash-2.05a$ b=x
bash-2.05a$ expr 0"$a" + 0"$b"    # περίπτωση 5
expr: non-integer argument
bash-2.05a$ k=`expr $c + $w`      # περίπτωση 6
Syntax error
bash-2.05a$ echo $k

bash-2.05a$

Μια πρόχειρη παράκαμψη του προβλήματος μπορεί να γίνει χρησιμοποιώντας το "0" πριν από κάθε μεταβλητή, έτσι ώστε, αν η μεταβλητή είναι κενή ο τελεστέος να είναι "0", αν δεν είναι κενή, παράδειγμα "1", ο τελεστέος θα είναι "01", δηλαδή αριθμητικά "1". Παρόλα αυτά, πάλι μπορούν να προκύψουν σφάλματα στην περίπτωση που μια μεταβλητή δεν περιέχει αριθμό αλλά χαρακτήρες (περίπτωση 5).

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

OK Πριν από τη χρήση της expr απαιτείται οπωσδήποτε έλεγχος και επιβεβαίωση νόμιμων τιμών των μεταβλητών.
OK Εναλλακτικά, μετά από τη χρήση της expr απαιτείται οπωσδήποτε έλεγχος και επιβεβαίωση του αποτελέσματος ή του κωδικού σφάλματος (error code ή exit code).
Αριθμητικές Πράξεις Arithmetic mode

Αριθμητικές Πράξεις με χρήση της αριθμητικής κατάστασης (arithmetic mode) του bash

Το bash (ως επέκταση του sh) έχει τη δυνατότητα να κάνει αριθμητικές πράξεις. Επιπλέον:
asidirop@aetos:~$ a=$((5+4))    # περίπτωση 1
asidirop@aetos:~$ echo $a
9
asidirop@aetos:~$ a=$((a+4))    # περίπτωση 2
asidirop@aetos:~$ echo $a
13
asidirop@aetos:~$ a=$((a+w))    # περίπτωση 3
asidirop@aetos:~$ echo $a
13
asidirop@aetos:~$ a=$((a+$w))   # περίπτωση 4
-bash: a+: syntax error: operand expected (error token is "+")
asidirop@aetos:~$
asidirop@aetos:~$ w='test'
asidirop@aetos:~$ a=$((a+w))    # περίπτωση 5
asidirop@aetos:~$ echo $a
13

Στο παραπάνω παράδειγμα βλέπουμε (περιπτώσεις 1 και 2) ότι δεν υπάρχει πρόβλημα με τη μη ύπαρξη κενών διαστημάτων μέσα στην αριθμητική παράσταση. Στην περίπτωση 3, που χρησιμοποιείται η μεταβλητή w, η οποία δεν έχει οριστεί, χρησιμοποιείται σαν μηδέν (0). Όμως αν είχαμε βάλει τον χαρακτήρα $ πριν από τη μεταβλητή w, τότε, όπως φαίνεται στην περίπτωση 4, υπάρχει πρόβλημα. Τέλος, αν μια μεταβλητή περιέχει γράμματα και όχι αριθμό, τότε μεταφράζεται σε μηδέν (περίπτωση 5).

Χρειάζεται μεγάλη προσοχή στην περίπτωση που χρησιμοποιηθεί ο χαρακτήρας $ μέσα σε αριθμητική παράσταση ως μεταβλητή.

asidirop@aetos:~$ a=13
asidirop@aetos:~$ b='test'
asidirop@aetos:~$ test='1000'
asidirop@aetos:~$ c=$(($a+$b))
asidirop@aetos:~$ echo $c
1013
asidirop@aetos:~$

To $ ερμηνεύεται πριν από την εκτέλεση της πράξης. Άρα, το $b θα αντικατασταθεί με το test και το $a με το 13. Άρα, είναι σαν να γράφουμε: c=$((13+test)) και το test είναι μεταβλητή που περιέχει την τιμή 1000!

OK Προτιμήστε τη δυνατότητα του bash για αριθμητικές πράξεις, αρκεί το σενάριο να ξεκινάει με #!/bin/bash
OK Αποφύγετε τη χρήση του $ μέσα στην κατάσταση αριθμητικής λειτουργίας (arithmetic mode).

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

asidirop@aetos:~$ a=13
asidirop@aetos:~$ ((a++))
asidirop@aetos:~$ echo $a
14
asidirop@aetos:~$ ((a+=6))
asidirop@aetos:~$ echo $a
20
asidirop@aetos:~$ ((b=a+8))
asidirop@aetos:~$ c=$((a+8))
asidirop@aetos:~$ echo $b
28
asidirop@aetos:~$ echo $c
28
asidirop@aetos:~$

Όπως φαίνεται στα παραπάνω παραδείγματα, ανάθεση τιμής σε μεταβλητή μπορεί να γίνει εξολοκλήρου μέσα στην κατάσταση αριθμητικής λειτουργίας, δηλαδή το ((b=a+8)). Προσοχή, σε αυτήν την περίπτωση δεν χρησιμοποιείται ο χαρακτήρας $ πριν από τις παρενθέσεις.

Λίστα με τις αριθμητικές πράξεις της κατάστασης αριθμητικής λειτουργίας του κελύφους.
ΠράξηΕξήγηση
id++ id--variable post-increment and post-decrement
++id --idvariable pre-increment and pre-decrement
- +unary minus and plus
! ~logical and bitwise negation
**exponentiation
* ,/ , %multiplication, division, remainder
+, - addition, subtraction
<< , >>left and right bitwise shifts
<= ,>=, <, >comparison
==, !=equality and inequality
&, ^, | bitwise AND, bitwise exclusive OR, bitwise OR
&&, ||logical AND, logical OR
expr?expr:exprconditional operator
= *= /= %= += -= <<= >>= &= ^= |=assignment

Ασκήσεις για εξάσκηση

Στόχος

Εισαγωγή στα σενάρια φλοιού

Άσκηση 1

Φτιάξτε ένα σενάριο κελύφους με όνομα myls, το οποίο θα εκτελεί την εντολή ls –l (θα εμφανίζει τα αποτελέσματά της), μετά θα εμφανίζει το πλήθος των απλών αρχείων, το πλήθος των καταλόγων, το πλήθος των κρυφών αρχείων, το πλήθος των κρυφών καταλόγων.

Άσκηση 2

Δημιουργήστε ένα script με όνομα echo_test το οποίο να περιέχει:

Αρχείο: echo_test
#!/bin/sh #echo_test #----------------------------------- echo "1.the process id is : $$ and cwd $PWD" echo '2.the process id is : $$ and cwd $PWD ' echo 3.the process id is : $$ and cwd $PWD echo 4.the process id is\ \ :\ \ \ \$\$ and cwd $PWD echo '5.the process id is : "$$ and cwd $PWD" ' echo "6.the process id is : '$$' and cwd $PWD"

Τι παρατηρείτε κατά την εκτέλεσή του;

Άσκηση 3

Δημιουργήστε ένα σενάριο κελύφους με όνομα echo_test3 το οποίο να περιέχει:

Αρχείο: echo_test3
#!/bin/bash #echo_test3 #----------------------------------- a=TEST b=TEST B c="TEST C" d="$c + D" echo "1.a is $a" echo echo "2.b is $b" echo echo '3.c is $c' echo "4.c is $c" echo 5.c is $c echo echo "5.d is $d" echo var1="6 " var2=$((var1 + 2 )) echo "7.var1 is $var1, var2 is $var2"
  1. Τι παρατηρείτε; Ποιες είναι οι διαφορές με τη χρήση διπλών ή μονών εισαγωγικών;
  2. Ποια τιμή έχει η μεταβλητή b;

Άσκηση 4

Δημιουργήστε τα παρακάτω σενάρια κελύφους και εκτελέστε τα. Ποιο είναι το καλύτερο;

Αρχείο: echo_test4a
#!/bin/sh #echo_test4a #----------------------------------- x=`ls –l` echo x is $x

Αρχείο: echo_test4b
#!/bin/sh #echo_test4b #----------------------------------- x=`ls –l` echo "x is $x"

Αρχείο: echo_test4c
#!/bin/sh #echo_test4c #----------------------------------- x=`ls –l` echo 'x is $x'

Άσκηση 5

Δημιουργήστε ένα σενάριο κελύφους με όνομα echo_test5 το οποίο να περιέχει:

Αρχείο: echo_test5
#!/bin/bash #echo_test5 #----------------------------------- a=5 b=" 6" c="" d=$((a+5)) e=$((b+5)) ((f=c+5)) echo "d is $d, e is $e, f is $f" echo d=`expr $a + 5` e=`expr $b+5` f=`expr $c+5` echo "d is $d, e is $e, f is $f"

Άσκηση 6

Δημιουργήστε ένα σενάριο κελύφους με όνομα echo_test6 το οποίο να περιέχει:

Αρχείο: echo_test6
#!/bin/sh #script06 #----------------------------------- a=5 echo –n "Give me your name: " read name echo "value of a is $a" echo "value of name is $name"

Άσκηση 7

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

Αρχείο: echo_test7
#!/bin/sh #echo_test7 #----------------------------------- echo "first parameter : $1" echo "third parameter : $3" echo "ninth parameter : $9" echo "tenth parameter : $10" echo "eleventh parameter : $11" echo "No of parameters : $#" echo "all parameters : $*" echo "all parameters : $@" shift echo "first parameter : $1" echo "all parameters : $*"

Στον εξηγούνται οι προδηλωμένες μεταβλητές του κελύφους.

Τι θα εμφανιστεί στην οθόνη με την εκτέλεση της ακόλουθης γραμμής εντολής;

./echo_test7 one two 3 4 5 6 7 eight 9 ten 11
Προ-δηλωμένες μεταβλητές του κελύφους.
ΜεταβλητήΕξήγηση
$1,$2,…$9 οι τιμές των 9 πρώτων ορισμάτων που έδωσε ο χρήστης σε ένα script
$# το πλήθος των ορισμάτων που έδωσε ο χρήστης σε ένα script
$*, $@ Οι πίνακες με τα ορίσματα που έδωσε ο χρήστης σε ένα script
$$ Το ProcessID του τρέχοντος shell