Shell Scripts: loops / if / test Loops with for while until for variable in list_of_values ne while εντολή (επιτυχής) ne until εντολή (επιτυχής) ne For in exi times apo ayti ti lista in 1 2 alla kai alles dio in 1 2 3 4 5 6 in {1..6} for ((i=1;i<=6;i++)) i=1 while test $i -le 6 echo $i; i=$((i+1)) ne in "$*" in "$@" in * in "$1"/* in "$1"/a* Το i παίρνει τιµές από την λίστα που δίνεται µετά το in Το i παίρνει τιµές από την λίστα των παραµέτρων που δίνονται µε την εντολή Το i παίρνει τιµές από οντότητες (αρχεία, directories,..) που βρίσκονται στο τρέχοντα φάκελλο Στην εντολή for, oι τιµές της µεταβλητής (list_of_values) µπορούν να είναι νούµερα ή συµβολοσειρές, µπορούν όµως να χρησιµοποιηθούν και µεταχαρακτήρες *. $*, $@, π.χ. in $* (1) όλες οι παράµετροι in * (2) όλα τα αρχεία και directory στον τρέχοντα κατάλογο in *askisi (3) όλα τα αρχεία και directory στο current που τελειώνουν σε askisi in "$1"/* (4) όλα τα αρχεία και directory στο directory που δίνεται σαν 1 ο όρισµα Στο (1) το i παίρνει τιµές από την λίστα των παραµέτρων που δίνονται σαν ορίσµατα στην γραµµή εντολών Ετσι στο παρακάτω παράδειγµα µε όνοµα exfor και 4 ορίσµατα one two 3 lala) exfor in $* echo -n "$i " ne echo vassik@aetos:~$ chmod +x exfor vassik@aetos:~$./exfor one two 3 lala one two 3 lala vassik@aetos:~$ echo -n "$i " ne echo Αρχείο µε όνοµα exfor Μπορούµε στην εντολή for να παραλείψουµε το κοµµάτι in list_of_values. Σε αυτή την περίπτωση η λίστα αποτελείται από τα ορίσµατα που δίνονται /παρόµοιο µε in $*, in $@ [1]
while / until / if Στις αυτές, η συνθήκη (successful_command) επιζητά επιτυχία δηλαδή µια τιµή true. H τιµή true είναι το µηδέν. Αν µια εντολή αποτύχει επιστρέφει µια τιµή διάφορη του µηδέν. if echo "you must give a lename" echo "lename: $1" vassik@aetos/xunix/lab8:~$./ el you must give a lename vassik@aetos/xunix/lab8:~$./ lala lename: lala ΝΑ ΞΕΡΩ *? $a " " ' ' ` ` #! # $0 $1..$9 $* $@ $# $$ $? $! 1>&2 expr $(( )) $[ ] Αρχείο µε όνοµα H συνθήκη (successful_command) που ελέγχεται στο παράδειγµα µε όνοµα είναι η: test $# -eq 0 Η εντολή test ελέγχει αν ο αριθµός των παραµέτρων $# ισούται µε µηδέν. Η εντολή επιστρέφει τιµή µηδέν αν η απάντηση στο έλεγχο είναι «ναι» αλλιώς µια τιµή διάφορη του µηδέν. Η εντολή test µαζί µε την if είναι η πιο συνήθης αλλά µπορεί να δοθεί και µια οποιαδήποτε άλλη εντολή στην if π.χ. if cd ~/lab8 (αν έχει κάποιο νόηµα βέβαια). Στο παράδειµα αν δεν δοθεί ένα όρισµα (πχ όνοµα αρχείου) θα τυπωθεί µήνυµα για το λάθος και το script θα τερµατίσει (νωρίτερα, πριν φτάσει στο τέλος του αρχείου) µε την εντολή. Τότε η τιµή που επιστρέφει η είναι 1 και όχι 0. echo "you must give a lename" 1>&2 echo "lename: $1" Θα µπορούσε στο τέλος του να προστεθεί µια γραµµή ακόµη exit 0 για να εξασφαλιστεί ότι το εκτελέστηκε σωστά (αλλά και το echo.. επιστρέφει µηδέν) Όταν τo exit χρησιµοποιείται xωρίς νούµερο επιστρέφεται το exit code της εντολής ($?) που εκτελέστηκε τελευταία. Στο µήνυµα λάθους echo you must give a lename προστέθηκε το 1>&2 για να τυπωθεί το µήνυµα στην έξοδο λαθών (stderr) ανακατευθύνοντας 1 το stut stream (1) στο stderr stream (2). Aυτό είναι χρήσιµο µιας και το κανονικό αποτέλεσµα µιας εντολής µπορεί να διασωληνωθεί σε µια άλλη εντολή ή να γραφτεί σε αρχείο. 1 Το >& δηλώνει ανακατεύθυνση ενός stream σε άλλο stream µε fd 0:stdin, 1:stut 2:stderr [2]
echo "you must give a lename" 1>&2 echo "lename: $1" Στο πιο πάνω παράδειγµα θα µπορούσε να χρησιµοποιηθεί το:, όπως εδώ, (εδώ δεν απαιτείται το: Στο τροποιηµένο παρακάτω παράδειγµα 1, στην περίπτωση που δοθεί ένα όρισµα π.χ. όνοµα αρχείου θα γίνει και β έλεγχος αν το αρχείο που δίνεται υπάρχει και δεν είναι άδειο: 1 echo "you must give a lename" 1>&2 elif test! -s "$1" echo "no le $1" 1>&2 Αρχείο µε echo όνοµα "lename: 1 $1" Η εντολή test Mε το keyword elif δεν απαιτείται δεύτερο. Θα µπορούσε να χρησιµοποιηθεί και το if οπότε τότε απαιτείται και δεύτερο (δυο χωριστά if, το ένα if nested) Παραπάνω χρησιµοποιήθηκαν στους ελέγχους του if το test $# -eq 0 και το test! -s "$1" Γενικότερα το όρισµα της test είναι µια έκφραση που αν είναι αληθής επιστρέφει µηδέν (αλλιώς επιστρέφει κάτι άλλο εκτός 0). Μπορούν να γίνουν 3 κύρια είδη ελέγχων: test για αριθµητικές τιµές (-eq,-ne, -gt, -lt, -ge, -le) if test a -eq b οι τιµές των a,b (νούµερα) είναι ίσες (equal), if test a -gt b η τιµή του a µεγαλύτερη του b (greater than) κτλ test για αρχεία (-s, -f, -d, -w, -r) 2 if test -s le1 το αρχείο µε όνοµα le1 υπάρχει, και δεν είναι άδειο (size), if test -f le1 είναι κανονικό αρχείο, if test -d le1 είναι directory κτλ test για αλφαριθµητικά (=,!=, -z, -n ) if test a = b τα a,b (strings) είναι ίδια -z ab το αλφαριθµητικό µε όνοµα ab έχει µέγεθος 0 (zero), -n ab έχει µέγεθος non-zero κτλ. Στην ανισότητα!= δεν µπαίνει κενό ανάµεσα στο = και το! Τελεστής! εν/not, απαιτεί κενά πριν και µετά το! π.χ. if test! -f le1 ελέγχει αν το le1 δεν είναι αρχείο if test! -f "$1" ελέγχει αν η 1 η παράµετρος δεν είναι αρχείο Συνδυαστικοί έλεγχοι µε τελεστές -a (and) και -ο (οr) (κενά πριν και µετά τους τελεστές) while test $a!= y -a $a!= n ελέγχει αν η µεταβλητή a δεν είναι y,n. Μεταβλητές: Μια µεταβλητή a µπορεί να πάρει τιµή µε ανάθεση π.χ. a=3 (χωρίς κενό πριν και µετά το =), µε read π.χ read a, µε εκχώρηση του αποτελέσµατος άλλης εντολής π.χ. a=`date` 2 [3]
Έστω µια µεταβλητή a=3. echo $a δίνει 3 echo "$a" δίνει 3 echo '$a' δίνει $a Έστω οι µεταβλητές a=3 και b=0003, είναι ίσες? test $a -eq $b echo $? δίνει 0 (ίσες) όπου το $? είναι το exit status της τελευταίας εντολής (0 σηµαίνει αληθής), Αν συγκρίνω µε -eq και διπλά εισαγωγικά δηλ. test "$a" -eq "$b" exit status 0 (ίσες). Αν βάλω -eq και µονά εισαγωγικά δηλ. test '$a' -eq '$b' ζητά να συγκριθεί το string $a µε το string $b αλλά το -eq για σύγκριση string δεν δουλεύει. Aν αντί σύγκρισης αριθµητικών χρησιµοποιήσω σύγκριση string θα είναι άνισες test $a = $b ; echo $? δεν δίνει 0 (άνισες) Έστω οι µεταβλητές a=3 και b=' 3', είναι άραγε ίσες αυτές? Ισχύει πάλι η ίδια λογική: µε -eq (σηµαίνει συγκρίνω τους αριθµούς) οπότε χωρίς εισαγωγικά, αλλά και µε διπλά εισαγωγικά το exit status είναι 0 (ίσες) δηλ. test $a -eq $b ; echo $? δίνει 0 δηλ. σαν αριθµοί ίσες αλλά π.χ. µε = και µε διπλά εισαγωγικά συγκρίνει τα δυο string test "$a" = "$b" ; echo $? δεν δίνει 0 δηλ. σαν string άνισες Ας τυπώσoυµε την µεταβλητή b=' 3' echo H b exei $b timi δίνει H b exei 3 timi echo "H b exei $b timi" δίνει H b exei.. 3 timi echo 'H b exei $b timi' δίνει H b exei $b timi Mε τα διπλά εισαγωγικά διατηρούνται τα κενά. Χρήσιµα τα διπλά εισαγωγικά και στο ακόλουθο: b=`ls -l` echo $b total 192 -rwx------ 2 vassik conit 478 May 12 19:25 ask1 -rwx------ 2 vassik conit 229 May 12 11:47 ask2 -rwx---- -- 2 vassik conit 517 May 12 19:20 ask3 echo "$b" total 192 -rwx------ 2 vassik conit 478 May 12 19:25 ask1 -rwx------ 2 vassik conit 229 May 12 11:47 ask2 -rwx------ 2 vassik conit 517 May 12 19:20 ask3 Αν κάνω εκχώρηση του αποτελέσµατος ls -l στην µεταβλητή b και την τυπώσω µε echo $b χάνεται ο χαρακτήρας αλλαγής γραµµής (αυτό δεν το θέλω ειδικά όταν ακολουθεί διασωλήνωση µε την egrep). Αντιθέτως µε echo "$b" δεν χάνεται το newline οπότε βλέπω γραµµή γραµµή. ιπλά εισαγωγικά για το "$*" σηµαίνουν "$1 $2.. " ενώ στο "$@" σηµαίνουν "$1" "$2".. µε διαχωριστικό µεταξύ τους το κενό (εκτός και αν έχει οριστεί διαφορετικά στο IFS). [4]
Μερικά παραδείγµατα Όταν γράφεται ένα script, ίσως είναι πιο εύκολο όλες οι συνθήκες που αφορούν τα λάθη, να µπαίνουν πρώτες και µετά να εκτελείται ότι ζητά το script. Χρήση ιf - Αρχείο µε όνοµα cdir - Ελέγχει αν δεν δίνεται µια παράµετρος και τυπώνει µήνυµα. Αν δίνεται παράµετρος ($1) ελέγχει αν δεν είναι directory και τυπώνει µήνυµα. Αν η παράµετρος είναι directory,µετράει πόσες οντότητες υπάρχουν µέσα σε αυτό, καθώς και πόσες υπάρχουν µέσα στο τρέχον directory και συγκρίνει τα δυο νούµερα) echo "you must give a directory name" 1>&2 elif test! -d "$1" echo "$1: not a directory" 1>&2 dir=`ls "$1" wc -l` curdir=`ls wc -l` if test $dir -ne $curdir echo "current dir and $1 n't match" vassik@aetos:~$ chmod +x cdir vassik@aetos:~$./cdir you must give a directory name vassik@aetos:~$./cdir aaa aaa: not a directory vassik@aetos:~$./cdir fakelos2 current dir and fakelos2 n't match έστω ότι υπάρχει directory fakelos2 και δεν υπάρχει aaa Χρήση for και if - Αρχείο µε όνοµα ol - Ελέγχει αν δεν δίνεται παράµετρος, τυπώνει µήνυµα και exit. Για όλες τις παράµετρους ($1, $2, ) που δίνονται ελέγχει αν δεν είναι αρχεία ή directories γεµάτα και τυπώνει µήνυµα, αλλιώς δίνει πληροφορίες για αυτές) echo usage: $0 le1 le2 1>&2 if test! -s "$i " echo "no le $i" 1>&2 ls -l "$i" ne vassik@aetos:~$ chmod +x ol vassik@aetos:~$./ol usage:./ol le1 le2 vassik@aetos:~$./ol aaa ask1 ask2 fakelos2 no le aaa -rwx------ 2 vassik conit 478 May 12 19:25 ask1 -rwx------ 2 vassik conit 229 May 12 11:47 ask2 total 0 -rw------- 2 vassik conit 0 May 12 00:06 haha έστω ότι υπάρχει ότι υπάρχoυν τα αρχεία ask1,ask2, το directory fakelos2 και δεν υπάρχει aaa ( χωρίς λίστα: η µεταβλητή i παίρνει τιµές από την λίστα µε τα ονόµατα που δίνονται σαν ορίσµατα στην γραµµή εντολών) Χρήση while και if - Αρχείο µε όνοµα sl - Ελέγχει αν δεν δίνεται παράµετρος, τυπώνει µήνυµα και exit. Για όλες τις παράµετρους που δίνονται ελέγχει αν δεν είναι αρχεία ή directories γεµάτα και τυπώνει µήνυµα not found, αλλιώς τυπώνει µήνυµα found). echo "usage: $0 dir1, le1, " 1>&2 while test $# -gt 0 if test! -s "$1" echo "not found dir or le $1" 1>&2 echo "found dir or le not empty $1" shift ne vassik@aetos:~$ chmod +x sl vassik@aetos:~$./sl usage:./sl dir1, le1, vassik@aetos:~$./sl aaa ask1 ask2 fakelos2 not found dir or le aaa found dir or le not empty ask1 found dir or le not empty ask2 found dir or le not empty fakelos2 έστω ότι υπάρχει ότι υπάρχoυν τα αρχεία ask1,ask2, το directory fakelos2 και δεν υπάρχει aaa. (Με το while ελέγχεται αν ο αριθµός των ορισµάτων ($#) είναι µεγαλύτερος από το 0, και στην περίπτωση αυτή το if ελέγχει αν το [5] 1 ο όρισµα είναι αρχείο µε µέγεθος >0. Κατόπιν µε την shift τα ορίσµατα µετατοπίζονται κατά ένα και άρα το 2 ο γίνεται αυτό 1 ο, µε το επόµενο shift το 3 ο γίνεται 1 ο κοκ µέχρι να τελειώσουν).