bash_practice

=Από τη θεωρία στην πράξη=

Δε λέω, καλά όσα είδαμε στις προηγούμενες ενότητες. Θα μπορούσα να τα αποκαλέσω σαν μια συμπυκνωμένη ύλη από θεωρητικές γνώσεις. Βέβαια εσάς μπορεί να μη σας φαίνεται τόσο συμπυκνωμένη. Αρκεί να σας πω ότι βασίστηκε σε 3-4 βιβλία των 600 σελίδων το καθένα. Πάντως σε καμία περίπτωση δεν θα πρέπει να θεωρηθεί ένας πλήρες οδηγός για "bash scripting". Μάλλον για μια καλή αρχή

Για τους διαβαστερούς παραθέτω τα ακόλουθα links από το "The Linux Documentation Project (TLDP)": http://tldp.org/LDP/Bash-Beginners-Guide/html/ http://tldp.org/LDP/abs/html/

Είναι όμως καιρός να περάσουμε από τη θεωρία στην πράξη και να φτιάξουμε κάτι χρήσιμο:

Η αλήθεια είναι ότι ποτέ μα ποτέ δεν κατάφερα να βρω ένα ολοκληρωμένο πρόγραμμα για backup που να ικανοποιεί όλες μου τις απαιτήσεις. Έτσι είπα να αξιοποιήσω το πολύ δυνατό εργαλείο rsync και να το φέρω στα μέτρα μου γράφοντας ένα scriptάκι

Κάπου το Μάρτιο του 2008 λοιπόν ήταν έτοιμη η πρώτη του έκδοση (0.1) η οποία φιλοξενείται μέχρι και σήμερα στην ιστοδελίδα του περιοδικού "Ελληνικό Linux Format" (thank u dimitris ): http://www.linuxformat.gr/?q=forum/backup-%CE%A4%CE%A9%CE%A1%CE%91 Σήμερα (08 Αυγ. 08 - κι ακόμα να πάω διακοπές ), το σουλούπωσα λίγο (του άλλαξα τα φώτα δηλαδή !!) και βουαλά η έκδοση 0.2. Παραθέτω μερικά γραφόμενα μου από το Ελληνικό LXF για να μπείτε στο πνεύμα (Μπορείτε βέβαια με ένα κλικ να πάτε εκεί και να το διαβάσετε ολόκληρο !!):

μια ματιά σε μερικές **αλήθειες**:

- Αχρείαστο να' ναι το backup, αλλά όταν έρθει εκείνη η ώρα πρέπει να είναι εύχρηστο. - Το backup ΔΕΝ πρέπει να είναι μια επίπονη και πολύωρη διαδικασία που αποτρέπει τη συχνή χρήση της. - Οι εκφράσεις "στήνω έναν backup server" ή "είσαι σίγουρος ότι τρέχει o daemon?" ή "μόνο με ένα ssh tunel θα σωθείς" είναι σαν να βγαίνουν από το στόμα του αρχιμηχανικού του galactica. Εγώ θέλω απλά μια στο τόσο να δίνω μια εντολή και ως δια μαγείας να δημιουργούνται αντίγραφα ασφαλείας από τα πολύτιμα αρχειάκια μου.

...και σε μερικές **απαιτήσεις**:

- Τα αντίγραφα ασφαλείας πρέπει να διατηρούν ακριβώς τις ίδιες ιδιότητες με τα αυθεντικά αρχεία (permissions, owner, group, time κτλ). - Δεν θέλω κάθε φορά που κάνω backup να αντιγράφεται εξ' ολοκλήρου η διακοσίων gigabyte συλλογή με mp3 που έχω, αλλά μόνο οι αλλαγές. - Αν σβήσω έναν υποκατάλογο ή αρχείο ή το μεταφέρω κάπου αλλού, θέλω να έχει την ίδια επίπτωση και στο αντίγραφο ώστε να μην καταντήσει μπάχαλο - Αν έχω ξεχάσει να κάνω mount τον usb σκληρό μου ή κατά λάθος δώσω ένα όνομα υποκαταλόγου που δεν υπάρχει, θέλω αυτό να γίνεται αντιληπτό και να προειδοποιούμαι. - Αν τροποποιήσω ένα αρχείο θέλω να γίνει το ίδιο και στο αντίγραφο του. - Έχω έναν υποκατάλογο στο σκληρό, αλλά τα αρχεία του τα δουλεύω και με flashάκι σε άλλους υπολογιστές ή με το laptop μου. Πριν το backup θέλω να συγχρονίσω το directory αυτό με το flashάκι ή το laptop και να μείνουν τα πιο καινούργια αρχεία και στα δυο. - Κάθε υποκατάλογος πρέπει να δημιουργεί ένα αντίγραφο ασφαλείας εκεί ακριβώς που θα του πω εγώ κι όχι όλα χύμα στο '/' ενός σκληρού. - Να γίνονται όλες οι διαδικασίες με ασφάλεια - προστατεύοντάς τα original αρχεία μου αλλά και τα ήδη ευμεγέθη αντίγραφα που έχω κάνει παλιότερα.

Για να φέρουμε σε πέρας την αποστολή "backup" Θα χρησιμοποιήσουμε το ισχυρότατο εργαλείο - εντολή rsync. Περισσότερες λεπτομέρειες γι' αυτήν, θ' ακολουθήσουν παρακάτω

Τι θα χρειαστούμε:

Φυσικά το rsync. Εγκαταστήστε το πακέτο (πχ. ένα 'apt-get install rsync' αρκεί)

Και τώρα (μη τρομάξετε) ακολουθεί ο κώδικας της νέας έκδοσης του "luckyBackup.sh" script :

