Ces scripts, bien que ne rentrant pas dans le texte de ce document, illustrent quelques techniques intéressantes de programmation shell. Ils sont aussi utiles. Amusez-vous à les analyser et à les lancer.
Exemple A-1. manview: Visualiser des pages man formatées
#!/bin/bash # manview.sh: Formatte la source d'une page man pour une visualisation. # Ceci est utile lors de l'écriture de la source d'une page man et que vous #+ voulez voir les résultats intermédiaires lors de votre travail. E_MAUVAISARGS=65 if [ -z "$1" ] then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi groff -Tascii -man $1 | less # De la page man de groff. # Si la page man inclut des tables et/ou des équations, # alors le code ci-dessus échouera. # La ligne suivant peut gérer de tels cas. # # gtbl < "$1" | geqn -Tlatin1 | groff -Tlatin1 -mtty-char -man # # Merci, S.C. exit 0 |
Exemple A-2. mailformat: Formater un courrier électronique
#!/bin/bash # mail-format.sh: Formatte les courriers électroniques. # Supprime les caractères '>', les tabulations et coupe les lignes #+ excessivement longues. # ================================================================= # Vérification standard des argument(s) du script ARGS=1 E_MAUVAISARGS=65 E_PASDEFICHIER=66 if [ $# -ne $ARGS ] # Le bon nombre d'arguments a-t'il été passé au script? then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi if [ -f "$1" ] # Vérifie si le fichier existe. then nomfichier=$1 else echo "Le fichier \"$1\" n'existe pas." exit $E_PASDEFICHIER fi # ================================================================= LONGUEUR_MAX=70 # Longueur à partir de laquelle on coupe les lignes. # Suppression du caractère '>', des tabulations en début de ligne #+ et coupage des lignes après $LONGUEUR_MAX caractères. sed ' s/^>// s/^ *>// s/^ *// s/ *// ' $1 | fold -s --width=$LONGUEUR_MAX # L'option -s de "fold" coupe les lignes à un espace blanc, si #+ possible. # Ce script a été inspiré par un article d'un journal bien connu #+ proposant un utilitaire Windows de 164Ko pour les mêmes fonctionnalités. # # Un joli ensemble d'utilitaire de manipulation de texte et un langage de #+ scripts efficace apportent une alternative à des exécutables gonflés. exit 0 |
Exemple A-3. rn: Un utilitaire simple pour renommer des fichiers
Ce script est une modification de l'Exemple 12-15.
#! /bin/bash # # Un très simplifié "renommeur" de fichiers (basé sur "lowercase.sh"). # # L'utilitaire "ren", par Vladimir Lanin (lanin@csd2.nyu.edu), #+ fait un bien meilleur travail que ceci. ARGS=2 E_MAUVAISARGS=65 UN=1 # Pour avoir correctement singulier ou pluriel # (voir plus bas.) if [ $# -ne "$ARGS" ] then echo "Usage: `basename $0` ancien-modele nouveau-modele" # Comme avec "rn gif jpg", qui renomme tous les fichiers gif du répertoire #+ courant en jpg. exit $E_MAUVAISARGS fi nombre=0 # Garde la trace du nombre de fichiers renommés. for fichier in *$1* # Vérifie tous les fichiers correspondant du répertoire. do if [ -f "$fichier" ] # If finds match... then fname=`basename $fichier` # Supprime le chemin. n=`echo $fname | sed -e "s/$1/$2/"` # Substitue nouveau par ancien dans # le fichier. mv $fname $n # Renomme. let "nombre += 1" fi done if [ "$nombre" -eq "$UN" ] # Pour une bonne grammaire. then echo "$nombre fichier renommé." else echo "$nombre fichiers renommés." fi exit 0 # Exercices: # --------- # Avec quel type de fichiers cela ne fonctionnera pas? # Comment corriger cela? # # Réécrire ce script pour travailler sur tous les fichiers d'un répertoire, #+ contenant des espaces dans leur noms, et en les renommant après avoir #+ substitué chaque espace par un tiret bas. |
Exemple A-4. blank-rename: Renommer les fichiers dont le nom contient des espaces
C'est une version encore plus simple du script précédent.
#! /bin/bash # blank-rename.sh # # Substitue les tirets soulignés par des blancs dans tous les fichiers d'un #+ répertoire. UN=1 # Pour obtenir le singulier/puriel correctement (voir # plus bas). nombre=0 # Garde trace du nombre de fichiers renommés. TROUVE=0 # Valeur de retour en cas de succès. for fichier in * #Traverse tous les fichiers du répertoire. do echo "$fichier" | grep -q " " # Vérifie si le nom du fichier if [ $? -eq $TROUVE ] #+ contient des espace(s). then nomf=$fichier # Supprime le chemin. n=`echo $nomf | sed -e "s/ /_/g"` # Remplace l'espace par un tiret. mv "$nomf" "$n" # Réalise le renommage. let "nombre += 1" fi done if [ "$nombre" -eq "$UN" ] # Pour une bonne grammaire. then echo "$nombre fichiers renommés." else echo "$nombre fichiers renommés." fi exit 0 |
Exemple A-5. encryptedpw: Charger un fichier sur un site ftp, en utilisant un mot de passe crypté en local
#!/bin/bash # Exemple "ex72.sh" modifié pour utiliser les mots de passe cryptés. # Notez que c'est toujours peu sécurisé, car le mot de passe décrypté est #+ envoyé en clair. # Utilisez quelque chose comme "ssh" si cela vous préoccupe. E_MAUVAISARGS=65 if [ -z "$1" ] then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi NomUtilisateur=bozo # Changez suivant vos besoins. motpasse=/home/bozo/secret/fichier_avec_mot_de_passe_crypte. # Le fichier contient un mot de passe crypté. Nomfichier=`basename $1` # Supprime le chemin du fichier Serveur="XXX" # Changez le nom du serveur et du répertoire suivant Repertoire="YYY" #+ vos besoins. MotDePasse=`cruft <$motpasse` # Décrypte le mot de passe. # Utilise la paquetage de cryptage de fichier de l'auteur, #+ basé sur l'algorythme classique "onetime pad", #+ et disponible à partir de: #+ Site primaire: ftp://metalab.unc.edu /pub/Linux/utils/file #+ cruft-0.2.tar.gz [16k] ftp -n $Serveur <<Fin-de-Session user $NomUtilisateur $MotDePasse binary bell cd $Repertoire put $Nomfichier bye Fin-de-Session # L'option -n de "ftp" désactive la connexion automatique. # "bell" fait sonner une cloche après chaque transfert. exit 0 |
Exemple A-6. copy-cd: Copier un CD de données
#!/bin/bash # copy-cd.sh: copier un CD de données CDROM=/dev/cdrom # périphérique CD ROM OF=/home/bozo/projects/cdimage.iso # fichier de sortie # /xxxx/xxxxxxx/ A modifier suivant votre système. TAILLEBLOC=2048 VITESSE=2 # May use higher speed if supported. echo; echo "Insérez le CD source, mais ne le montez *pas*." echo "Appuyez sur ENTER lorsque vous êtes prêt. " read pret # Attendre une entrée, $pret n'est # pas utilisé. echo; echo "Copie du CD source vers $OF." echo "Ceci peut prendre du temps. Soyez patient." dd if=$CDROM of=$OF bs=$TAILLEBLOC # Copie brute du périphérique. echo; echo "Supprimer le CD de données." echo "Insérez un CDR vierge." echo "Appuyez sur ENTER lorsque vous êtes prêt. " read pret # Attendre une entrée, $pret n'est # pas utilisé. echo "Copie de $OF vers CDR." cdrecord -v -isosize speed=$VITESSE dev=0,0 $OF # Utilise le paquetage "cdrecord" de Joerg Schilling's (voir sa docs). # http://www.fokus.gmd.de/nthp/employees/schilling/cdrecord.html echo; echo "Copie terminée de $OF vers un CDR du périphérique $CDROM." echo "Voulez-vous écraser le fichier image (o/n)? " # Probablement un fichier # immense. read reponse case "$reponse" in [oO]) rm -f $OF echo "$OF supprimé." ;; *) echo "$OF non supprimé.";; esac echo # Exercice: # Modifiez l'instruction "case" pour aussi accepter "oui" et "Oui" comme #+ entrée. exit 0 |
Exemple A-7. collatz: Séries de Collatz
#!/bin/bash # collatz.sh # Le célèbre "hailstone" ou la série de Collatz. # ---------------------------------------------- # 1) Obtenir un entier "de recherche" à partir de la ligne de commande. # 2) NOMBRE <--- seed # 3) Afficher NOMBRE. # 4) Si NOMBRE est pair, divisez par 2, ou # 5)+ si impair, multiplier par 3 et ajouter 1. # 6) NOMBRE <--- résultat # 7) Boucler à l'étape 3 (pour un nombre spécifié d'itérations). # # La théorie est que chaque séquence, quelle soit la valeur initiale, #+ se stabilisera éventuellement en répétant des cycles "4,2,1...", #+ même après avoir fluctuée à travers un grand nombre de valeurs. # # C'est une instance d'une "itération", une opération qui remplit son #+ entrée par sa sortie. # Quelque fois, le résultat est une série "chaotique". MAX_ITERATIONS=200 # Pour une grande échelle de nombre (>32000), augmenter MAX_ITERATIONS. h=${1:-$$} # Nombre de recherche # Utiliser $PID comme nombre de recherche, #+ si il n'est pas spécifié en argument de la #+ ligne de commande. echo echo "C($h) --- $MAX_ITERATIONS Iterations" echo for ((i=1; i<=MAX_ITERATIONS; i++)) do echo -n "$h " # ^^^^^ # tab let "reste = h % 2" if [ "$reste" -eq 0 ] # Pair? then let "h /= 2" # Divise par 2. else let "h = h*3 + 1" # Multiplie par 3 et ajoute 1. fi COLONNES=10 # Sortie avec 10 valeurs par ligne. let "retour_ligne = i % $COLONNES" if [ "$retour_ligne" -eq 0 ] then echo fi done echo # Pour plus d'informations sur cette fonction mathématique, #+ voir "Computers, Pattern, Chaos, and Beauty", par Pickover, p. 185 ff., #+ comme listé dans la bibliographie. exit 0 |
Exemple A-8. days-between: Calculer le nombre de jours entre deux dates
#!/bin/bash # days-between.sh: Nombre de jours entre deux dates. # Usage: ./days-between.sh [M]M/[D]D/AAAA [M]M/[D]D/AAAA ARGS=2 # Deux arguments attendus en ligne de commande. E_PARAM_ERR=65 # Erreur de paramètres. ANNEEREF=1600 # Année de référence. SIECLE=100 JEA=365 AJUST_DIY=367 # Ajusté pour l'année bisextile + fraction. MEA=12 JEM=31 CYCLE=4 MAXRETVAL=256 # Valeur de retour positive la plus grande possible renvoyée # par une fonction. diff= # Déclaration d'une variable globale pour la différence de date. value= # Déclaration d'une variable globale pour la valeur absolue. jour= # Déclaration de globales pour jour, mois, année. mois= annee= Erreur_Param () # Mauvais paramètres en ligne de commande. { echo "Usage: `basename $0` [M]M/[D]D/YYYY [M]M/[D]D/YYYY" echo " (la date doit être supérieure au 1/3/1600)" exit $E_PARAM_ERR } Analyse_Date () # Analyse la date à partir des paramètres en ligne de { # commande. mois=${1%%/**} jm=${1%/**} # Jour et mois. jour=${dm#*/} let "annee = `basename $1`" # Pas un nom de fichier mais fonctionne de la même façon. } verifie_date () # Vérifie la validité d'une date. { [ "$jour" -gt "$JEM" ] || [ "$mois" -gt "$MEA" ] || [ "$annee" -lt "$ANNEEREF" ] && Erreur_Param # Sort du script si mauvaise(s) valeur(s). # Utilise une "liste-ou / liste-et". # # Exercice: Implémenter une vérification de date plus rigoureuse. } supprime_zero_devant () # Il est mieux de supprimer les zéros possibles { # du jour et/ou du mois car sinon Bash va les val=${1#0} # interpréter comme des valeurs octales return $val # (POSIX.2, sect 2.9.2.1). } index_jour () # Formule de Gauss: { # Nombre de jours du 3 Jan. 1600 jusqu'à la date passée # en arguments. jour=$1 mois=$2 annee=$3 let "mois = $mois - 2" if [ "$mois" -le 0 ] then let "mois += 12" let "annee -= 1" fi let "annee -= $ANNEEREF" let "indexyr = $annee / $SIECLE" let "Jours = $JEA*$annee + $annee/$CYCLE - $indexyr + $indexyr/$CYCLE + $AJUST_DIY*$mois/$MEA + $jour - $JEM" # Pour une explication en détails de cet algorithme, voir # http://home.t-online.de/home/berndt.schwerdtfeger/cal.htm if [ "$Jours" -gt "$MAXRETVAL" ] # Si supérieur à 256, then # alors devient négatif let "dindex = 0 - $Jours" # qui pourra être renvoyé par la fonction. else let "dindex = $Jours" fi return $dindex } calcule_difference () # Différence entre les indices des jours. { let "diff = $1 - $2" # Variable globale. } abs () # Valeur absolut. { # Utilise une variable globale "valeur". if [ "$1" -lt 0 ] # Si négatif then # alors let "value = 0 - $1" # change de signe, else # sinon let "value = $1" # on le laisse. fi } if [ $# -ne "$ARGS" ] # Requiert deux arguments en ligne de commande. then Erreur_Param fi Analyse_Date $1 verifie_date $jour $mois $annee # Vérifie si la date est valide. supprime_zero_devant $jour # Supprime tous zéros devant. day=$? # sur le jour et/ou le mois. supprime_zero_devant $mois month=$? index_jour $jour $mois $annee date1=$? abs $date1 # S'assure que c'est positif en récupérant date1=$value # la valeur absolue. Analyse_Date $2 verifie_date $jour $mois $annee supprime_zero_devant $jour day=$? supprime_zero_devant $mois month=$? index_jour $jour $mois $annee date2=$? abs $date2 # S'assure que c'est positif. date2=$value calcule_difference $date1 $date2 abs $diff # S'assure que c'est positif. diff=$value echo $diff exit 0 # Comparez ce script avec l'implémentation de la formule de Gauss en C sur # http://buschencrew.hypermart.net/software/datedif |
Exemple A-9. makedict: Créer un << dictionnaire >>
#!/bin/bash # makedict.sh [make dictionary] # Modification du script /usr/sbin/mkdict. # Script original copyright 1993, par Alec Muffett. # # Ce script modifié inclus dans ce document d'une manière consistente avec le #+ document "LICENSE" du paquetage "Crack" dont fait partie le script original. # Ce script manipule des fichiers texte pour produire une liste triée de mots #+ trouvés dans les fichiers. # Ceci pourrait être utile pour compiler les dictionnaires et pour des #+ recherches lexicographique. E_MAUVAISARGS=65 if [ ! -r "$1" ] # Au moins un argument, qui doit être then #+ un fichier valide. echo "Usage: $0 fichiers-à-manipuler" exit $E_MAUVAISARGS fi # SORT="sort" # Plus nécessaire de définir des options #+ pour sort. Modification du script #+ original. cat $* | # Contenu des fichiers spécifiés vers stdout. tr A-Z a-z | # Convertion en majuscule. tr ' ' '\012' | # Nouveau: modification des espaces en #+ retours chariot. # tr -cd '\012[a-z][0-9]' | # Suppression de tout ce qui n'est pas # alphanumérique #+ (script original). tr -c '\012a-z' '\012' | # Plutôt que de supprimer #+ modification des non alpha en retours #+ chariot. sort | # Les options $SORT ne sont plus #+ nécessaires maintenant. uniq | # Suppression des mots dupliqués. grep -v '^#' | # Suppression des lignes commençant avec #+ le symbole '#'. grep -v '^$' # Suppression des lignes blanches. exit 0 |
Exemple A-10. soundex: Conversion phonétique
#!/bin/bash # soundex.sh: Calcule le code "soundex" pour des noms # ======================================================= # Script soundex # par # Mendel Cooper # thegrendel@theriver.com # 23 Janvier 2002 # # Placé dans le domaine public. # # Une version légèrement différente de ce script est apparu dans #+ la colonne "Shell Corner" d'Ed Schaefer en juillet 2002 #+ du magazine en ligne "Unix Review", #+ http://www.unixreview.com/documents/uni1026336632258/ # ======================================================= NBARGS=1 # A besoin du nom comme argument. E_MAUVAISARGS=70 if [ $# -ne "$NBARGS" ] then echo "Usage: `basenom $0` nom" exit $E_MAUVAISARGS fi affecte_valeur () # Affecte une valeur numérique { #+ aux lettres du nom. val1=bfpv # 'b,f,p,v' = 1 val2=cgjkqsxz # 'c,g,j,k,q,s,x,z' = 2 val3=dt # etc. val4=l val5=mn val6=r # Une utilisation particulièrement intelligente de 'tr' suit. # Essayez de comprendre ce qui se passe ici. valeur=$( echo "$1" \ | tr -d wh \ | tr $val1 1 | tr $val2 2 | tr $val3 3 \ | tr $val4 4 | tr $val5 5 | tr $val6 6 \ | tr -s 123456 \ | tr -d aeiouy ) # Affecte des valeurs aux lettres. # Supprime les numéros dupliqués, sauf s'ils sont séparés par des voyelles. # Ignore les voyelles, sauf en tant que séparateurs, donc les supprime à la fin. # Ignore 'w' et 'h', même en tant que séparateurs, donc les supprime au début. # # La substitution de commande ci-dessus utilise plus de tube qu'un plombier # <g>. } nom_en_entree="$1" echo echo "Nom = $nom_en_entree" # Change tous les caractères en entrée par des minuscules. # ------------------------------------------------ nom=$( echo $nom_en_entree | tr A-Z a-z ) # ------------------------------------------------ # Au cas où cet argument est un mixe de majuscules et de minuscules. # Préfixe des codes soundex: première lettre du nom. # -------------------------------------------- pos_caract=0 # Initialise la position du caractère. prefixe0=${nom:$pos_caract:1} prefixe=`echo $prefixe0 | tr a-z A-Z` # Met en majuscule la première lettre de soundex. let "pos_caract += 1" # Aller directement au deuxième caractères. nom1=${nom:$pos_caract} # ++++++++++++++++++++++++++ Correctif Exception +++++++++++++++++++++++++++++++++ # Maintenant, nous lançons à la fois le nom en entrée et le nom décalé d'un #+ caractère vers la droite au travers de la fonction d'affectation de valeur. # Si nous obtenons la même valeur, cela signifie que les deux premiers #+ caractères du nom ont la même valeur et que l'une d'elles doit être annulée. # Néanmoins, nous avons aussi besoin de tester si la première lettre du nom est #+ une voyelle ou 'w' ou 'h', parce que sinon cela va poser problème. caract1=`echo $prefixe | tr A-Z a-z` # Première lettre du nom en minuscule. affecte_valeur $nom s1=$valeur affecte_valeur $nom1 s2=$valeur affecte_valeur $caract1 s3=$valeur s3=9$s3 # Si la première lettre du nom est une #+ voyelle ou 'w' ou 'h', #+ alors sa "valeur" sera nulle (non #+ initialisée). #+ Donc, positionnons-la à 9, une autre #+ valeur non utilisée, qui peut être #+ vérifiée. if [[ "$s1" -ne "$s2" || "$s3" -eq 9 ]] then suffixe=$s2 else suffixe=${s2:$pos_caract} fi # ++++++++++++++++++++++ fin Correctif Exception +++++++++++++++++++++++++++++++++ fin=000 # Utilisez au moins 3 zéro pour terminer. soun=$prefixe$suffixe$fin # Terminez avec des zéro. LONGUEURMAX=4 # Tronquer un maximum de 4 caractères soundex=${soun:0:$LONGUEURMAX} echo "Soundex = $soundex" echo # Le code soundex est une méthode d'indexage et de classification de noms #+ en les groupant avec ceux qui sonnent de le même façon. # Le code soundex pour un nom donné est la première lettre de ce nom, suivi par #+ un code calculé sur trois chiffres. # Des noms similaires devraient avoir les mêmes codes soundex # Exemples: # Smith et Smythe ont tous les deux le soundex "S-530" # Harrison = H-625 # Hargison = H-622 # Harriman = H-655 # Ceci fonctionne assez bien en pratique mais il existe quelques anomalies. # # # Certaines agences du gouvernement U.S. utilisent soundex, comme le font les # généalogistes. # # Pour plus d'informations, voir #+ "National Archives and Records Administration home page", #+ http://www.nara.gov/genealogy/soundex/soundex.html # Exercice: # -------- # Simplifier la section "Correctif Exception" de ce script. exit 0 |
Exemple A-11. << life: Jeu de la Vie >>
#!/bin/bash # life.sh: "Life in the Slow Lane" # ##################################################################### # # This is the Bash script version of John Conway's "Game of Life". # # "Life" is a simple implementation of cellular automata. # # --------------------------------------------------------------------- # # On a rectangular grid, let each "cell" be either "living" or "dead". # # Designate a living cell with a dot, and a dead one with a blank space.# # Begin with an arbitrarily drawn dot-and-blank grid, # #+ and let this be the starting generation, "generation 0". # # Determine each successive generation by the following rules: # # 1) Each cell has 8 neighbors, the adjoining cells # #+ left, right, top, bottom, and the 4 diagonals. # # 123 # # 4*5 # # 678 # # # # 2) A living cell with either 2 or 3 living neighbors remains alive. # # 3) A dead cell with 3 living neighbors becomes alive (a "birth"). # SURVIVE=2 # BIRTH=3 # # 4) All other cases result in dead cells. # # ##################################################################### # startfile=gen0 # Read the starting generation from the file "gen0". # Default, if no other file specified when invoking script. # if [ -n "$1" ] # Specify another "generation 0" file. then if [ -e "$1" ] # Check for existence. then startfile="$1" fi fi ALIVE1=. DEAD1=_ # Represent living and "dead" cells in the start-up file. # This script uses a 10 x 10 grid (may be increased, #+ but a large grid will will cause very slow execution). ROWS=10 COLS=10 GENERATIONS=10 # How many generations to cycle through. # Adjust this upwards, #+ if you have time on your hands. NONE_ALIVE=80 # Exit status on premature bailout, #+ if no cells left alive. TRUE=0 FALSE=1 ALIVE=0 DEAD=1 avar= # Global; holds current generation. generation=0 # Initialize generation count. # ================================================================= let "cells = $ROWS * $COLS" # How many cells. declare -a initial # Arrays containing "cells". declare -a current display () { alive=0 # How many cells "alive". # Initially zero. declare -a arr arr=( `echo "$1"` ) # Convert passed arg to array. element_count=${#arr[*]} local i local rowcheck for ((i=0; i<$element_count; i++)) do # Insert newline at end of each row. let "rowcheck = $i % ROWS" if [ "$rowcheck" -eq 0 ] then echo # Newline. echo -n " " # Indent. fi cell=${arr[i]} if [ "$cell" = . ] then let "alive += 1" fi echo -n "$cell" | sed -e 's/_/ /g' # Print out array and change underscores to spaces. done return } IsValid () # Test whether cell coordinate valid. { if [ -z "$1" -o -z "$2" ] # Mandatory arguments missing? then return $FALSE fi local row local lower_limit=0 # Disallow negative coordinate. local upper_limit local left local right let "upper_limit = $ROWS * $COLS - 1" # Total number of cells. if [ "$1" -lt "$lower_limit" -o "$1" -gt "$upper_limit" ] then return $FALSE # Out of array bounds. fi row=$2 let "left = $row * $ROWS" # Left limit. let "right = $left + $COLS - 1" # Right limit. if [ "$1" -lt "$left" -o "$1" -gt "$right" ] then return $FALSE # Beyond row boundary. fi return $TRUE # Valid coordinate. } IsAlive () # Test whether cell is alive. # Takes array, cell number, state of cell as arguments. { GetCount "$1" $2 # Get alive cell count in neighborhood. local nhbd=$? if [ "$nhbd" -eq "$BIRTH" ] # Alive in any case. then return $ALIVE fi if [ "$3" = "." -a "$nhbd" -eq "$SURVIVE" ] then # Alive only if previously alive. return $ALIVE fi return $DEAD # Default. } GetCount () # Count live cells in passed cell's neighborhood. # Two arguments needed: # $1) variable holding array # $2) cell number { local cell_number=$2 local array local top local center local bottom local r local row local i local t_top local t_cen local t_bot local count=0 local ROW_NHBD=3 array=( `echo "$1"` ) let "top = $cell_number - $COLS - 1" # Set up cell neighborhood. let "center = $cell_number - 1" let "bottom = $cell_number + $COLS - 1" let "r = $cell_number / $ROWS" for ((i=0; i<$ROW_NHBD; i++)) # Traverse from left to right. do let "t_top = $top + $i" let "t_cen = $center + $i" let "t_bot = $bottom + $i" let "row = $r" # Count center row of neighborhood. IsValid $t_cen $row # Valid cell position? if [ $? -eq "$TRUE" ] then if [ ${array[$t_cen]} = "$ALIVE1" ] # Is it alive? then # Yes? let "count += 1" # Increment count. fi fi let "row = $r - 1" # Count top row. IsValid $t_top $row if [ $? -eq "$TRUE" ] then if [ ${array[$t_top]} = "$ALIVE1" ] then let "count += 1" fi fi let "row = $r + 1" # Count bottom row. IsValid $t_bot $row if [ $? -eq "$TRUE" ] then if [ ${array[$t_bot]} = "$ALIVE1" ] then let "count += 1" fi fi done if [ ${array[$cell_number]} = "$ALIVE1" ] then let "count -= 1" # Make sure value of tested cell itself fi #+ is not counted. return $count } next_gen () # Update generation array. { local array local i=0 array=( `echo "$1"` ) # Convert passed arg to array. while [ "$i" -lt "$cells" ] do IsAlive "$1" $i ${array[$i]} # Is cell alive? if [ $? -eq "$ALIVE" ] then # If alive, then array[$i]=. #+ represent the cell as a period. else array[$i]="_" # Otherwise underscore fi #+ (which will later be converted to space). let "i += 1" done # let "generation += 1" # Increment generation count. # Set variable to pass as parameter to "display" function. avar=`echo ${array[@]}` # Convert array back to string variable. display "$avar" # Display it. echo; echo echo "Generation $generation -- $alive alive" if [ "$alive" -eq 0 ] then echo echo "Premature exit: no more cells alive!" exit $NONE_ALIVE # No point in continuing fi #+ if no live cells. } # ========================================================= # main () # Load initial array with contents of startup file. initial=( `cat "$startfile" | sed -e '/#/d' | tr -d '\n' |\ sed -e 's/\./\. /g' -e 's/_/_ /g'` ) # Delete lines containing '#' comment character. # Remove linefeeds and insert space between elements. clear # Clear screen. echo # Title echo "=======================" echo " $GENERATIONS generations" echo " of" echo "\"Life in the Slow Lane\"" echo "=======================" # -------- Display first generation. -------- Gen0=`echo ${initial[@]}` display "$Gen0" # Display only. echo; echo echo "Generation $generation -- $alive alive" # ------------------------------------------- let "generation += 1" # Increment generation count. echo # ------- Display second generation. ------- Cur=`echo ${initial[@]}` next_gen "$Cur" # Update & display. # ------------------------------------------ let "generation += 1" # Increment generation count. # ------ Main loop for displaying subsequent generations ------ while [ "$generation" -le "$GENERATIONS" ] do Cur="$avar" next_gen "$Cur" let "generation += 1" done # ============================================================== echo exit 0 # -------------------------------------------------------------- # The grid in this script has a "boundary problem". # The the top, bottom, and sides border on a void of dead cells. # Exercise: Change the script to have the grid wrap around, # + so that the left and right sides will "touch", # + as will the top and bottom. |
Exemple A-12. Fichier de données pour le << Jeu de la Vie >>
# This is an example "generation 0" start-up file for "life.sh". # -------------------------------------------------------------- # The "gen0" file is a 10 x 10 grid using a period (.) for live cells, #+ and an underscore (_) for dead ones. We cannot simply use spaces #+ for dead cells in this file because of a peculiarity in Bash arrays. # [Exercise for the reader: explain this.] # # Lines beginning with a '#' are comments, and the script ignores them. __.__..___ ___._.____ ____.___.. _._______. ____._____ ..__...___ ____._____ ___...____ __.._..___ _..___..__ |
+++
Les deux scripts suivants sont de Mark Moraes de l'Université de Toronto. Voir le fichier joint << Moraes-COPYRIGHT >> pour les permissions et restrictions.
Exemple A-13. behead: Supprimer les en-têtes des courriers électroniques et des nouvelles
#! /bin/sh # Supprime l'entête d'un message mail/news jusqu'à la première ligne vide. # Mark Moraes, Université de Toronto # ==> Ces commentaires sont ajoutés par l'auteur de ce document. if [ $# -eq 0 ]; then # ==> Si pas d'arguments en ligne de commande, alors fonctionne avec un # ==> fichier redirigé vers stdin. sed -e '1,/^$/d' -e '/^[ ]*$/d' # --> Supprime les lignes vides et les autres jusqu'à la première # --> commençant avec un espace blanc. else # ==> Si des arguments sont présents en ligne de commande, alors fonctionne avec # ==> des fichiers nommés. for i do sed -e '1,/^$/d' -e '/^[ ]*$/d' $i # --> De même. done fi # ==> Exercice: Ajouter la vérification d'erreurs et d'autres options. # ==> # ==> Notez que le petit script sed se répère à l'exception des arguments # ==> passés. # ==> Est-il intéressant de l'embarquer dans une fonction? Pourquoi? |
Exemple A-14. ftpget: Télécharger des fichiers via ftp
#! /bin/sh # $Id: ftpget.sh,v 1.2 2003/11/02 17:21:35 guillaume Exp $ # Script pour réaliser une suite d'actions avec un ftp anonyme. Généralement, # convertit une liste d'arguments de la ligne de commande en entrée vers ftp. # Simple et rapide - écrit comme compagnon de ftplist # -h spécifie l'hôte distant (par défaut prep.ai.mit.edu) # -d spécifie le répertoire distant où se déplacer - vous pouvez spécifier une # séquence d'options -d - elles seront exécutées chacune leur tour. Si les # chemins sont relatifs, assurez-vous d'avoir la bonne séquence. Attention aux # chemins relatifs, il existe bien trop de liens symboliques de nos jours. # (par défaut, le répertoire au moment de la connexion) # -v active l'option verbeux de ftp et affiche toutes les réponses du serveur # ftp # -f fichierdistant[:fichierlocal] récupère le fichier distant et le renomme en # localfile # -m modele fait un mget suivant le modèle spécifié. Rappelez-vous de mettre # entre guillemets les caractères shell. # -c fait un cd local vers le répertoire spécifié # Par exemple example, # ftpget -h expo.lcs.mit.edu -d contrib -f xplaces.shar:xplaces.sh \ # -d ../pub/R3/fixes -c ~/fixes -m 'fix*' # récupèrera xplaces.shar à partir de ~ftp/contrib sur expo.lcs.mit.edu et # l'enregistrera sous xplaces.sh dans le répertoire actuel, puis obtiendra # tous les correctifs de ~ftp/pub/R3/fixes en les plaçant sous le répertoire # ~/fixes. # De façon évidente, la séquence des options est importante, car les commandes # équivalentes sont exécutées par ftp dans le même ordre. # # Mark Moraes (moraes@csri.toronto.edu), Feb 1, 1989 # ==> Les signes inférieur et supérieur ont été modifiés par des "parens" pour # ==> éviter des soucis avec DocBook. # # ==> Ces commentaires ont été ajoutés par l'auteur de ce document. # PATH=/local/bin:/usr/ucb:/usr/bin:/bin # export PATH # ==> Les deux lignes ci-dessus faisaient parti du script original et étaient # ==> probablement inutiles FICHIER_TEMPORAIRE=/tmp/ftp.$$ # ==> Crée un fichier temporaire, en utilisant l'identifiant du processus du # ==> script ($$) pour construire le nom du fichier. SITE=`domainname`.toronto.edu # ==> 'domainname' est similaire à 'hostname' # ==> Ceci pourrait être réécrit en ajoutant un paramètre ce qui rendrait son # ==> utilisation plus générale. usage="Usage: $0 [-h hotedisrant] [-d repertoiredistant]... [-f fichierdistant:fichierlocal]... \ [-c repertoirelocal] [-m modele] [-v]" optionsftp="-i -n" verbflag= set -f # So we can use globbing in -m set x `getopt vh:d:c:m:f: $*` if [ $? != 0 ]; then echo $usage exit 65 fi shift trap 'rm -f ${FICHIER_TEMPORAIRE} ; exit' 0 1 2 3 15 echo "user anonymous ${USER-gnu}@${SITE} > ${FICHIER_TEMPORAIRE}" # ==> Ajout des guillemets (recommendé pour les echo complexes). echo binary >> ${FICHIER_TEMPORAIRE} for i in $* # ==> Analyse les arguments de la ligne de commande. do case $i in -v) verbflag=-v; echo hash >> ${FICHIER_TEMPORAIRE}; shift;; -h) hotedistant=$2; shift 2;; -d) echo cd $2 >> ${FICHIER_TEMPORAIRE}; if [ x${verbflag} != x ]; then echo pwd >> ${FICHIER_TEMPORAIRE}; fi; shift 2;; -c) echo lcd $2 >> ${FICHIER_TEMPORAIRE}; shift 2;; -m) echo mget "$2" >> ${FICHIER_TEMPORAIRE}; shift 2;; -f) f1=`expr "$2" : "\([^:]*\).*"`; f2=`expr "$2" : "[^:]*:\(.*\)"`; echo get ${f1} ${f2} >> ${FICHIER_TEMPORAIRE}; shift 2;; --) shift; break;; esac done if [ $# -ne 0 ]; then echo $usage exit 65 # ==> Modifier de l'"exit 2" pour se conformer avec le standard. fi if [ x${verbflag} != x ]; then optionsftp="${optionsftp} -v" fi if [ x${hotedistant} = x ]; then hotedistant=prep.ai.mit.edu # ==> A réécrire pour utiliser votre site ftp favori. fi echo quit >> ${FICHIER_TEMPORAIRE} # ==> Toutes les commandes sont sauvegardées dans fichier_temporaire. ftp ${optionsftp} ${hotedistant} < ${FICHIER_TEMPORAIRE} # ==> Maintenant, exécution par ftp de toutes les commandes contenues dans le # ==> fichier fichier_temporaire. rm -f ${FICHIER_TEMPORAIRE} # ==> Enfin, fichier_temporaire est supprimé (vous pouvez souhaiter le copier # ==> dans un journal). # ==> Exercices: # ==> --------- # ==> 1) Ajouter une vérification d'erreurs. # ==> 2) Ajouter des tas de trucs. |
+
Antek Sawicki a contribué avec le script suivant, qui fait une utilisation très intelligente des opérateurs de substitution de paramètres discutés dans la Section 9.3.
Exemple A-15. password: Générer des mots de passe aléatoires de 8 caractères
#!/bin/bash # Pourrait avoir besoin d'être appelé avec un #!/bin/bash2 sur les anciennes #+ machines. # # Générateur de mots de passe aléatoires pour bash 2.x #+ par Antek Sawicki <tenox@tenox.tc>, # qui a généreusement permis à l'auteur de ce document de l'utiliser ici. # # ==> Commentaires ajoutés par l'auteur du document ==> MATRICE="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" LONGUEUR="8" # ==> Modification possible de 'LONGUEUR' pour des mots de passe plus longs. while [ "${n:=1}" -le "$LONGUEUR" ] # ==> Rappelez-vous que := est l'opérateur de "substitution par défaut". # ==> Donc, si 'n' n'a pas été initialisé, l'initialisez à 1. do PASS="$PASS${MATRICE:$(($RANDOM%${#MATRICE})):1}" # ==> Très intelligent, pratiquement trop astucieux. # ==> Commençons par le plus intégré... # ==> ${#MATRICE} renvoie la longueur du tableau MATRICE. # ==> $RANDOM%${#MATRICE} renvoie un nombre aléatoire entre 1 et la # ==> longueur de MATRICE - 1. # ==> ${MATRICE:$(($RANDOM%${#MATRICE})):1} # ==> renvoie l'expansion de MATRICE à une position aléatoire, par # ==> longueur 1. # ==> Voir la substitution de paramètres {var:pos:len}, section 3.3.1 # ==> et les exemples suivants. # ==> PASS=... copie simplement ce résultat dans PASS (concaténation). # ==> Pour mieux visualiser ceci, décommentez la ligne suivante # ==> echo "$PASS" # ==> pour voir la construction de PASS, un caractère à la fois, # ==> à chaque itération de la boucle. let n+=1 # ==> Incrémentez 'n' pour le prochain tour. done echo "$PASS" # ==> Ou, redirigez le fichier, comme voulu. exit 0 |
+
James R. Van Zandt a contribué avec ce script, qui utilise les tubes nommés et, ce sont ses mots, << really exercises quoting and escaping >>.
Exemple A-16. fifo: Faire des sauvegardes journalières, en utilisant des tubes nommés
#!/bin/bash # ==> Script de James R. Van Zandt, et utilisé ici avec sa permission. # ==> Commentaires ajoutés par l'auteur de ce document. ICI=`uname -n` # ==> nom d'hôte LA_BAS=bilbo echo "début de la sauvegarde distabte vers $LA_BAS à `date +%r`" # ==> `date +%r` renvoie l'heure en un format sur 12 heures, par exempe # ==> "08:08:34 PM". # Assurez-vous que /pipe est réellement un tube et non pas un fichier #+ standard. rm -rf /tube mkfifo /tube # ==> Crée un fichier "tube nommé", nommé "/tube". # ==> 'su xyz' lance les commandes en tant qu'utilisateur "xyz". # ==> 'ssh' appele le shell sécurisé (client de connexion à distance). su xyz -c "ssh $LA_BAS \"cat >/home/xyz/sauve/${ICI}-jour.tar.gz\" < /tube"& cd / tar -czf - bin boot dev etc home info lib man root sbin share usr var >/tube # ==> Utilise un tube nommé, /tube, pour communiquer entre processus: # ==> 'tar/gzip' écrit dans le tube et 'ssh' lit /tube. # ==> Le résultat final est que cela sauvegarde les répertoires principaux; #+ ==> à partir de /. # ==> Quels sont les avantages d'un "tube nommé" dans cette situation, # ==> en opposition avec le "tube anonyme", avec |? # ==> Est-ce qu'un tube anonyme pourrait fonctionner ici? exit 0 |
+
Stephane Chazelas a contribué avec le script suivant pour démontrer que générer des nombres aléatoires ne requiert pas de tableaux.
Exemple A-17. primes: Générer des nombres aléatoires en utilisant l'opérateur modulo
#!/bin/bash # primes.sh: Génère des nombres premiers, sans utiliser des tableaux. # Script contribué par Stephane Chazelas. # Il n'utilise *pas* l'algorithme classique "Sieve of Eratosthenes", #+ mais utilise à la palce la méthode plus intuitive de test de chaque nombre #+ candidat pour les facteurs (diviseurs), en utilisant l'opérateur modulo "%". LIMITE=1000 # Premiers de 2 à 1000 Premiers() { (( n = $1 + 1 )) # Va au prochain entier. shift # Prochain paramètre dans la liste. # echo "_n=$n i=$i_" if (( n == LIMITE )) then echo $* return fi for i; do # "i" est initialisé à "@", les précédentes #+ valeurs de $n. # echo "-n=$n i=$i-" (( i * i > n )) && break # Optimisation. (( n % i )) && continue # Passe les non premiers en utilisant l'opérateur #+ modulo. Premiers $n $@ # Récursion à l'intérieur de la boucle. return done Premiers $n $@ $n # Récursion à l'extérieur de la boucle. # Accumule successivement les paramètres de #+ position. # "$@" est la liste des premiers accumulés. } Premiers 1 exit 0 # Décommenter les lignes 17 et 25 pour vous aider à comprendre ce qui se passe. # Comparez la vitesse de cet algorithme de génération des nombres premiers avec # celui de "Sieve of Eratosthenes" (ex68.sh). # Exercice: Réécrivez ce script sans récursion, pour une exécution plus rapide. |
+
Jordi Sanfeliu a donné sa permission pour utiliser son élégant script sur les arborescences.
Exemple A-18. tree: Afficher l'arborescence d'un répertoire
#!/bin/sh # @(#) tree 1.1 30/11/95 by Jordi Sanfeliu # email: mikaku@arrakis.es # # Version initiale : 1.0 30/11/95 # Prochaine version: 1.1 24/02/97 Maintenant avec des liens symboliques # Corrigé par : Ian Kjos, pour supporter les répertoires non dispo # email: beth13@mail.utexas.edu # # Tree est un outil pour visualiser la hiérarchie d'un répertoire # # ==> Le script 'Tree' utilisé ici avec la permission de son auteur, Jordi Sanfeliu. # ==> Commentaires ajoutés par l'auteur de ce document. # ==> Ajout des guillemets pour les arguments. search () { for dir in `echo *` # ==> `echo *` affiche tous les fichiers du répertoire actuel sans retour à # ==> la ligne. # ==> Même effet que for dir in * # ==> mais "dir in `echo *`" ne gère pas les noms de fichiers comprenant des # ==> espaces blancs. do if [ -d "$dir" ] ; then # ==> S'il s'agit d'un répertoire (-d)... zz=0 # ==> Variable temporaire, pour garder trace du niveau de # ==> répertoire. while [ $zz != $deep ] # Conserve la trace de la boucle interne. do echo -n "| " # ==> Affiche le symbôle du connecteur vertical # ==> avec 2 espaces mais pas de retour à la ligne # ==> pour l'indentation. zz=`expr $zz + 1` # ==> Incrémente zz. done if [ -L "$dir" ] ; then # ==> Si le répertoire est un lien symbolique... echo "+---$dir" `ls -l $dir | sed 's/^.*'$dir' //'` # ==> Affiche le connecteur horizontal et affiche le nom du # ==> répertoire mais... # ==> supprime la partie date/heure. else echo "+---$dir" # ==> Affiche le symbole du connecteur # ==> horizontal et le nom du répertoire. if cd "$dir" ; then # ==> S'il peut se déplacer dans le sous-répertoire... deep=`expr $deep + 1` # ==> Incrémente la profondeur. search # avec la récursivité ;-) # ==> La fonction s'appelle elle-même. numdirs=`expr $numdirs + 1` # ==> Incrémente le compteur de # ==> répertoires. fi fi fi done cd .. # ==> Se placer un niveau au-dessus. if [ "$deep" ] ; then # ==> Si la profondeur est nulle (renvoie TRUE)... swfi=1 # ==> initialiser l'indicateur indiquant que la # ==> recherche est terminée. fi deep=`expr $deep - 1` # ==> Décrémente la profondeur. } # - Principal - if [ $# = 0 ] ; then cd `pwd` # ==> Pas d'arguments au script, alors utilise le répertoire actuel. else cd $1 # ==> Sinon, va dans le répertoire indiqué. fi echo "Répertoire initial = `pwd`" swfi=0 # ==> Indicateur de terminaison. deep=0 # ==> Profondeur de la liste. numdirs=0 zz=0 while [ "$swfi" != 1 ] # Tant que l'indicateur n'est pas initialisé do search # ==> Appelle la fonctione après avoir initialisé les variables. done echo "Nombre total de répertoires = $numdirs" exit 0 # ==> Challenge: essayez de comprendre comment fonctionne ce script |
+
Noah Friedman a donné sa permission pour utiliser son script contenant des fonctions sur les chaînes de caractères, qui reproduit les fonctions de manipulations de la bibliothèque C string.
Exemple A-19. string: Manipuler les chaînes de caractères comme en C
#!/bin/bash # string.bash --- bash emulation of string(3) library routines # Author: Noah Friedman <friedman@prep.ai.mit.edu> # ==> Used with his kind permission in this document. # Created: 1992-07-01 # Last modified: 1993-09-29 # Public domain # Conversion to bash v2 syntax done by Chet Ramey # Commentary: # Code: #:docstring strcat: # Usage: strcat s1 s2 # # Strcat appends the value of variable s2 to variable s1. # # Example: # a="foo" # b="bar" # strcat a b # echo $a # => foobar # #:end docstring: ###;;;autoload ==> Autoloading of function commented out. function strcat () { local s1_val s2_val s1_val=${!1} # indirect variable expansion s2_val=${!2} eval "$1"=\'"${s1_val}${s2_val}"\' # ==> eval $1='${s1_val}${s2_val}' avoids problems, # ==> if one of the variables contains a single quote. } #:docstring strncat: # Usage: strncat s1 s2 $n # # Line strcat, but strncat appends a maximum of n characters from the value # of variable s2. It copies fewer if the value of variabl s2 is shorter # than n characters. Echoes result on stdout. # # Example: # a=foo # b=barbaz # strncat a b 3 # echo $a # => foobar # #:end docstring: ###;;;autoload function strncat () { local s1="$1" local s2="$2" local -i n="$3" local s1_val s2_val s1_val=${!s1} # ==> indirect variable expansion s2_val=${!s2} if [ ${#s2_val} -gt ${n} ]; then s2_val=${s2_val:0:$n} # ==> substring extraction fi eval "$s1"=\'"${s1_val}${s2_val}"\' # ==> eval $1='${s1_val}${s2_val}' avoids problems, # ==> if one of the variables contains a single quote. } #:docstring strcmp: # Usage: strcmp $s1 $s2 # # Strcmp compares its arguments and returns an integer less than, equal to, # or greater than zero, depending on whether string s1 is lexicographically # less than, equal to, or greater than string s2. #:end docstring: ###;;;autoload function strcmp () { [ "$1" = "$2" ] && return 0 [ "${1}" '<' "${2}" ] > /dev/null && return -1 return 1 } #:docstring strncmp: # Usage: strncmp $s1 $s2 $n # # Like strcmp, but makes the comparison by examining a maximum of n # characters (n less than or equal to zero yields equality). #:end docstring: ###;;;autoload function strncmp () { if [ -z "${3}" -o "${3}" -le "0" ]; then return 0 fi if [ ${3} -ge ${#1} -a ${3} -ge ${#2} ]; then strcmp "$1" "$2" return $? else s1=${1:0:$3} s2=${2:0:$3} strcmp $s1 $s2 return $? fi } #:docstring strlen: # Usage: strlen s # # Strlen returns the number of characters in string literal s. #:end docstring: ###;;;autoload function strlen () { eval echo "\${#${1}}" # ==> Returns the length of the value of the variable # ==> whose name is passed as an argument. } #:docstring strspn: # Usage: strspn $s1 $s2 # # Strspn returns the length of the maximum initial segment of string s1, # which consists entirely of characters from string s2. #:end docstring: ###;;;autoload function strspn () { # Unsetting IFS allows whitespace to be handled as normal chars. local IFS= local result="${1%%[!${2}]*}" echo ${#result} } #:docstring strcspn: # Usage: strcspn $s1 $s2 # # Strcspn returns the length of the maximum initial segment of string s1, # which consists entirely of characters not from string s2. #:end docstring: ###;;;autoload function strcspn () { # Unsetting IFS allows whitspace to be handled as normal chars. local IFS= local result="${1%%[${2}]*}" echo ${#result} } #:docstring strstr: # Usage: strstr s1 s2 # # Strstr echoes a substring starting at the first occurrence of string s2 in # string s1, or nothing if s2 does not occur in the string. If s2 points to # a string of zero length, strstr echoes s1. #:end docstring: ###;;;autoload function strstr () { # if s2 points to a string of zero length, strstr echoes s1 [ ${#2} -eq 0 ] && { echo "$1" ; return 0; } # strstr echoes nothing if s2 does not occur in s1 case "$1" in *$2*) ;; *) return 1;; esac # use the pattern matching code to strip off the match and everything # following it first=${1/$2*/} # then strip off the first unmatched portion of the string echo "${1##$first}" } #:docstring strtok: # Usage: strtok s1 s2 # # Strtok considers the string s1 to consist of a sequence of zero or more # text tokens separated by spans of one or more characters from the # separator string s2. The first call (with a non-empty string s1 # specified) echoes a string consisting of the first token on stdout. The # function keeps track of its position in the string s1 between separate # calls, so that subsequent calls made with the first argument an empty # string will work through the string immediately following that token. In # this way subsequent calls will work through the string s1 until no tokens # remain. The separator string s2 may be different from call to call. # When no token remains in s1, an empty value is echoed on stdout. #:end docstring: ###;;;autoload function strtok () { : } #:docstring strtrunc: # Usage: strtrunc $n $s1 {$s2} {$...} # # Used by many functions like strncmp to truncate arguments for comparison. # Echoes the first n characters of each string s1 s2 ... on stdout. #:end docstring: ###;;;autoload function strtrunc () { n=$1 ; shift for z; do echo "${z:0:$n}" done } # provide string # string.bash ends here # ========================================================================== # # ==> Everything below here added by the document author. # ==> Suggested use of this script is to delete everything below here, # ==> and "source" this file into your own scripts. # strcat string0=one string1=two echo echo "Testing \"strcat\" function:" echo "Original \"string0\" = $string0" echo "\"string1\" = $string1" strcat string0 string1 echo "New \"string0\" = $string0" echo # strlen echo echo "Testing \"strlen\" function:" str=123456789 echo "\"str\" = $str" echo -n "Length of \"str\" = " strlen str echo # Exercise: # -------- # Add code to test all the other string functions above. exit 0 |
+
+
Stephane Chazelas montre la programmation objet dans un script Bash.
Exemple A-20. obj-oriented: Bases de données orientées objet
#!/bin/bash # obj-oriented.sh: programmation orienté objet dans un script shell. # Script par Stephane Chazelas. person.new() # Ressemble à la déclaration d'une classe en C++. { local nom_objet=$1 nom=$2 prenom=$3 datenaissance=$4 eval "$nom_objet.set_nom() { eval \"$nom_objet.get_nom() { echo \$1 }\" }" eval "$nom_objet.set_prenom() { eval \"$nom_objet.get_prenom() { echo \$1 }\" }" eval "$nom_objet.set_datenaissance() { eval \"$nom_objet.get_datenaissance() { echo \$1 }\" eval \"$nom_objet.show_datenaissance() { echo \$(date -d \"1/1/1970 0:0:\$1 GMT\") }\" eval \"$nom_objet.get_age() { echo \$(( (\$(date +%s) - \$1) / 3600 / 24 / 365 )) }\" }" $nom_objet.set_nom $nom $nom_objet.set_prenom $prenom $nom_objet.set_datenaissance $datenaissance } echo person.new self Bozeman Bozo 101272413 # Crée une instance de "person.new" (en fait, passe les arguments à la #+ fonction). self.get_prenom # Bozo self.get_nom # Bozeman self.get_age # 28 self.get_datenaissance # 101272413 self.show_datenaissance # Sat Mar 17 20:13:33 MST 1973 echo # typeset -f # pour voir les fonctions créées (attention, cela fait défiler la page). exit 0 |