code format="text"
 * 1) !/bin/bash


 * 1) filename			: luckyBabkup.sh
 * 2) Date of creation		: March 2008
 * 3) Date of last modification	: 08 August 2008
 * 4) Creator			: luckyb
 * 5) modified by			: luckyb on 08 August 2008


 * 1) "luckyBuckup.sh" syncs and backups all the directories that all declared in the "user input section" below.


 * 1) Syncing means that in each of a directory couple, files that have most recently modified will be kept.
 * 2) e.g. Directory "A" contains 2 (file1, file2) files and directory "B" contains 3 files (file1, file2, file3)
 * 3) if file1 in dir "A" is modified later than file1 in dir "B" and file2 in dir "A" is older than file2 in dir "B" then
 * 4) at the end of syncing both dir "A" & dir "B" will contain the same files:
 * 5) file1 from dir "A", file2 form dir "B", file3 from dir "B"
 * 6) Syncing is done using the "sync_stuff " function


 * 1) backing up means that a source directory will create an exact image of itself in a destination directory.
 * 2) Source directory is left untouched, while destination directory is modified so as to be exactly the same as the source.
 * 3) Syncing is done using the "backup_stuff " function


 * 1) Before anything is done the script checks if it is run with root privileges with function "check_user "
 * 2) This is important because we're gonna move-copy-delete files that probably have different permissions.
 * 3) and the simple user might not authorised to handle all of them.


 * 1) Then, using the function "check_dirs ", the script checks if all the directories that were declared in the
 * 2) user input section exist or are empty.
 * 3) If one of the sync dirs does not exist the specific procedure is going to be skiped
 * 4) If one destination directory does not exist it's going to be created
 * 5) If on3 source directory is empty or doesn't exist then the procedure is going to be skipped to protect our backup files

IFS=$',' 		# set IFS to treat spaces as spaces - DO NOT touch this


 * 1) -- user input
 * 1) -- user input


 * 1) Sync -
 * 2) Please define directories/files to be synced first :

syncdirA[${#syncdirA[*]}]="/home/luckyb//UCL/diplomatiki/" syncdirB[${#syncdirB[*]}]="/media/LUCKYUSB/UCL/diplomatiki/"


 * 1) syncdirA[${#syncdirA[*]}]="path/to/2nd/syncdirA"
 * 2) syncdirB[${#syncdirB[*]}]="path/to/2nd/syncdirB/"


 * 1) Backup ---
 * 2) Please define directories to be backed up as well as the destination of the backups :

sourcedir[${#sourcedir[*]}]="/home/luckyb/mp3/" destdir[${#destdir[*]}]="/media/backups/mp3/"

sourcedir[${#sourcedir[*]}]="/home/luckyb/photos/" destdir[${#destdir[*]}]="/media/backups/photos/"

sourcedir[${#sourcedir[*]}]]="/home/luckyb/docsNstuff" destdir[${#destdir[*]}]="/media/XoraoTaPanta"


 * 1) sourcedir[${#sourcedir[*]}]="/path/to/4rth/source/dir/"
 * 2) destdir[${#destdir[*]}]="/path/to/4th/dest/dir/"


 * 1) sourcedir[x] is the source directory and destdir[x] is the destination backup directory
 * 2) 'x' is an integer starting from 0 and increasing by 1. It must be the same for the sourcedir and the destdir
 * 3) use '/' at the end of the sourcedir and destdir not to copy 'root' source but only subfolders and files


 * 1) Example -
 * 2) The following will backup /home/luckyb/mp3 to /media/backups/ - a directory /media/backups/mp3 will be Created
 * 3) The directory /home/luckyb/photos will be backed up to /media/backups/luckyb_photos - All contents of the /home/luckyb/photos directory will be backedup but no photos directory will be created in /media/backups/luckyb_photos


 * 1) sourcedir[0]="/home/luckyb/mp3"
 * 2) destdir[0]="/media/backups"
 * 3) sourcedir[1]="/home/luckyb/photos/"
 * 4) destdir[1]="/media/backups/luckyb_photos/"
 * 1) destdir[1]="/media/backups/luckyb_photos/"


 * 1) user input end---

W="[1;31m" # red 	- Warning E="[1;33m" # yellow 	- info S="[1;32m" # green 	- info 2 Q="[1;36m" # CYAN 	- User Action M="[1;34m" # BLUE 	- Info 3 C="[1;35m" # MAGENTA 	- data N="[0;39m" # default system console color: Normal
 * 1) main script (this is where all is done)
 * 2) Do not modify this part unless you really know what you are doing (You have been warned)
 * 3) color variables
 * 1) color variables
 * 1) color variables

intro {	clear echo echo "${M}			luckybackup script -  version 0.2" echo "============================================================================================" echo echo "${S}This script will (hopefully) sync and backup everything you need in no time${N}" echo "(Well, it might take a little longer if run for the first time ;-)" echo echo "${E}Make sure that:" echo "You have already declared and mounted all the directories that need to be synced/backed-up" echo "If you haven't, don't worry. I will check everything before doing something stupid !!" echo echo "Hit ${Q}'enter' to continue${E} otherwise press ${Q}'ctrl+c' to exit NOW${N}" read echo }
 * 1) intro  function
 * 2) just prints stuff on the screen
 * 1) just prints stuff on the screen

check_user {	declare -a options options[${#options[*]}]="Just continue with the script, I know what I'm doing" options[${#options[*]}]="Quit now. I'll try again as su" if [ "$USER" != "root" ] then echo echo "${S}$USER," echo "${W}Having in mind that ${C}you are not root${W}, some of the operations you have asked for, might produce errors" echo echo "${Q}What would you like me to do??${N}" select opt in "${options[@]}"; do case ${opt} in				${options[0]})					echo "${E}ok, but you have been WARNED${N}"; ;;				*) echo "${E}exiting now...${N}"; exit 0; ;; esac; break done fi }
 * 1) check_user  function
 * 2) Checks if the script is run with su privileges and asks for confirmation to go on if not
 * 1) Checks if the script is run with su privileges and asks for confirmation to go on if not

check_dirs {	ask=0			# Boolean variable. TRUE if an error occurs and user confirmation is mandatory
 * 1) check_dirs  function
 * 2) Checks if the directories declared are valid (they exist or are empty) and asks for confirmation to go on
 * 1) Checks if the directories declared are valid (they exist or are empty) and asks for confirmation to go on

clear echo "==============================================================================================================" echo "${S}			Checking if the directories you specified exist or are empty${N}" echo "==============================================================================================================" echo sleep 0.5		# a small sleep just cause I don't like things happening too fast

for ((count="0"; count <${#syncdirA[*]}; count++))	# Check sync dirs do sleep 0.2	# a small sleep just cause I don't like things happening too fast echo "--" # Do both sync directories exist and are not empty ?? if [ -d ${syncdirA[$count]} ] && [ -d ${syncdirB[$count]} ] && [ "$(ls -A ${syncdirA[$count]})" ] && [ "$(ls -A ${syncdirB[$count]})" ] then echo "[ ${M}ok${N} ]		->	Sync directory ${M}${syncdirA[$count]}${N}" echo "[ ${M}ok${N} ]		->	Sync directory ${M}${syncdirB[$count]}${N}" false

# Is one or both of the sync dirs not existent ?? elif [ ! -d ${syncdirA[$count]} ] || [ ! -d ${syncdirB[$count]} ] then echo "[ ${E}WARNING${N} ]	->	Sync directory ${E}${syncdirA[$count]}${N}" echo "				and-or ${E}${syncdirB[$count]}${N} does NOT exist" echo echo "I will ${E}skip${N} this operation" ask=1

# is one or both of the sync dirs empty ?? else echo "[ ${W}CRITICAL${N} ]	->	Sync directory ${W}${syncdirA[$count]}${N}" echo "				and-or ${E}${syncdirB[$count]}${N} is Empty" echo echo "I will ${W}NOT skip${N} this operation. The directories are going to be synced anyway !!" ask=1 fi done count=0

for ((count="0"; count <${#sourcedir[*]}; count++))	# Check source & destination directories do sleep 0.2	# a small sleep just cause I don't like things happening too fast echo "--"

# If the source directory exists and it is not empty (ok) if [ -d ${sourcedir[$count]} ] && [ "$(ls -A ${sourcedir[$count]})" ] then echo "[ ${M}ok${N} ]		->	Source directory ${M}${sourcedir[$count]}${N}" false # If the destination directory exists and it is not empty (ok) if [ -d ${destdir[$count]} ] && [ "$(ls -A ${destdir[$count]})" ] then echo "[ ${M}ok${N} ]		->	Destination directory ${M}${destdir[$count]}${N}" false

# If the destination directory does not exist or it is empty else echo "[ ${W}CRITICAL${N} ]	->	Destination directory ${W}${destdir[$count]}${N} " echo "			does NOT exist or it is empty (Check if you mount something there !!)" echo echo "I will ${W}NOT skip${N} this operation." echo "The Destination Directory will be created if it doesn't exist and filled with new backup data" echo "Beware if it is not the first time your run the script for the specific backup operation" ask=1 fi # If the source directory does not exist or it is empty else echo "[ ${E}WARNING${N} ]	->	Source directory ${E}${sourcedir[$count]}${N}" echo "			does NOT exist or it is empty (Check if you mount something there !!)" echo echo "I will ${E}skip${N} this operation to protect your backup data (if any)" ask=1 fi done echo "--"

if [ $ask == 1 ]	# if something went wrong in the previous directories checks then declare -a options options[${#options[*]}]="Just continue with the script, I know what I'm doing !!" options[${#options[*]}]="Quit now. I'll fix the problems and try again" echo echo echo "${S}It is advised that you quit now and fix any problems" echo "To keep your data safe I will take all the actions mentioned in the above Warning messages"

echo echo "${Q}What would you like me to do??${N}" select opt in "${options[@]}"; do case ${opt} in 				${options[0]})					echo "${E}ok, continuing with the script...${N}";					sleep 1; ;;				*) echo "${E}exiting now...${N}"; exit 0; ;; esac; break done

else	# if all went ok in the previous directories checks echo echo "${S}All directories valid :-)"		echo "You are ready to go${N}"		echo		echo "Hit enter to continue..."		read	fi }

sync_stuff { echo "${S} Syncing stuff --- ${N}" echo
 * 1) sync_stuff  function
 * 2) Syncs all of the directories in the syncdirA array with the directories in the syncdirB array
 * 1) Syncs all of the directories in the syncdirA array with the directories in the syncdirB array

syncno=${#syncdirA[*]}		#No. of dirs to be synced count=0				#typical count variable while [ $count -le $((syncno-1)) ] do echo "now syncing directory $((count+1)) of $syncno:" echo "${C} ${syncdirA[$count]} ${N}" echo "with" echo "${C} ${syncdirB[$count]} ${N}" echo if [ -d ${syncdirA[$count]} ] && [ -d ${syncdirB[$count]} ]	#check if both sync directories exist then #echo "Executing rsync NOW !!" rsync -h --archive --progress --stats --update ${syncdirB[$count]} ${syncdirA[$count]} rsync -h --archive --progress --stats --update ${syncdirA[$count]} ${syncdirB[$count]} echo "...done" echo else echo echo "${W}One or both directories are missing...${N}" echo "...skipping" fi count=$((count+1)) echo "--" echo done }

backup_stuff {
 * 1) backup_stuff  function
 * 2) Backs-up all of the directories in the sourcedir array to the directories in the destdir array
 * 1) Backs-up all of the directories in the sourcedir array to the directories in the destdir array

dirno=${#sourcedir[*]}		#No. of dirs to be backed up	echo "${S}--- Backing up stuff - ${N}" echo count=0 while [ $count -le $((dirno-1)) ] do echo "backing up directory $((count+1)) of $dirno:" echo "${C} ${sourcedir[$count]} ${N}" echo "to" echo "${C} ${destdir[$count]} ${N}" echo if [ -d ${sourcedir[$count]} ] && [ "$(ls -A ${sourcedir[$count]})" ]	#check if sourcedir exists or empty then #echo "Executing rsync NOW !!" rsync -h --archive --progress --stats --update --delete-after ${sourcedir[$count]} ${destdir[$count]} echo "...done" echo else echo echo "${W}${sourcedir[$count]}${N} is missing" echo "...skipping" fi count=$((count+1)) echo "--" echo done }

thats_all {	echo "==============================================================================================================" echo "${Q}				Syncing and backing up is finished" echo "				hope everything went ok ;-)${N}"	echo "==============================================================================================================" echo }
 * 1) thats_all  function
 * 2) Just prints a message to the screen
 * 1) Just prints a message to the screen

intro check_user check_dirs sync_stuff backup_stuff thats_all
 * 1) main code
 * 1) main code

unset IFS # IFS back to normal exit 0 code

Παίρνω βαθιά ανάσα και προχωράω στην ανάλυσή:

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

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

Τα μέλη των arrays αυτών περιέχουν τα ζευγάρια των υποκαταλόγων που θέλουμε να συγχρονίσουμε. Πολλές φορές κρατάμε τα ίδια αρχεία στο σταθερό pc μας και ίσως σ'ένα φλασάκι ώστε να τα επεξεργαζόμαστε είτε από το σπίτι, είτε από οποιονδήποτε Η/Υ. Είναι φρόνιμο, επειδή ποτέ δεν θυμόμαστε πότε επεξεργαστήκαμε ποιο αρχείο και από που, να συγχρονίσουμε τους υποκαταλόγους μας ώστε στο τέλος να περιέχουν και οι δυο τα πιο πρόσφατα αρχεία.
 * Α. syncdirA και syncdirB**

Δηλώνουμε λοιπόν:

code format="text" syncdirA[${#syncdirA[*]}]="/home/luckyb//UCL/diplomatiki/" syncdirB[${#syncdirB[*]}]="/media/LUCKYUSB/UCL/diplomatiki/" code

και όσα άλλα ζευγάρια θέλουμε ως επόμενα μέλη των arrays:

code format="text" syncdirA[${#syncdirA[*]}]="path/to/2nd/syncdirA" syncdirB[${#syncdirB[*]}]="path/to/2nd/syncdirB/"

syncdirA[${#syncdirA[*]}]="path/to/2nd/syncdirA" syncdirB[${#syncdirB[*]}]="path/to/2nd/syncdirB/" . . . code

ώστε να συγχρονιστούν ο υποκατάλογος "/home/luckyb//UCL/diplomatiki/" με τον "/media/LUCKYUSB/UCL/diplomatiki/" επίσης ο "/path/to/2nd/syncdirA" με τον "/path/to/2nd/syncdirB" κ.ο.κ.

και τώρα θα μου πείτε "τί είναι αυτό το:

code format="text" syncdirA[${#syncdirA[*]}] code


 * Γιατί δεν χρησιμοποιεί τη γνωστή σύνταξη syncdirA[0]=.... και syncdirA[1]=.... κλπ ;;"

Κολπάκι: Έστω ότι έχουμε ένα array με όνομα ARRAY. Το πόσα μέλη έχει το ARRAY μας το δίνει η έκφραση

code format="text" ${#ARRAY[*]} code Δηλαδή αν αρχικά η συστοιχία μας έχει μηδέν (0) μέλη γράφοντας:

code format="text" ARRAY[${#ARRAY[*]}]=Τιμή code είναι σα να γράφουμε ARRAY[0]=Τιμή. Τώρα το ARRAY έχει ένα μέλος, οπότε γράφοντας:

code format="text" ARRAY[${#ARRAY[*]}]=Τιμή code είναι σα να γράφουμε ARRAY[1]=Τιμή. Έτσι προσθέτουμε καινούργια μέλη στο ARRAY χωρίς να μας αποασχολούν τα INDEX (οι αριθμοί μέσα στις αγγύλες), τα οποία ξεκινούν από το μηδέν (0) και αυξάνονται κατά ένα (δηλ. 0,1,2,3,4,...) για κάθε νέο μέλος

Τα μέλη των arrays αυτών περιέχουν τα ονόματα των υποκαταλόγων που θα κάνουμε backup (sourcedir) και των αντίστοιχων στους οποίους θα δημιουργηθεί το backup (destdir).
 * Β. sourcedir και destdir**

Στον συγκεκριμένο κώδικα ο υποκατάλογος "/home/luckyb/mp3/" θα δημιουργήσει ένα αντίγραφο ασφαλείας στον υποκατάλογο "/media/backups/mp3/" (Το "/media/backups" μπορεί να είναι κάλλιστα το σημείο που γίνεται mount ένας εξωτερικός σκληρός δίσκος ) κ.ο.κ.

ΠΡΟΣΟΧΗ στη χρήση των καθέτων στο τέλος των υποκαταλόγων !! πχ Με την δήλωση: sourcedir[0]="/home/luckyb/mp3"  (χωρίς κάθετο στο τέλος) destdir[0]="/media/backups" θα αντιγραφεί ολόκληρος ο υποκατάλογος "mp3" από το "/home/luckyb". Στον υποκατάλογο "/media/backups" θα δημιουργηθεί ξεχωριστός υποκατάλογος "mp3"

ενώ με την δήλωση: sourcedir[0]="/home/luckyb/mp3/" (με κάθετο στο τέλος) destdir[0]="/media/backups/" δεν θα δημιουργηθεί υποκατάλογος "mp3" μέσα στον "/media/backups". Όλα τα περιεχόμενα του "/home/luckyb/mp3" θα γραφούν χύμα μέσα στον "/media/backups/"

Επίσης στις αρχικές δηλώσεις (πριν αρχίσει ο τζερτζελές !!) μεταβλητών θα δούμε τα εξής:

code format="text" W="[1;31m" # red 	- Warning E="[1;33m" # yellow 	- info S="[1;32m" # green 	- info 2 Q="[1;36m" # CYAN 	- User Action M="[1;34m" # BLUE 	- Info 3 C="[1;35m" # MAGENTA 	- data N="[0;39m" # default system console color: Normal code Οι τιμές αυτών των μεταβλητών θα δώσουν χρώμα στο άχαρο τερματικό μας.
 * 1) color variables

__...και πάμε στο ζουμί:__

Θα κατηγοριοποιήσουμε τις ξεχωριστές διεργασίες που κάνει το script ως κατωτέρω:


 * 1.** Εμφάνιση κάποιων τυπικών εισαγωγικών μηνυμάτων στην οθόνη.
 * 2.** Έλεγχος αν αυτός που τρέχει το script είναι ένας απλός χρήστης ή ο root. Επειδή υπάρχει περίπτωση κάποια από τα αρχεία που θα δημιουργήσουμε αντίγραφα ασφαλείας να έχουν ιδιότητες που δεν θα επιτρέψουν την αντιγραφή τους (με διατήρηση των ιδιοτήτων αυτών) από έναν απλό χρήστη, είναι σοφό να εκτελούμε το script ως υπερχρήστης (su). Επίσης θα μας δίνεται τη δυνατότητα να συνεχίσουμε ή όχι αν είμαστε απλός χρήστης.
 * 3.** Έλεγχος αν οι υποκατάλογοι που δηλώσαμε προηγουμένως υπάρχουν ή αν είναι κενοί (για ν'αποφύγουμε τις πατάτες !!. Φανταστείτε να έχετε μια συλλογή μουσικής 200GB σε εξωτερικό σκληρό και να έχετε δημιουργήσει ήδη ένα backup σε έναν δίσκο. Αν έχετε ξεχάσει να κάνετε mount τον δίσκο με τα mp3, τότε θα σβηστεί και το backup του !!). Επίσης θα μας δίνεται την επιλογή αν υπάρχουν λάθη να μη συνεχίσουμε.
 * 4.** Συγχρονισμός των υποκαταλόγων που δηλώσαμε (μόνο αν υπάρχουν και οι δυο).
 * 5.** Δημιουργία backup των υποκαταλόγων που δηλώσαμε (μόνο αν υπάρχουν οι source υποκατάλογοι και δεν είναι άδειοι).
 * 6.** Εμφάνιση ενός μηνύματος ότι όλα πήγαν καλά (μάλλον )

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

intro check_user check_dirs sync_stuff backup_stuff thats_all


 * 1. intro function**

Η μια από τις δυο πιο απλές συναρτήσεις μας. Απλά με μια ακολουθία από "echo" μας εμφανίζει ένα αρχικό μύνημα στην οθόνη που λέει τι είναι αυτό που μόλις τρέξαμε και άλλες χρήσιμες (?) πληροφορίες.


 * 2. check_user function**

Αρχικά δηλώνουμε το array "options" (με δυο μέλη) το οποίο περιέχει τις επιλογές που θα μας δοθούν αν δεν είμαστε root (με την εντολή "select"):

code format="text" declare -a options options[${#options[*]}]="Just continue with the script, I know what I'm doing" options[${#options[*]}]="Quit now. I'll try again as su" code

Η μεταβλητή "$USER" είναι μεταβλητή του bash και η τιμή της είναι ίδια με το όνομα του τρέχων χρήστη. Μπορείτε να το επιβεβαιώσετε δίνοντας την εντολή:

code format="text" echo $USER code

Οπότε με τη συνθήκη "if [ "$USER" != "root" ]" (δηλαδή αν ο τρέχων χρήστης ($USER) ΔΕΝ είναι (!=) ο "root") θα εκτελέσουμε τον κώδικα που ακολουθεί μέχρι το "fi", διαφορετικά η συνάρτηση θα φτάσει στο τέλος της και το script θα συνεχίσει κανονικά.

Στον κώδικα της συνθήκης, εκτός από διάφορα μηνύματα (δεν είσαι ο root, τι πας να κάνεις εκεί;; κλπ) χρησιμοποιούμε την εντολή "select" σε συνδυασμό με την "case" για να δημιουργήσουμε ένα μενού και να δώσουμε την επιλογή στον χρήστη να συνεχίσει κανονικά ή να βγει από το script (exit (0)):

code format="text" if [ "$USER" != "root" ] then echo echo "${S}$USER," echo "${W}Having in mind that ${C}you are not root${W}, some of the operations you have asked for, might produce errors" echo echo "${Q}What would you like me to do??${N}" select opt in "${options[@]}"; do case ${opt} in				${options[0]})					echo "${E}ok, but you have been WARNED${N}"; ;;				*) echo "${E}exiting now...${N}"; exit 0; ;; esac; break done fi code


 * 3. check_dirs function**

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

Συγκεκριμένα ελέγχουμε: __Α. Αν οι υποκατάλογοι που είναι για συγχρονισμό υπάρχουν κι αν ΝΑΙ τότε αν είναι άδειοι.__

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


 * Β. Αν τόσο οι υποκατάλογοι που θα γίνουν backup (source), όσο και οι υποκατάλογοι στους οποίους θα δημιουργηθεί το backup (destination), υπάρχουν και αν είναι κενοί**

Αν κάποιος από τους destination υποκαταλόγους δεν υπάρχει ή είναι κενός, τότε θα προειδοποιήσουμε ότι θα δημιουργηθεί και/ή θα γεμίσει με δεδομένα. Αφενός αυτό είναι χρήσιμο για κάποιον που κάνει backup για πρώτη φορά γιατί επιβεβαιώνει τις επιλογές του πριν ξενινήσουν οι αντιγραφές αρχείων. Αφετέρου, φανταστείτε εκείνον που κάνει το backup του, σε έναν εξωτερικό σκληρό δίσκο τον οποίο κάνει mount στο πχ /media/backups. Αν έχει ξεχάσει να προσαρτήσει τον εξωτερικό σκληρό του, τότε το script θα δημιουργήσει κανονικά το αντίγραφο ασφαλείας στο τρέχον partition που έχουμε τη διανομή μας, στον υποκατάλογο /media/backups. ...και άντε να βγάλεις άκρη μετά !!

Αν κάποιος από τους source υποκαταλόγους δεν υπάρχει ή είναι κενός, τότε θα αναφέρουμε ότι η συγκεκριμένη διεργασία δεν θα πραγματοποιηθεί. Καταρχήν είναι τελείως άσκοπο να κάνουμε backup σ' έναν ανύπαρκτο ή κενό υποκατάλογο Ο βασικός σκοπός όμως αυτού του ελέγχου είναι να προστατέψουμε τα ήδη ευμεγέθη αντίγραφα ασφαλείας που πιθανόν να υπάρχουν. Φανατστείτε ο source υποκατάλογος να είναι η 200GB συλλογή μας με μουσική και να βρίσκεται σε κάποιον αφαιρούμενο δίσκο. Αν έχουμε πάρει ήδη μια φορά αντίγραφο ασφαλείας (παίρνει μια-δυο μέρες την πρώτη φορά ), τότε κάθε φορά που ξαναεκτελούμε τη διεργασία, θα αντιγράφονται στο backup μόνο οι αλλαγές που έχουμε κάνει. Αυτό συμπεριλαμβάνει και το σβήσιμο αρχείων αν συνέβη κάτι τέτοιο στα αυθεντικά δεδομένα (source). Αν τώρα για κάποιο λόγο ξεχάσουμε να κάνουμε mount τον σκληρό που περιέχει τα source δεδομένα μας τότε θα θέσω το εξής (ρητορικό) ερώτημα: "Τι θα συμβεί στο 200GB backup που ήδη έχουμε ;;"

__Γ. Ανάλογα αν τ' αποτελέσματα των ελέγχων (Α) & (Β) ήταν όλα επιτυχή ή όχι:__ θα δίνουμε τη δυνατότητα να σταματήσει ο χρήστης την εκτέλεση του script ή απλά θα τυπώνουμε ένα μήνυμα επιτυχίας !!

Για να δούμε πως θα γίνουν όλ'αυτά στην πράξη:

__Α. Ξεκινάμε ελέγχοντας τους υποκαταλόγους που είναι για συγχρονισμό.__

Τα βάζουμε όλα σ' ένα "for loop" το οποίο θα εκετελεστεί τόσες φορές όσα και τα μέλη της συστοιχίας "syncdirA". Χρησιμοποιούμε την μεταβλητή "count" η οποία θα παίρνει διαφορετικές τιμές για κάθε φορά που τρέχει το loop, ξεκινώντας από το μηδέν (0), θ' αυξάνεται κατά ένα (1) και θα καταλήγει με τιμή ένα μικρότερο από το σύνολο των μελών του array "syncdirA". Δηλαδή αν το "syncdirA" έχει 4 μέλη, η count θα πάρει τις τιμές 0, 1, 2 και 3.: code format="text" for ((count="0"; count <${#syncdirA[*]}; count++)) code Έτσι για κάθε φορά που θα τρέχει το loop θα χρησιμοποιούμε την count για να αναφερθούμε σε τιμές συγκεκριμένων μελών των arrays με την έκφραση "${syncdirA[$count]}" και "${syncdirB[$count]}"

Μέσα στο for loop θα ελέγξουμε με μια συνθήκη "if" αν τα μέλη των συστοιχιών syncdirA και syncdirB είναι υποκατάλογοι που υπάρχουν και δεν είναι κενοί. Για να δούμε αν είναι κενοί χρησιμοποιούμε την έξοδο της εντολής "ls" η οποία μας βγάζει ως έξοδο τα περιεχόμενα ενός υποκαταλόγου. Αν βγάλει οποιαδήποτε έξοδο τότε ο υποκατάλογος στον οποίο αναφέρεται δεν είναι κενός. Αν δεν βγάλει έξοδο ή μάλλον αν βγάλει μηδενική (ή FALSE) έξοδο τότε ο υποκατάλογος στον οποίο αναφέρεται είναι άδειος:

code format="text" if [ -d ${syncdirA[$count]} ] && [ -d ${syncdirB[$count]} ] && [ "$(ls -A ${syncdirA[$count]})" ] && [ "$(ls -A ${syncdirB[$count]})" ] code

Έχουμε λοιπόν μέσα σε ένα if, 4 συνθήκες οι οποίες πρέπει να ισχύουν όλες. Αυτό το πετυχαίνουμε με τα σύμβολα "&&" που σημαίνουν ΚΑΙ (AND). Τα αντίστοιχα σύμβολα για να πούμε "αν ισχύει αυτό Ή (OR) το άλλο (το ένα από τα δυο τέλος πάντων)" είναι τα "||" Αν λοπόν ισχύει το σύνολο των συνθηκών της if τότε θα μας βγάλει μήνυμα ότι οι υποκατάλογοι μας είναι ok:

code format="text" if [ -d ${syncdirA[$count]} ] && [ -d ${syncdirB[$count]} ] && [ "$(ls -A ${syncdirA[$count]})" ] && [ "$(ls -A ${syncdirB[$count]})" ] then echo "[ ${M}ok${N} ]		->	Sync directory ${M}${syncdirA[$count]}${N}" echo "[ ${M}ok${N} ]		->	Sync directory ${M}${syncdirB[$count]}${N}" code

Αν τώρα δεν ισχύει έστω και μια από τις ανωτέρω συνθήκες αρχίζουμε τις αλχημείες. Με μια elif ελέγχουμε αν ένα από τα syncdirA[xx] και syncdirB[xx] δεν (προσέξτε το θαυμαστικό !) υπάρχουν. Αν όντως ένα από τα δυο δεν υπάρχει τότε θα μας προειδοποιεί ότι δεν θα εκτελεστεί αυτή η διεργασία: code format="text" elif [ ! -d ${syncdirA[$count]} ] || [ ! -d ${syncdirB[$count]} ] then echo "[ ${E}WARNING${N} ]	->	Sync directory ${E}${syncdirA[$count]}${N}" echo "				and-or ${E}${syncdirB[$count]}${N} does NOT exist" echo echo "I will ${E}skip${N} this operation" ask=1 code

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

code format="text" else echo "[ ${W}CRITICAL${N} ]	->	Sync directory ${W}${syncdirA[$count]}${N}" echo "				and-or ${E}${syncdirB[$count]}${N} is Empty" echo echo "I will ${W}NOT skip${N} this operation. The directories are going to be synced anyway !!" ask=1

code

Προσέξτε ότι όταν έχουμε ένα μήνυμα λάθους τότε θέτουμε την τιμή της μεταβλητής ask=1. Αυτή θα μας χρειαστεί αργότερα για να προσδιορίσουμε το αν θα πρέπει να ρωτήσουμε το χρήστη αν θέλει να συνεχίσει την εκτέλεση του script ή όχι.

__Β. Συνεχίζουμε ελέγχοντας τους υποκαταλόγους που θα γίνουν backup και τους προορισμούς τους.__

Όμοια με πριν τα βάζουμε όλα σ' ένα for loop χρησιμοποιώντας τη μεταβλητή count η οποία παίρνει τιμές από μηδέν (0) έως το σύνολο των μελών της συστοιχίας "sourcedir[xx]" ελαττωμένη κατά ένα (1): code format="text" for ((count="0"; count <${#sourcedir[*]}; count++)) code

Μέσα στο loop λοιπόν αρχίζουμε τα "αν" για να βγάλουμε κάτι χρήσιμο. Παραθέτω τον ακόλουθο εικονικό κώδικο για καλύτερη κατανόηση: code format="text" code
 * Για κάθε count με τιμή από 0 έως και το πλήθος του array "sourcedir[xx]" ελαττωμένο κατά ένα
 * κάνε τα κατωτέρω:
 * 1->	|Έλεγξε το sourcedir[$count]. ΑΝ υπάρχει ΚΑΙ  δεν είναι άδειο
 * |ΤΟΤΕ:
 * |1->	Εμφάνισε το μήνυμα "Το sourcedir[$count] είναι ok"
 * |2->	|Έλεγξε το destdir[$count]. ΑΝ υπάρχει ΚΑΙ δεν είναι άδειο
 * |	|TOTE:
 * |	|1->	Εμφάνισε το μήνυμα "Το destdir[$count] είναι ok"
 * |	|ΑΛΛΙΩΣ:
 * |	|1->	Εμφάνισε το μήνυμα ότι το destdir έχει πρόβλημα, αλλά η διεργασία θα γίνει κανονικά
 * |	|2->	Θέσε στη μεταβλητή ask την τιμή "1"
 * |ΑΛΛΙΩΣ:
 * |1->	Εμφάνισε το μήνυμα ότι το sourcedir έχει πρόβλημα και η διεργασία δεν θα γίνει
 * |2->	Θέσε στη μεταβλητή ask την τιμή "1"
 * |2->	Θέσε στη μεταβλητή ask την τιμή "1"

και τον κανονικό κώδικα για σύγκριση: code format="text" for ((count="0"; count <${#sourcedir[*]}; count++))	# Check source & destination directories do sleep 0.2	# a small sleep just cause I don't like things happening too fast echo "--"

# If the source directory exists and it is not empty (ok) if [ -d ${sourcedir[$count]} ] && [ "$(ls -A ${sourcedir[$count]})" ] then echo "[ ${M}ok${N} ]		->	Source directory ${M}${sourcedir[$count]}${N}" false # If the destination directory exists and it is not empty (ok) if [ -d ${destdir[$count]} ] && [ "$(ls -A ${destdir[$count]})" ] then echo "[ ${M}ok${N} ]		->	Destination directory ${M}${destdir[$count]}${N}" false

# If the destination directory does not exist or it is empty else echo "[ ${W}CRITICAL${N} ]	->	Destination directory ${W}${destdir[$count]}${N} " echo "			does NOT exist or it is empty (Check if you mount something there !!)" echo echo "I will ${W}NOT skip${N} this operation." echo "The Destination Directory will be created if it doesn't exist and filled with new backup data" echo "Beware if it is not the first time your run the script for the specific backup operation" ask=1 fi # If the source directory does not exist or it is empty else echo "[ ${E}WARNING${N} ]	->	Source directory ${E}${sourcedir[$count]}${N}" echo "			does NOT exist or it is empty (Check if you mount something there !!)" echo echo "I will ${E}skip${N} this operation to protect your backup data (if any)" ask=1 fi done code

__Γ. ...και τλειώνουμε με την εμφάνιση ενός μενού επιλογών του στυλ "συνεχίζουμε ή όχι ??"__

Αν ισχύει λοιπόν η συνθήκη [ $ask == 1] (το οποίο σημαίνει ότι κάτι πήγε στραβά στους ελέγχους υποκαταλόγων και η ask πήρε την τιμή "1") τότε με τον γνωστό τρόπο (select και case) δημιουργούμε ένα μενού που θα μας ρωτάει αν θέλουμε να συνεχίσουμε ή όχι την εκτέλεση του script. Αν δεν ισχύει το [ $ask == 1] τότε απλά θα μας εμφανίζει ένα μήνυμα ότι όλοι οι έλεγχοι είναι ok και θα μας παροτρύνει να πατήσουμε ένα πλήκτρο για να γίνουν επιτέλους οι διεργασίες συγχρονισμού και backup: code format="text" if [ $ask == 1 ]	# if something went wrong in the previous directories checks then declare -a options options[${#options[*]}]="Just continue with the script, I know what I'm doing !!" options[${#options[*]}]="Quit now. I'll fix the problems and try again" echo echo echo "${S}It is advised that you quit now and fix any problems" echo "To keep your data safe I will take all the actions mentioned in the above Warning messages"

echo echo "${Q}What would you like me to do??${N}" select opt in "${options[@]}"; do case ${opt} in 				${options[0]})					echo "${E}ok, continuing with the script...${N}";					sleep 1; ;;				*) echo "${E}exiting now...${N}"; exit 0; ;; esac; break done

else	# if all went ok in the previous directories checks echo echo "${S}All directories valid :-)"		echo "You are ready to go${N}"		echo		echo "Hit enter to continue..."		read	fi code


 * 4. sync_stuff function**

Τέρμα με τα μπλαμπλα και τις ερωτήσεις. Ήρθε επιτέλους η ώρα να κάνουμε αυτό για το οποίο ξεκινήσαμε το γράψιμο αυτού του scipt. ...και ξεκινάμε με τον συγχρονισμό υποκαταλόγων.

Τα Βάζουμε λοιπόν όλα σ' ένα while loop με παρόμοιο τρόπο με τα προαναφερθέντα for loops. Δηλαδή εκτελούμε κάτι τόσες φορές όσες και το σύνολο των μελών ενός array. Επίσης χρησιμοποιούμε τη μεταβλητή count η οποία θα παίρνει τιμές από 0 έως ... τα γνωστά. Τώρα θα μου πείτε "και γιατί δεν χρησιμοποιούμε το for, που το μάθαμε κι όλας;;". Έτσι για ποικιλία :p : code format="text" count=0 while [ $count -le $((syncno-1)) ] code

Μέσα στο loop (μετά από λίγο ακόμα μπλα μπλα), βάζουμε τη συνθήκη "ΑΝ τα δυο μέλη των array syncdirA και syncdirB είναι υποκατάλογοι, και υπάρχουν" και αν ναι, τότε εκτελούμε διαδοχικά δυο όμοιες εντολές "rsync". Αν όχι, παραλείπουμε αυτή τη διεργασία μ' ένα μήνυμα: code format="text" syncno=${#syncdirA[*]}		#No. of dirs to be synced count=0				#typical count variable while [ $count -le $((syncno-1)) ] do echo "now syncing directory $((count+1)) of $syncno:" echo "${C} ${syncdirA[$count]} ${N}" echo "with" echo "${C} ${syncdirB[$count]} ${N}" echo if [ -d ${syncdirA[$count]} ] && [ -d ${syncdirB[$count]} ]	#check if both sync directories exist then #echo "Executing rsync NOW !!" rsync -h --archive --progress --stats --update ${syncdirB[$count]} ${syncdirA[$count]} rsync -h --archive --progress --stats --update ${syncdirA[$count]} ${syncdirB[$count]} echo "...done" echo else echo echo "${W}One or both directories are missing...${N}" echo "...skipping" fi count=$((count+1)) echo "--" echo done code

Αν και δεν είναι σκοπός του tutorial τούτου να εξηγούμε τις εντολές bash, αλλά πως τις χρησιμοποιούμε για να γράψουμε script, θα δώσω λίγη έκταση στη λειτουργία και τις επιλογές της "rsync". Η εντολή "rsync" λοιπόν είναι ένα πολύ ισχυρό εργαλείο για ...ν' αντιγράφουμε αρχεία !! Η δύναμη της βρίσκεται στα options που μπορούμε να χρησιμοποιήσουμε

η rsync λοιπόν ακολουθεί την παρακάτω σύνταξη: code format="text" rsync [options] [source] [destination] code

Για περισσότερες πληροφορίες δείτε τη σελίδα man

code format="text" man rsync code

ή σε konqueror που βολεύει: man:rsync

Όπου source είναι ο υποκατάλογος που θέλουμε ν' αντιγράψουμε. Destination είναι ο υποκατάλογος στον οποίο θέλουμε ν' αντιγραφεί ο source. Options είναι διάφορες επιλογές που τελικά θα κάνουν τη δουλειά έτσι ακριβώς όπως τη θέλουμε. Συγκεκριμένα (στη συνάρτηση sync_stuff) θα χρησιμοποιήσουμε τα:

-h --archive --progress --stats --update

Τα οποία σημαίνουν : --archive:  Μπες και στους υποκαταλόγους του source directory, αντέγραψε τα symlinks ως symlinks, διατήρησε τις ιδιότητες, τους χρόνους τελευταίας επεξεργασίας, τον κάτοχο και το γκρουπ όλων των αρχείων -- update:  Αν ένα αρχείο στον destination υποκατάλογο είναι πιο καινούργιο από το αντίστοιχο στον source υποκατάλογο, τότε μην εκτελέσεις τη μεταφορά --progress & --stats:  Αυτά μας δείχνουν πολλές πληροφορίες κατά τη μεταφορά ώστε να έχουμε κάτι να κοιτάμε και να μη βαριόμαστε !!

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


 * 5. backup_stuff function**

Δεν θα πούμε πολλά εδώ. Ακολουθείται παρόμοια τεχνική με την sync_stuff, δηλαδή χρήση ενός while loop και μιας συνθήκης για να επιβεβαιώσουμε ότι υπάρχουν τα source directories και δεν είναι άδεια. Το ξέρω, δεν είναι και πολύ καλό να ειπωθεί κάτι τέτοιο σ' ένα tutorial, αλλά "σας δίνω τον κώδικα ξερό για να βγάλετε άκρη μόνοι σας" :

code format="text" count=0 while [ $count -le $((dirno-1)) ] do echo "backing up directory $((count+1)) of $dirno:" echo "${C} ${sourcedir[$count]} ${N}" echo "to" echo "${C} ${destdir[$count]} ${N}" echo if [ -d ${sourcedir[$count]} ] && [ "$(ls -A ${sourcedir[$count]})" ]	#check if sourcedir exists or empty then #echo "Executing rsync NOW !!" rsync -h --archive --progress --stats --update --delete-after ${sourcedir[$count]} ${destdir[$count]} echo "...done" echo else echo echo "${W}${sourcedir[$count]}${N} is missing" echo "...skipping" fi count=$((count+1)) echo "--" echo done code

Βασική διαφορά στις παραμέτρους της "rsync" σε σχέση με την sync_stuff είναι το "--delete-after". Αυτό σημαίνει "σβήσε όλα τα αρχεία που δεν υπάρχουν πια στον υποκατάλογο source, από τον υποκατάλογο destination, αφού τελειώσει η απαραίτητη μεταφορά"

tip: Μπορούμε να πειραματιστούμε κατά βούληση με το script έχοντας την ασφάλεια ότι δεν θα εκτελείται ποτέ καμία εντολή rsync. Απλά κάντε τις κατωτέρω μετατροπές. Στη συνάρτηση "sync_stuff" αλλάξτε τις γραμμές από: code format="text" rsync -h --archive --progress --stats --update ${syncdirB[$count]} ${syncdirA[$count]} rsync -h --archive --progress --stats --update ${syncdirA[$count]} ${syncdirB[$count]} code σε code format="text" echo "Executing rsync NOW !!" rsync -h --archive --progress --stats --update ${syncdirB[$count]} ${syncdirA[$count]} rsync -h --archive --progress --stats --update ${syncdirA[$count]} ${syncdirB[$count]} code
 * 1) echo "Executing rsync NOW !!"

και στη συνάρτηση "backup_stuff" αλλάξτε τις γραμμές από: code format="text" rsync -h --archive --progress --stats --update ${syncdirB[$count]} ${syncdirA[$count]} rsync -h --archive --progress --stats --update ${syncdirA[$count]} ${syncdirB[$count]} code σε code format="text" echo "Executing rsync NOW !!" code
 * 1) echo "Executing rsync NOW !!"
 * 1) rsync -h --archive --progress --stats --update ${syncdirB[$count]} ${syncdirA[$count]}
 * 2) rsync -h --archive --progress --stats --update ${syncdirA[$count]} ${syncdirB[$count]}


 * 6. thats_all function**

Πληροφοριακά : Για να λέμε και του στραβού το δίκιο, αν οι διεργασίες (1), (2), (3) και (6) δεν υπήρχαν καθόλου, δεν τρέχει και τίποτα. Θα μπορούσαμε να έχουμε μόνο τις διεργασίες (4) και (5) και να γλιτώσουμε πόσο κώδικα. Απλά προτιμώ ένα πρόγραμμα να δίνει συνεχώς πληροφορίες και δυνατότητες επιλογής στο χρήστη. Είναι αφενός πιο όμορφο και αφετέρου δίνει το συναίσθημα του ελέγχου και της ασφάλειας. Φανταστείτε κάτι να έτρεχε απλά και να περιμέναμε πόση ώρα χωρίς να έχουμε ιδέα τι γίνεται !! Αρχίζουν οι φιλοσοφικές αναζητήσεις του στυλ "Τι κάνει τώρα, κόλησε ??", "μήπως μου σβήνει τη συλλογή μου που κατέβαζα δέκα χρόνια τώρα !!!", "ελπίζω να έγραψα σωστά εκείνα τα destdir-κάτι που μου είπανε γιατί αλλιώς ..." κλπ κλπ

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

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

Φυσικά και μπορείτε να ρωτήσετε οτιδήποτε θέλετε (μη φανταστείτε ότι είμαι και ο guru του bash, αλλά θα βγάλουμε άκρη) ή να κάνετε τις παρατηρήσεις σας για να βελτιώσουμε όσο γίνεται αυτό το tutorial.

Σας εύχομαι καλό scripting luckyb

-- Πίνακας περιεχομένων Προηγούμενο (Συναρτήσεις)