Documentation sur les scripts shell
Documentation sur les scripts shell
Introduction
Mais de ce tutoriel
Ce didacticiel est écrit pour aider les gens à comprendre certaines bases
de la programmation de scripts shell (alias scripts shell ) et, prétendra-
le, pour présenter certaines possibilités de programmation simples mais
puissantes disponibles sous le shell Bourne. En tant que tel, il a été écrit
comme base pour des tutoriels et des exercices individuels ou en groupe,
et comme référence pour une utilisation ultérieure.
Obtenir la version la plus récente de ce didacticiel
Vous lisez la version 4.2 , dernière mise à jour le 2 mars 2021 .
La version la plus récente de ce tutoriel est toujours disponible
sur : [Link] . Vérifiez toujours la dernière copie. (Si
vous lisez ceci à une adresse différente, il s'agit probablement d'une copie
du site réel et peut donc être obsolète).
Une brève histoire de sh
Steve Bourne a écrit le shell Bourne qui est apparu dans la version
Seventh Edition Bell Labs Research d'Unix.
De nombreux autres coquillages ont été écrits ; ce tutoriel particulier se
concentre sur les coques Bourne et Bourne Again.
D'autres coques incluent le Korn Shell (ksh), le C Shell (csh) et des
variantes telles que tcsh.
Ce tutoriel ne couvre pas ces coques.
Spectateurs
Ce didacticiel suppose une certaine expérience préalable ; à savoir :
o Utilisation d' un shell interactif Unix/Linux
o Connaissances minimales en programmation - l'utilisation de
variables, de fonctions, est une connaissance de base utile
o Compréhension de certaines commandes Unix/Linux
et compétence dans l'utilisation de certaines des plus
courantes . ( ls , cp , écho , etc.)
o Les programmeurs de ruby , perl , python , C , Pascal ou de tout
langage de programmation (même BASIC) qui peuvent-être lire des scripts
shell, mais n'ont pas l'impression de comprendre exactement comment ils
fonctionnent.
Vous pourriez-être examinateur de certains commentaires que ce
didacticiel a reçus pour voir à quel point vous pourriez le trouver utile.
Conventions typographiques utilisées dans ce didacticiel
Les mots significatifs seront écrits en italique lorsqu'ils seront mentionnés
pour la première fois.
Les segments de code et la sortie du script seront affichés sous forme de
texte à espacement fixe.
Les entrées de la ligne de commande seront associées au signe dollar
($). Si votre invitation est différente, saisissez la commande :
PS1="$ " ; = "$" ; exportateur PS1
Ensuite, vos interactions doivent correspondre aux exemples donnés
(comme ./[Link]-dessous).
La sortie du script (telle que "Hello World" ci-dessous) s'affiche au début
de la ligne.
$ echo '#!/bin/sh' > [Link]'#!/bin/sh' > mon - script .
sh
$ echo 'echo Hello World' >> [Link]'echo Hello World'
>> mon - script . sh
$ chmod 755 mon-script.sh755 mon - script . sh
$ ./[Link]./ mon - script . sh
Bonjour le mondeBonjour le monde
$
Les scripts entiers seront affichés avec un arrière-plan gris et incluront une
référence au texte brut du script, le cas échéant :
[Link]
#!/bin/ch
# Ceci est un commentaire !
echo Hello World # C'est aussi un commentaire !Hello World #
C'est aussi un commentaire !
Notez que pour rendre un fichier exécutable, vous devez définir le bit
eXecutable, et pour un script shell, le bit Readable doit également être
défini :
$ chmod a+rx [Link]+ rx mon - script . sh
$ ./[Link]./ mon - script . sh
2. Philosophie
3. Un premier scénario
Pour notre premier script shell, nous allons simplement écrire un script qui
dit "Hello World". Nous essaierons ensuite de tirer le meilleur parti d'un
programme Hello World que de tout autre didacticiel que vous ayez jamais
lu :-)
Créez un fichier ([Link]) comme suit :
[Link]
#!/bin/ch
# Ceci est un commentaire !
echo Hello World # C'est aussi un commentaire !
La première ligne indique à Unix que le fichier doit être exécuté par
/bin/sh. Il s'agit de l'emplacement standard du shell Bourne sur à peu près
tous les systèmes Unix. Si vous utilisez GNU/Linux, /bin/sh est
normalement un lien symbolique vers bash (ou, plus récemment, dash).
La deuxième ligne commence par un symbole spécial : #. Cela marque la
ligne comme un commentaire, et elle est complètement ignorée par le
shell.
La seule exception est lorsque la toute première ligne du fichier
commence par #!- comme le nôtre. Il s'agit d'une directive spéciale
qu'Unix traite spécialement. Cela signifie que même si vous utilisez csh,
ksh ou autre comme shell interactif, ce qui suit doit être interprété par le
shell Bourne.
De même, un script Perl peut commencer par la
ligne #!/usr/bin/perl indiquant à votre shell interactif que le
programme qui doit être exécuté par perl. Pour la programmation Bourne
shell, nous nous en tiendrons à#!/bin/sh.
La troisième ligne exécute une commande : echo, avec deux paramètres
ou arguments - le premier est "Hello"; la seconde est "World".
Notez que echoce sera automatiquement un seul espace entre ses
paramètres.
Le #symbole marque toujours un commentaire ; le # et tout ce qui est
ignoré par le shell.
actualiser chmod 755 [Link] maintenant rendre le fichier texte
exécutable, et actualiser ./[Link].
Votre écran alors devrait être appliqué à ceci :
$ chmod 755 en premier . sh $ ./ premier ministre . sh Bonjour
le monde $
Monde
#!/bin/sh
MON_MESSAGE = "Bonjour le monde" echo $MON_MESSAGE
C'est parce que le programme externe exprn'attend que des nombres. Mais il n'y a pas
de différence syntaxique entre :
MY_MESSAGE = "Bonjour le monde"
MY_SHORT_MESSAGE = salut
MY_NUMBER = 1
MY_PI = 3.142
MY_OTHER_PI = "3.142"
MY_MIXED = 123abc
Notez cependant que les caractères spéciaux doivent être correctement
échappés pour éviter leur interprétation par le shell.
Ceci est expliqué plus en détail au Chapitre 6, Caractères d'échappement .
Nous pouvons définir de manière interactive des noms de variables à
l'aide de la readcommande ; le script suivant vous demande votre nom
puis vous salue personnellement :
[Link]
#!/bin/sh echo Quel est votre nom ? read MY_NAME echo "Bonjour
$MY_NAME - j'espère que vous allez bien."
Quel est ton nom
#!/bin/sh echo "Quel est votre nom ?" read USER_NAME echo
"Bonjour $USER_NAME" echo "Je vais vous créer un fichier
appelé ${USER_NAME}_file" touch "${USER_NAME}_file"
$ mv *. txt *. cuisiner
n'aura pas l'effet créé ; réfléchissez à la façon dont cela est développé par
le shell avant d'être transmis à mv. essayez ceci en utilisant echoau lieu
de mvsi cela aide.
Nous y reviendrons plus tard, car il utilise quelques concepts non encore
couverts.
6. Caractères d'échappement
Certains caractères sont significatifs pour le shell ; nous avons vu, par
exemple, que l'utilisation des caractères guillemets doubles ("") affecte le
traitement des espaces et des caractères TAB, par exemple :
$ echo Bonjour tout le monde
Bonjour le monde
$ echo "Bonjour le monde"
Bonjour le monde
Alors, commentez-nous affichons : Hello "World"?
$ écho *écho * _
[Link] [Link] [Link]. évasion shtml . premier
ministre shtml . shtml
[Link] [Link] [Link] . conseils shtml .
indexer shtml . shtml
[Link] raid1+[Link] - apprêt . txt raid1 + [Link]
$ écho *txt * txt
[Link] raid1+[Link] - apprêt . txt raid1 + [Link]
$ echo "*" "*"
* *
$ echo "*txt" "*SMS"
*SMS * txt
Dans le premier exemple, * est développé pour signifier tous les fichiers
du répertoire courant.
Dans le deuxième exemple, *txt signifie tous les fichiers se terminant
par txt.
Dans le troisième, nous modifions le * entre guillemets doubles, et il est
interprété littéralement.
Dans le quatrième exemple, la même chose s'applique, mais nous avons
ajouté txtà la chaîne.
Cependant, ", $, `, et \sont toujours interprétés par le shell, même
lorsqu'ils sont entre guillemets doubles.
Le caractère barre oblique inverse (\) est utilisé pour marquer ces
caractères spéciaux afin qu'ils ne soient pas interprétés par le shell, mais
transmis à la commande en cours d'exécution (par exemple, echo).
Donc, pour sortir la chaîne : (En supposant que la valeur de $Xest 5) :
Un guillemet est ", la barre oblique inverse est \, la barre
oblique inverse est `.", le backslash est \, le backtick est
`.
Quelques espaces sont et le dollar est $. X $ vaut 5.
il faudrait écrire :
$ echo "Un guillemet est \", la barre oblique inverse est \\,
la barre oblique inverse est \`.""Un guillemet est \", la
barre oblique inverse est \\, la barre oblique inverse est \
`."
Un guillemet est ", la barre oblique inverse est \, la barre
oblique inverse est `.", le backslash est \, le backtick est
`.
$ echo "Quelques espaces sont et dollar est \$. \$X est $
{X}."Quelques espaces sont et dollar est \$ . \$X est $ { X }.
"
Quelques espaces sont et le dollar est $. X $ vaut 5.
Nous avons vu pourquoi le " est spécial pour préserver l'espacement.
Dollar ( $) est spécial car il marque une variable, il $Xest donc remplacé
par le shell avec le contenu de la variable X. La barre oblique inverse ( \)
est spéciale car elle est elle-même utilisée pour marquer d'autres
caractères off ; nous avons besoin des options suivantes pour un shell
complet :
$ echo "Ceci est \\ une barre oblique inverse""Ceci est \\ une
barre oblique inverse"
Ceci est \ une barre oblique inverseCeci est \ une barre
oblique inverse
$ echo "Ceci est \" un guillemet et ceci est \\ une barre
oblique inverse""Ceci est \" un guillemet et ceci est \\ une
barre oblique inverse"
Ceci est " un guillemet et ceci est \ une barre oblique
inverseCeci est " un guillemet et ceci est \ une barre oblique
inverse
Ainsi, la barre oblique inverse elle-même doit être échappée pour montrer
qu'elle doit être prise littéralement. L'autre caractère spécial, le backtick,
est traité plus loin dans le Chapitre 12, Programmes externes .
7. Boucles
#!/bin/ch
pour moi dans 1 2 3 4 5
faire
echo "Boucle ... nom $i"
Fini
essayez ce code et voyez ce qu'il fait. Notez que les valeurs peuvent être
n'importe quoi :
[Link]
#!/bin/ch
pour moi dans bonjour 1*2 au revoir
faire
echo "Boucle ... je suis défini sur $i""Boucle ... i est
défini sur $i"
FiniFini
Cela vaut la peine d'essayer. assurez-vous que vous comprenez ce qui se
passe ici. essayez-le sans le *et saisissez l'idée, puis relisez la section
Caractères génériques et réessayez avec le *en place. essayez-le
également dans différents répertoires, et avec le *entouré de guillemets
doubles, et essayez-le inscrire d'une barre oblique inverse ( \*)
Si vous n'avez pas accès à un shell pour le moment (il est très utile d'avoir
un shell sous la main lors de la lecture de ce tutoriel), les résultats des
deux scripts ci-dessus sont :
Boucle .... numéro 1 .... numéro 1
Boucle .... numéro 2Boucle .... numéro 2
Boucler .... numéro 3Boucler .... numéro 3
Boucle .... numéro 4Boucle .... numéro 4
Boucle .... numéro 5Boucle .... numéro 5
et, pour le deuxième exemple :
En boucle ... je suis réglé sur bonjour ... je suis prêt à
vous saluer
Bouclage ... i est mis à 1Bouclage ... i est mis à 1
Boucle ... i est défini sur (nom du premier fichier du
répertoire courant)Boucle ... i est défini sur ( nom du
premier fichier du répertoire courant )
... etc ...... etc ...
Boucle ... je suis défini sur (nom du dernier fichier dans le
répertoire courant) Boucle ... je suis défini sur ( nom du
dernier fichier dans le répertoire courant )
Bouclage ... je suis réglé sur 2 Bouclage ... je suis réglé
sur 2
En boucle ... je suis prêt à dire au revoir En boucle ... je
suis prêt à dire au revoir
Donc, comme vous pouvez le voir, foril suffit de boucler sur l'entrée qui
lui est donnée, jusqu'à ce qu'il n'y ait plus d'entrée.
Boucles tout en
whileles boucles peuvent être beaucoup plus amusantes ! (selon votre
idée du plaisir, et la fréquence à laquelle vous sortez de la maison... )
tandis [Link]
#!/bin/ch
INPUT_STRING=bonjour = bonjour
tandis que [ "$INPUT_STRING" != "au revoir" ] tandis que [
"$INPUT_STRING" != "au revoir" ]
faire faire
echo "Veuillez saisir quelque chose (au revoir pour
quitter)" "Veuillez saisir quelque chose (au revoir pour
quitter)"
lire INPUT_STRING
echo "Vous avez tapé : $INPUT_STRING" "Vous avez tapé :
$INPUT_STRING"
Fini Fini
Ce qui se passe ici, c'est que les instructions echo et read s'exécutent
indéfiniment jusqu'à ce que vous tapiez "bye" lorsque vous y êtes invité.
Passez en revue Variables - Partie I pour voir pourquoi nous avons
défini INPUT_STRING=helloavant de le tester. Cela en fait une boucle de
répétition, pas une boucle while traditionnelle.
[Link]
#!/bin/ch
pendentif que :tandis que :
fairefaire
echo "Veuillez saisir quelque chose (^C pour
quitter)""Veuillez saisir quelque chose (^C pour quitter)"
lire INPUT_STRING
echo "Vous avez tapé : $INPUT_STRING""Vous avez tapé :
$INPUT_STRING"
FiniFini
Une autre astuce utile est la while readboucle. Cet exemple utilise
l'instruction case , que nous aborderons plus tard. Il lit à partir du
fichier [Link], et pour chaque ligne, vous indique quelle langue il
pense être utilisé.
(Remarque : chaque ligne doit se terminer par un LF (nouvelle ligne) -
si cat [Link] ne se termine pas par une ligne vide, cette dernière
ligne ne sera pas traitée.)
Ceci a allumé le fichier " [Link]", une ligne à la fois, dans la variable
" $input_text". L'instruction case a ensuite la valeur de $input_text. Si
le mot qui a été lu [Link]était "bonjour", alors c'est echole mot
"anglais". Si c'était "gday", alors ça ira echo Australian. Si le mot (ou les
mots) lu à partir d'une ligne [Link] correspond à aucun des
modèles fournis, alors le fourre-tout "*" par défaut affichera le message
"Langue inconnue : $input_text" - où bien sûr "$input_text " est la valeur
de la ligne lue à partir de [Link].
[Link]
#!/bin/ch
pendentif la lecture input_textpendentif la lecture input_text
fairefaire
cas $input_text danscas $input_text dans
bonjour) echo anglais ;;) echo anglais ;;
bonjour) echo americain ;;) echo americain
;;
gday) echo Australien ;;) écho australien
;;
bonjour) echo français ;;) echo French ;;
"guten tag") echo allemand ;; "guten tag" ) echo
allemand ;;
*) echo Langage inconnu : $input_text *)
echo Langage inconnu : $input_text
;; ;;
esac esac
fait < [Link] fait < monfichier . SMS
Disons que notre [Link] contient les cinq lignes suivantes :
ce fichier s'appelle [Link] et nous l'utilisons comme
exemple d'entrée.
salut
gday
Bonjour
salut
Un exemple d'exécution de ce script ressemblerait à ceci :
$ ./[Link] tandis que que3 . sh
Langue inconnue : ce fichier s'appelle [Link] et nous
l'utilisons comme exemple d'entré[Link] inconnu : ce
fichier s'appelle monfichier . txt et nous l'utilisons comme
exemple d'entrée .
Anglais Anglais
australien australien
English English
Langue inconnue : holaLangue inconnue : hola
Une astuce pratique de Bash (mais pas de Bourne Shell) que j'ai
récemment appris du projet Linux From Scratch est la suivante :
mkdir rc{0,1,2,3,4,5,6,S}.d
{ 0 , 1 , 2 , 3 , 4 , 5 , 6 , S }. concernant
au lieu du plus encombrant :
pour le niveau d'exécution en 0 1 2 3 4 5 6 Sniveau
d'exécution en 0 1 2 3 4 5 6 S
fairefaire
mkdir rc${niveau d'exécution}.d{ niveau d'execution }.
concernant
FiniFini
Et cela peut aussi être fait de manière récursive :
$ cd //
$ ls -ld {,usr,usr/local}/{bin,sbin,lib}- ld {, usr , usr /
local }/{ bin , sbin , lib }
drwxr-xr-x 2 racine racine 4096 26 octobre 01:00 /bin- xr - x
2 root root 4096 Oct 26 01 : 00 / bin
drwxr-xr-x 6 racine racine 4096 16 janvier 17:09 /lib - xr - x
6 root root 4096 Jan 16 17 : 09 / lib
drwxr-xr-x 2 racine racine 4096 27 octobre 00:02 /sbin- xr - x
2 root root 4096 Oct 27 00 : 02 / sbin
drwxr-xr-x 2 racine racine 40960 16 janvier 19:35 usr/bin- xr
- x 2 root root 40960 Jan 16 19 : 35 usr / bin
drwxr-xr-x 83 racine racine 49152 16 janvier 17:23 usr/lib- xr
- x 83 root root 49152 Jan 16 17 : 23 usr / lib
drwxr-xr-x 2 racine racine 4096 16 janvier 22:22
usr/local/bin- xr - x 2 root root 4096 Jan 16
22 : 22 usr / local / bin
drwxr-xr-x 3 racine racine 4096 16 janvier 19:17
usr/local/lib- xr - x 3 root root 4096 Jan 16
19 : 17 usr / local / lib
drwxr-xr-x 2 racine racine 4096 28 décembre 00:44
usr/local/sbin- xr - x 2 racine racine 4096 28
décembre 00 : 44 usr / local / sbin
drwxr-xr-x 2 racine racine 8192 27 décembre 02:10 usr/sbin- xr
- x 2 racine racine 8192 27 décembre 02 : 10
usr / sbin
8. Testez
Le test est utilisé par tous les scripts shell écrits. Cela peut ne pas sembler
ainsi, car testil n'est pas souvent appelé directement. testest plus
appelé [. [est un lien symbolique vers test, juste pour rendre les
programmes shell plus lisibles. C'est aussi normalement un shell intégré
(ce qui signifie que le shell lui-même interprétera [comme signifiant test,
même si votre environnement Unix est configuré différemment) :
$ inscrit [
[ est un shell intégré
$ qui [
/usr/bin/[
$ ls -l /usr/bin/[
lrwxrwxrwx 1 racine racine 4 27 mars 2000 /usr/bin/[ -> test
$ ls -l /usr/bin/test
-rwxr-xr-x 1 racine racine 35368 27 mars 2000 /usr/bin/test
Cela signifie que ' [' est en fait un programme, tout comme lset d'autres
programmes, il doit donc être entouré d'espaces :
si [$foo = "bar" ]
ne fonctionnera pas ; il est interprété comme if test$foo = "bar" ],
qui est un ' ]' sans début ' [' . Mettez des espaces autour de tous vos
opérateurs. J'ai mis en surbrillance les espaces obligatoires avec le mot
'ESPACE' - remplacez 'ESPACE' par un espace réel ; s'il n'y a pas d'espace,
cela ne fonctionnera pas :
si ESPACE [ ESPACE "$foo" ESPACE = ESPACE "bar" ESPACE ]
Remarque : Certains coquillages acceptent également " ==" pour la
comparaison de chaînes ; ceci n'est pas portable, un seul " =" doit être
utilisé pour les chaînes, ou " -eq" pour les entiers.
Test est un utilitaire de comparaison simple mais puissant. Pour plus de
détails, adaptés man testsur votre système, mais voici quelques
utilisations et exemples typiques.
Test est le plus souvent appelé via les instructions ifet while. C'est aussi
la raison pour laquelle vous rencontrez des difficultés si vous créez un
programme appelé testet essayez de l'exécuter, car ce shell intégré sera
appelé à la place de votre programme !
La syntaxe pour if...then...else...est :
si [ ... ] [ ... ]
alorsalors
# si-code# si-code
autreautre
# autre-code# autre-code
FiFi
Attention c'est à fil' ifenvers ! Ceci est réutilisé plus tard
avec étui et esac.
Faites également attention à la syntaxe - les commandes " if [ ... ]" et
" then" doivent être sur des lignes différentes. Alternativement, le point-
virgule " ;" peut les séparer :
si [ ... ] ; alors [ ... ] ; alors
# faire quelque chose# faire quelque chose
FiFi
Vous pouvez également utiliser le elif, comme ceci :
si quelque chose a choisi ] ; alors [ quelque chose choisi
] ; alors
echo "Quelque a choisi""Quelque chose"
elif [ autre_chose ] ; alorselif [ autre_chose ] ; alors
echo "Autre choisi""Autre choisi"
autreautre
echo "Aucune des réponses ci-dessus""Aucune de ces
réponses"
FiFi
Ce sera echo "Something"le cas si le [ something ]test réussit, sinon il
testera [ something_else ], et echo "Something else"si cela réussit. Si
tout le reste a échoué, ce sera le cas echo "None of the above".
essayez l'extrait de code suivant, avant de l'exécuter, réglez la variable X
sur différentes valeurs (essayez -1, 0, 1, bonjour, au revoir, etc.). Vous
pouvez le faire comme suit (merci à Dave pour avoir souligné la nécessité
d'exporter la variable, comme indiqué dans Variables - Partie I .) :
X$=5= 5
$ exportateur X
$ ./[Link]./ essai . sh
... sortie de [Link] ...... résultat du test . ch ...
$ X = bonjour= bonjour
$ ./[Link]./ essai . sh
... sortie de [Link] ...... résultat du test . ch ...
$ X=[Link]= essai . sh
$ ./[Link]./ essai . sh
... sortie de [Link] ...... résultat du test . ch ...
Puis réessayez, avec $Xcomme nom un fichier existant, tel
que /etc/hosts.
[Link]
#!/bin/ch
si [ "$X" -lt "0" ]si [ "$X" - lt "0" ]
alorsalors
echo "X est inférieur à zéro""X est inférieur à zéro"
FiFi
si [ "$X" -gt "0" ] ; alors si [ "$X" - gt "0" ] ; alors
echo "X est supérieur à zéro" "X est supérieur à zéro"
Fi Fi
[ "$X" -le "0" ] && \ [ "$X" - le "0" ] && \
echo "X est inférieur ou égal à zéro" "X est inférieur
ou égal à zéro"
[ "$X" -ge "0" ] && \ [ "$X" - et " 0" ] && \
echo "X est supérieur ou égal à zéro" "X est supérieur
ou égal à zéro"
[ "$X" = "0" ] && \ [ "$X" = "0" ] && \
echo "X est la chaîne ou le nombre \"0\"" "X est la
chaîne ou le nombre \"0\""
[ "$X" = "bonjour" ] && \[ "$X" = "bonjour" ] && \
echo "X correspond à la chaîne \"bonjour\"""X correspond
à la chaîne \"bonjour\""
[ "$X" != "bonjour" ] && \[ "$X" != "bonjour" ] && \
echo "X n'est pas la chaîne \"bonjour\"""X n'est pas la
chaîne \"bonjour\""
[ -n "$X" ] && \[ - n "$X" ] && \
echo "X est de longueur non nulle""X est de longueur non
nulle"
[ -f "$X" ] && \[ - f "$X" ] && \
echo "X est le chemin d'un vrai fichier" || \"X est le
chemin d'un vrai fichier" || \
echo "Aucun fichier de ce type : $X""Aucun fichier de ce
type : $X"
[ -x "$X" ] && \[ - x "$X" ] && \
echo "X est le chemin d'un fichier exécutable""X est le
chemin d'un fichier exécutable"
[ "$X" -nt "/etc/passwd" ] && \[ "$X" - nt "/etc/passwd" ] &&
\
echo "X est un fichier plus récent que /etc/passwd""X
est un fichier plus récent que /etc/passwd"
#!/bin/ch
X=0= 0
tandis que [ -n "$X" ]tandis que [ - n "$X" ]
fairefaire
echo "Entrez du texte (RETOUR pour quitter)""Entrez du texte
(RETOUR pour quitter)"
lire X
echo "Vous avez dit : $X""Vous avez dit : X $"
FiniFini
Ce code accepté à demander une entrée jusqu'à ce que vous appuyiez sur
RETURN (X est de longueur nulle). Merci à Justin Heath d'avoir signalé que
le script ne fonctionnait pas - j'avais raté les guillemets autour de $X dans
le fichier while [ -n "$X" ]. Sans ces guillemets, il n'y a rien à tester
lorsque $X est vide.
Alexander Weber a souligné que l'exécution de ce script se terminera de
manière désordonnée :
$ ./[Link]./ test2 . sh
Entrez du texte (RETURN pour quitter)Entrez du texte ( RETURN
pour quitter )
Fred
Vous avez dit : fredVous avez dit : fred
Entrez du texte (RETURN pour quitter)Entrez du texte ( RETURN
pour quitter )
Wilma
Vous avez dit : wilmaVous avez dit : wilma
Entrez du texte (RETURN pour quitter)Entrez du texte ( RETURN
pour quitter )
[Link]
#!/bin/ch
lire INPUT_STRING
case $INPUT_STRING in
hello )
echo "Bonjour toi-même !" ;;
au revoir )
echo "A bientôt !" pause ;; *)
echo "Désolé, je ne comprends pas" ;; esac terminé
écho
echo "C'est tout !
C'est tout !
$
La syntaxe est assez simple :
la caseligne elle-même est toujours du même format, et cela signifie que
nous testons la valeur de la variable INPUT_STRING.
Les options que nous comprenons sont ensuite répertoriées et suivies d'un
crochet droit, comme hello)et bye).
Cela signifie que si INPUT_STRINGcorrespond helloalors cette section de
code est réalisée, jusqu'au double point-virgule.
Si INPUT_STRINGcela correspond bye, le message d'adieu est imprimé et la
boucle se termine. Notez que si nous voulions quitter complètement le
script, nous utiliserions la commande exitau lieu de break.
La troisième option ici, le *), est la condition fourre-tout par défaut ; ce
n'est pas obligatoire, mais c'est souvent utile à des fins de débogage
même si nous pensons connaître les valeurs que la variable de test aura.
Toute l'instruction case se termine par esac(case à l'envers !) puis nous
terminons la boucle while par un done.
C'est à peu près aussi compliqué que les caseconditions deviennent, mais
ils peuvent être un outil très utile et puissant. Ils sont souvent utilisés pour
analyser les paramètres passés à un script shell, entre autres utilisations.
10. Variables - Partie II
#!/bin/ch
echo "J'ai été appelé avec $# paramètres"
echo "Je m'appelle $0"
echo "Mon premier paramètre est $1"
echo "Mon deuxième paramètre est $2"
echo "Tous les paramètres sont $@"
Examinons l'exécution de ce code et voyons le résultat :
$ /home/steve/[Link]
J'ai été appelé avec 0 paramètres
Je m'appelle /home/steve/[Link]
Mon premier paramètre est
Mon deuxième paramètre est
Tous les paramètres sont
$
$ ./[Link] bonjour monde terre
J'ai été appelé avec 3 paramètres
Je m'appelle ./[Link]
Mon premier paramètre est bonjour
Mon deuxième paramètre est le monde
Tous les paramètres sont hello world earth
Notez que la valeur de $0changement en fonction de la façon dont le
script a été appelé. L'utilitaire externe basenamepeut aider à ranger cela :
[Link]
#!/bin/ch
tandis que [ "$#" -gt "0" ][ "$#" - gt "0" ]
faire
echo "\$1 est $1""\$1 est $1"
changement
Fini Fini
Ce script continue d'utiliser shiftjusqu'à ce qu'il $#soit à zéro, auquel cas
la liste est vide.
Une autre variable spéciale est $?. Celui-ci contient la valeur de sortie de
la dernière commande d'exécution. Donc le code :
#!/bin/ch
/usr/local/bin/ma-commande/ usr / local / bin / ma - commande
si [ "$?" [ "$ ?" -ne "0" ] ; alorsne "0" ] ;
echo "Désolé, nous avons eu un problème !""Désolé, nous
avons eu un problème !"
FiFi
#!/bin/ch
old_IFS="$IFS"= "$IFS"
IFS= := :
echo "Veuillez saisir des données séparées par des deux-points
...""Veuillez saisir des données séparées par des deux-
points ..."
lire xyz
IFS=$old_IFS= $old_IFS
echo "x est $xy est $yz est $z""x est $xy est $yz est $z"
Ce script fonctionne comme ceci :
$ ./[Link]./ si . sh
Veuillez saisir des données séparées par des deux
points...Veuillez saisir des données séparées par des deux-
points ...
Bonjour commentaire vas tu aujourd'hui: comment vas-tu :
aujourd'hui
x c'est bonjour y c'est comment vas-tu z c'est aujourd'hui
Notez que si vous entrez : "hello:comment allez-
vous:aujourd'hui:mon:ami", le sera :
$ ./[Link]./ si . sh
Veuillez saisir des données séparées par des deux
points...Veuillez saisir des données séparées par des deux-
points ...
bonjour:comment vas-tu:aujourd'hui:mon:ami: comment vas-tu :
aujourd'hui : mon : ami
x c'est bonjour y c'est comment vas-tu z c'est
aujourd'hui :mon :ami: mon : ami
Il est important lorsqu'il s'agit d'IFS en particulier (mais de toute variable
qui n'est pas entièrement sous votre contrôle) de réaliser qu'il peut
contenir des espaces, des retours à la ligne et d'autres caractères"
incontrôlables". C'est donc une très bonne idée d'utiliser des guillemets
doubles autour, c'est-à-dire : old_IFS="$IFS"au lieu de old_IFS=$IFS.
1. Variables - Partie III
Comme nous l'avons mentionné dans Variables - Partie I , les accolades
autour d'une variable évitent toute confusion :
foo = soleil
echo $fooshine # $fooshine n'est pas défini
echo $ { foo } shine # affiche le mot "sunshine"
Ce n'est pas tout, cependant - ces supports fantasment ont une autre
utilisation, beaucoup plus puissante. Nous pouvons traiter les problèmes
de variables non définies ou nulles (dans le shell, il n'y a pas beaucoup de
différence entre undefined et null).
#!/bin/sh
echo - fr "Quel est ton nom [ `whoami` ] "
Lis mon nom
if [ - z "$monnom" ]; then
myname = `whoami` fi
echo "Votre nom est : $myname"
Passer le " -en" à echo lui dit de ne pas ajouter de saut de ligne (pour
bash et csh). Pour Dash, Bourne et d'autres coques transformées, vous
utilisez \cplutôt un " " à la fin de la ligne. Ksh comprend les deux
formes. (note : voir /[Link] une note surdifférentes
implémentations - en particulier Dash/Bourne vs Bash)
steve$ ./ nom . sh
Quel est votre nom [ steve ] Votre nom est : steve
... ou, avec l'entrée de l'utilisateur :
steve$ ./[Link]
Quel est ton nom [ steve ] foo
Votre nom est : foo
Cela pourrait être mieux fait en utilisant une fonctionnalité de shell
variable. En utilisant des accolades et l'utilisation spéciale ":-", vous
pouvez activer une valeur par défaut à utiliser si la variable n'est pas
définie :
Les programmes externes sont souvent utilisés dans les scripts shell ; il
existe quelques commandes intégrées ( echo, whichet sont généralement
intégrées), mais de nombreuses commandes utiles sont en fait des
utilitaires Unix , testtels que tr, et .grepexprcut
Le backtick (`) est également souvent associé à des commandes
externes. Pour cette raison, nous discuterons d'abord du backtick.
Le backtick est utilisé pour indiquer que le texte inclus doit être exécuté
comme une commande. C'est assez simple à comprendre. Tout d'abord,
utilisez un shell interactif pour lire votre nom complet à partir
de/etc/passwd :
$ grep "^${USER} :" / etc / passwd | couper - d : - f5
Steve Parker
Maintenant, nous allons saisir cette sortie dans une variable que nous
pouvons manipuler plus facilement :
$ MONNOM = `grep "^${USER} :" /etc/passwd | couper -d : -f5`
$ echo $MONNOM
Steve Parker
Nous voyons donc que le backtick attrape simplement la sortie standard
de toute commande ou ensemble de commandes que nous choisissons
d'exécuter. Cela peut également améliorer les performances si vous
souhaitez exécuter une commande lente ou un ensemble de commandes
et analyser divers bits de sa sortie :
#!/bin/sh
trouver / - nom "*.html" - imprimer | grep "/[Link]$"
trouver / - nom "*.html" - imprimer | grep "/[Link]$"
Ce code peut prendre beaucoup de temps à s'exécuter, et nous le faisons
deux fois !
Une meilleure solution est :
#!/bin/sh
HTML_FILES = `find / -name "*.html" -print`
echo "$HTML_FILES" | grep "/[Link]$"
echo "$HTML_FILES" | grep "/[Link]$"
Remarque : les guillemets autour $HTML_FILESsont indispensables pour
préserver les retours à la ligne entre chaque fichier listé. Sinon, grepvous
verrez une énorme longue ligne de texte, et non une ligne par fichier.
. ./bibliothè[Link]
va au début du script.
Il pourrait y avoir une certaine confusion quant à savoir s'il faut appeler
des procédures ou des fonctions de fonctions shell ; la définition d'une
fonction est traditionnellement qu'elle renvoie une seule valeur et ne
produit rien. Une procédure, en revanche, ne renvoie pas de valeur, mais
peut produire une sortie. Une fonction shell peut ne faire ni l'un ni l'autre
ou les deux. Il est généralement admis que dans les scripts shell, elles
sont appelées fonctions.
Une fonction peut renvoyer une valeur de quatre manières différentes :
o Changer l'état d'une variable ou de variables
o Utilisez la exitcommande pour terminer le script shell
o Utilisez la returncommande pour terminer la fonction et renvoyez la
valeur fournie à la section d'appel du script shell
o echo sortie vers stdout, qui sera intercepté par l'appelant tout
comme c=`expr $a + $b` est intercepté
C'est un peu comme C, en ce sens qu'il exitarrête le programme
et returnrend le contrôle à l'appelant. La différence est qu'une fonction
shell ne peut pas modifier ses paramètres, bien qu'elle puisse modifier les
paramètres globaux.
Un script simple utilisant une fonction ressemblerait à ceci :
[Link]
#!/bin/ch
# Un script simple avec une fonction...
add_a_user()
{
UTILISATEUR=$1
MOT DE PASSE=$2
changement; changement;
# Ayant décalé deux fois, le reste est maintenant
commenté...
COMMENTAIRES=$@
echo "Ajout de l'utilisateur $USER..."
echo useradd -c "$COMMENTAIRES" $UTILISATEUR
echo passwd $UTILISATEUR $MOT DE PASSE
echo "Ajout de l'utilisateur $USER ($COMMENTS) avec le mot
de passe $PASSWORD"
}
###
# Le corps principal du script commence ici
###
echo "Début du script..."
add_a_user bob letmein Bob Holness le présentateur
add_a_user fred badpassword Fred Durst le chanteur
add_a_user bilko piremot de passe Sgt. Bilko le modèleSergent
. Bilko le modèle
echo "Fin du script...""Fin du scénario..."
La ligne 4 s'identifie comme une déclaration de fonction en se terminant
par (). Ceci est suivi de {, et tout ce qui suit la correspondance }est
considéré comme le code de cette fonction.
Ce code n'est pas exécuté tant que la fonction n'est pas appelée. Les
fonctions sont lues, mais ignorées jusqu'à ce qu'elles soient réellement
appelées.
Notez que pour cet exemple, les commandes useraddet passwdont été
préfixées par echo- c'est une technique de débogage utile pour vérifier
que les bonnes commandes seront exécutées. Cela signifie également que
vous pouvez exécuter le script sans être root ni ajouter de comptes
d'utilisateurs douteux à votre système !
Nous avons été habitués à l'idée qu'un script shell est exécuté
séquentiellement. Ce n'est pas le cas avec les fonctions.
Dans ce cas, la fonction add_a_userest lue et vérifiée pour la syntaxe,
mais pas exécutée tant qu'elle n'est pas explicitement appelée. C'est là
qu'intervient le bug Shellshock de 2014. D'autres commandes après la
définition de la fonction ont été exécutées, même si elles ne faisaient pas
partie de la fonction elle-
même. Voir [Link] pour plus
d'informations à ce sujet.
L'exécution commence par l' echoinstruction "Début du script...". La ligne
suivante add_a_user bob letmein Bob Holnessest reconnue comme un
appel de fonction afin que la add_a_userfonction soit entrée et commence
à s'exécuter avec certains ajouts à l'environnement :
1 $ = Bob= Bob
2 $ = laissez-moi= laissez-moi
3 $ = Bob= Bob
$4=Sainteté= Holité
5 $ = le= le
6 $ = présentateur= présentateur
Ainsi, dans cette fonction, $1est défini sur bob, indépendamment de ce
qui $1peut être défini en dehors de la fonction.
Donc, si nous voulons faire référence au $1 "d'origine" à l' intérieur de la
fonction, nous devons lui attribuer un nom - tel que : A=$1avant d'appeler
la fonction. Ensuite, dans la fonction, nous pouvons nous référer à $A.
Nous utilisons shiftà nouveau la commande pour obtenir
les $3paramètres et suivants dans $@. La fonction ajoute ensuite
l'utilisateur et définit son mot de passe. C'est echoun commentaire à cet
effet, et renvoie le contrôle à la ligne suivante du code principal.
Portée des variables
Les programmeurs habitués à d'autres langages peuvent être surpris par
les règles de portée des fonctions shell. Fondamentalement, il n'y a pas de
portée, autre que les paramètres ( $1, $2, $@, etc.).
Prenant le segment de code simple suivant :
#!/bin/ch
mafonction()
{
echo "J'ai été appelé comme : $@"
x=2
}
mafonction()()
{{
echo "\$1 est $1""\$1 vaut 1$"
echo "\$2 est $2""\$2 vaut 2$"
# ne peut pas changer $1 - nous devrions dire :# ne peut pas
changer $1 - nous devrions dire :
# 1="Au revoir cruel"# 1="Au revoir cruel"
# qui n'est pas une syntaxe valide. Cependant, nous pouvons#
qui n'est pas une syntaxe valide. Cependant, nous pouvons
# changer $a :# changer $a :
a="Au revoir cruel"= "Au revoir cruel"
}}
a=Bonjour= Bonjour
b=Monde= Monde
mafonction $a $b
echo "a est $a""a est $a"
echo "b est $b""b est $b"
Cette fonction plutôt cynique change $a, donc le message "Hello World"
devient "Goodbye Cruel World".
Récursivité
Les fonctions peuvent être récursives - voici un exemple simple de
fonction factorielle :
[Link]
#!/bin/ch
factorielle()()
{{
si [ "$1" -gt "1" ] ; alorssi [ "$1" - gt "1" ] ; alors
i=`expr $1 - 1`= `expr $1 - 1`
j=`factorielle $i`= `factorielle $i`
k=`expr $1 \* $j`= `expr $1 \* $j`
echo $k
autreautre
écho 11
FiFi
}}
# [Link]
# Notez non #!/bin/sh car cela ne devrait pas apparaître#
Notez non #!/bin/sh car cela ne devrait pas apparaître
# une coque supplémentaire. Ce n'est pas la fin du monde# une
coque supplémentaire. Ce n'est pas la fin du monde
# pour en avoir un, mais plus clair pour ne pas en avoir.#
pour en avoir un, mais plus clair pour ne pas en avoir.
##
STD_MSG="Sur le point de renommer certains fichiers..."= "Sur
le point de renommer certains fichiers..."
Renommer()()
{{
# s'attend à être appelé comme : renommer .txt .bak#
s'attend à être appelé comme : renommer .txt .bak
DE=$1= 1 $
À=$2= 2 $
#!/bin/ch
# [Link]# [Link]
. ./[Link]. ./ commun . bibliothèque
écho $STD_MSG
renommer .txt .bak. txt . cuire
[Link]
#!/bin/ch
# [Link]# [Link]
. ./[Link]. ./ commun . bibliothèque
écho $STD_MSG
renommer .html .html-bak. html . html - bak
Ici, nous voyons deux scripts shell
utilisateur, [Link] [Link], chacun utilisant le fichier de
bibliothèque commun [Link] utilisant des variables et des
fonctions déclarées dans ce fichier.
Ce n'est rien de trop bouleversant, juste un exemple de la façon dont la
réutilisation du code peut être effectuée dans la programmation shell.
Codes de retour
Pour plus de détails sur les codes de sortie, consultez la partie Codes de
sortie de la section Trucs et astuces du didacticiel. Pour l'instant, nous
allons brièvement examiner l' returnappel.
#!/bin/ch
adduser()()
{{
UTILISATEUR=$1= 1 $
MOT DE PASSE=$2= 2 $
changement ; changement; changement
COMMENTAIRES=$@= $@
useradd -c "${COMMENTAIRES}" $USER- c "${COMMENTAIRES}"
$UTILISATEUR
si [ "$?" -ne "0" ] ; alorssi [ "$?" - ne "0" ] ; alors
echo "Échec de l'ajout de l'utilisateur""Échec de l'ajout
de l'utilisateur"
retour 1retour 1
FiFi
mot de passe $USER $PASSWORD
si [ "$?" -ne "0" ] ; alorssi [ "$?" - ne "0" ] ; alors
echo "La définition du mot de passe a échoué""Échec de la
définition du mot de passe"
retour 2retour 2
FiFi
echo "Ajout de l'utilisateur $USER ($COMMENTS) avec le mot
de passe $PASSWORD""Utilisateur $USER ($COMMENTS) ajouté avec
le mot de passe $PASSWORD"
}}
#!/bin/ch#!/bin/ch
steves=`grep -i steve /etc/passwd | couper -d : -f1`= `grep -i
steve /etc/passwd | couper -d : -f1`
echo "Tous les utilisateurs avec le mot "steve" dans leur mot
de passe""Tous les utilisateurs avec le mot " steve " dans
leur mot de passe"
echo "Les entrées sont : ""Les entrées sont :"
echo "$steves" | tr ' ' '\012' | tr '[az]' '[AZ]'"$Steve" | tr
' ' '\012' | tr '[az]' '[AZ]'
Ici, nous avons ajouté une traduction de [az] en [AZ]. Notez qu'il y a
exactement le même nombre de valeurs dans la plage az que AZ. Cela
peut alors traduire n'importe quel caractère tombant dans la plage ASCII
az en AZ ... en d'autres termes, convertir les lettres minuscules en
majuscules. trest en fait plus intelligent que cela : tr [:lower:]
[:upper:]ferait le travail tout aussi bien, et peut-être plus lisiblement. Ce
n'est pas aussi portable; tout le monde ne trpeut pas faire ça.
La triche
Ceux qui ne peuvent pas... tricher
Il n'y a rien de mal à tricher ! Certaines choses pour lesquelles la coquille
n'est tout simplement pas très bonne. Deux outils utiles
sont sedet awk. Bien qu'il s'agisse de deux utilitaires extrêmement
puissants, qui peuvent être utilisés comme des mini-langages de
programmation à part entière, ils sont souvent utilisés dans des scripts
shell pour des raisons très simples et spécifiques.
Bien que cela signifie que le système doit charger un exécutable assez
volumineux (52k pour sedet 110k pour awk), ce qui est une mauvaise
chose à faire, la raison pour laquelle un bon ouvrier ne blâme pas ses
outils, c'est qu'un bon ouvrier utilise les bons outils dans la première
place.
Alors permettez-moi de vous présenter ces deux, avec des utilisations très
simples.
Tricher avec awk
Considérez wc, qui compte le nombre de caractères, de lignes et de mots
dans un fichier texte. Sa sortie est :
$ wc hex2env.c. c
102 189 2306 hex2env.c102 189 2306 hex2env . c
Si nous voulons obtenir le nombre de lignes dans une variable, il suffit
d'utiliser :
NO_LINES=`wc -l fichier`= `wc -l fichier`
qui se lirait dans toute la ligne.
Étant donné que la sortie est remplie d'espace, nous ne pouvons pas
obtenir de manière fiable le nombre 102dans la chaîne. Au lieu de cela,
nous utilisons le fait qui awkfonctionne de la même manière scanfqu'en C
- il supprime les espaces blancs indésirables. Il les place dans des
variables, $1 $2 $3etc. Nous utilisons donc cette construction :
NO_LINES=`wc -l fichier | awk '{ imprimer $1 }'`= `wc -l
fichier | awk '{ imprimer $1 }'`
La variable NO_LINES est maintenant 102.
Tricher avec sed
Un autre utilitaire pratique est sed - l' éditeur de flux . Perl est très bon
pour gérer les expressions régulières, le shell ne l'est pas. Nous pouvons
donc rapidement utiliser la s/from/to/gconstruction en invoquant [Link]
exemple :
sed s/eth0/eth1/g fichier1 > fichier2/ eth0 / eth1 / g
fichier1 > fichier2
change chaque instance de eth0in file1 en eth1in file2.
Si nous ne modifions qu'un seul caractère, trce serait l'outil à utiliser,
étant plus petit et donc plus rapide à charger.
Une autre chose qui trne peut pas faire est de supprimer des caractères
d'un fichier :
echo ${QUELQUE CHOSE} | sed s/"gros mot"//g{ QUELQUE CHOSE } |
sed s / "gros mot" // g
Cela supprime l'expression « mauvais mot » de la variable $
{SOMETHING}. Il peut être tentant de dire : "Mais grepje peux faire ça !" -
grep ne traite que des lignes entières. Considérez le fichier :
#!/bin/ch
hôte=[Link]= 127,0 . 0,1
port=23= 23
connexion = steve= Steve
passwd=bonjourlà= bonjour ici
cmd="ls /tmp"= "ls /tmp"
#!/bin/ch#!/bin/ch
# [Link] | telnet > FICHIER1# [Link] | telnet >
FICHIER1
hôte=[Link]= 127,0 . 0,1
port=23= 23
connexion = steve= Steve
passwd=bonjourlà= bonjour ici
cmd="ls /tmp"= "ls /tmp"
délai d'attente = 3= 3
fichier=fichier1= fichier1
invite="$"= "$"
Exécutez la commande
& précédente en arrière-plan ls &
état de sortie de la
$? dernière commande ls ; echo "ls returned code $?"
Nom de la commande en
cours (telle qu'elle est
0$ appelée) echo "I am $0"
Nom du neuvième
paramètre de la
9$ commande actuelle echo "My ninth argument is $9"
connexion :
La différence ici est que vous ne récupérez pas la session csh.
Ce qui est bien, c'est l'historique :
csh % ksh
ksh$ ensemble - o vi
ksh$ # Vous pouvez maintenant éditer l'historique avec des
commandes de type vi, # et utiliser ESC-k pour accéder à
l'historique.
Si vous appuyez sur ESCpuis k, puis en appuyant plusieurs fois sur kvous
faites défiler vers l'arrière l'historique des commandes. Vous pouvez
utiliser les commandes vi en mode commande et en mode entrée pour
modifier les commandes, comme ceci :
ksh $ toucher foo
ESC - k ( entrer en mode vi , afficher la commande
précédente )
w ( passer au mot suivant , pour passer de "touch" à "foo"
cw ( changer de mot ) bar ( changer "foo" en "bar" )
ksh$ barre tactile
17. Exercices
Juste un exercice rapide qui, je pense, pourrait être utile pour déterminer à
quel point vous êtes doué pour les scripts shell ...
o Carnet d'adresses
o Traversée de répertoire
Carnet d'adresses
D'accord, c'est ennuyeux, mais voici votre exercice d'aujourd'hui :
créez un programme de carnet d'adresses en utilisant le shell bourne ou
bourne-again.
Il doit utiliser des fonctions pour effectuer les tâches requises. Il devrait
être basé sur des menus, vous permettant les options suivantes :
o Rechercher dans le carnet d'adresses
o Ajouter des entrées
o Supprimer/modifier des entrées
Vous aurez également besoin d'une fonction "affichage" pour afficher un
ou plusieurs enregistrements lorsqu'ils sont sélectionnés.
Recherche
Lorsque l'utilisateur recherche "Smith", le script doit identifier et afficher
tous les enregistrements "Smith". C'est à vous de décider si cette
recherche porte uniquement sur Nom ou sur toute la notice.
Ajouter
Saisissez les données (nom, prénom, e-mail, téléphone, etc.).
S'il semble s'agir d'un doublon, pour les points bonus, proposez de
modifier l'enregistrement existant.
Enregistrez l'enregistrement dans le fichier de données lorsque l'utilisateur
confirme.
Éliminer
Entrez les critères de recherche, réduisez-les à un, confirmez, puis
supprimez cet enregistrement.
Modifier
Comme supprimer, mais utiliser l'enregistrement existant pour les entrées
par défaut.
Par exemple, si vous modifiez le numéro de téléphone, la session peut
ressembler à ceci, si vous souhaitez uniquement modifier le numéro de
téléphone de John Smith :
Nom [ John Smith ]
Téléphone [ 12345 ] 54321
Courriel [ joe@[Link] ]
# Variables globales
LIVRE=~/.carnet d'adresses
exporter LIVRE
confirmer()
{
echo -fr "$@"
lire et répondre
ans=`echo $ans | tr '[AZ]' '[AZ]'`
if [ "$ans" == "Y" ]; alors
retour 0
autre
retour 1
Fi
}
nombre_lignes()
{
grep -i "$@" $LIVRE|wc -l| awk '{ imprimer $1 }'
}
trouver_lignes()
{
# Trouver les lignes correspondant à $1
res=-1
si [ ! -z "$1" ] ; alors
grep -i "$@" $LIVRE
res=$ ?
Fi
retourner $res
}
list_items()
{
# Liste des éléments correspondant aux critères de recherche
donnés
if [ "$#" -eq "0" ] ; alors
echo -en "Rechercher : (retourner à la liste complète)"
lire la recherche
if [ -z "$recherche" ]; alors
recherche="."
Fi
écho
autre
recherche="$@"
Fi
find_lines "${recherche}" | pendant que je lis
faire
echo "$i" | tr ':' '\t'
Fini
echo -en "Correspondances trouvées :"
num_lines "$recherche"
}
ajouter un item()
{
echo "Ajouter un élément : 3 éléments vous seront demandés :"
echo " - Nom, téléphone, e-mail."
écho
echo -fr "Nom : "
lire le nom
find_lines "^${nom} :"
if [ `num_lines "^${name}:"` -ne "0" ] ; alors
echo "Désolé, $name a déjà une entrée."
retourner
Fi
echo -fr "Téléphone : "
lire le téléphone
echo -fr "Courriel : "
lire un e-mail
# Confirmer
echo "${nom} :${téléphone} :${email}" > $LIVRE
}
locate_single_item()
{
echo -en "Élément à rechercher :"
lire la recherche
n=`nombre_lignes "$recherche"`
si [ -z "$n" ]; alors
n=0
Fi
tandis que [ "${n}" -ne "1" ] ; faire
#list_items "$recherche"
echo -en "${n} correspondances trouvées. Veuillez choisir
un "
cas "$n" dans
"0") echo "moins" ;;
"*") echo "plus" ;;
esac
echo "terme de recherche spécifique (q pour revenir au
menu) : "
lire la recherche
if [ "$recherche" == "q" ] ; alors
retour 0
Fi
n=`nombre_lignes "$recherche"`
Fini
return `grep -in $search $BOOK |cut -d":" -f1`
}
retirer l'objet()
{
locate_single_item
recherche=`tête -$ ? $RÉSERVER | queue -1|tr ' ' '.'`
if [ -z "${recherche}" ] ; alors
retourner
Fi
list_items "$recherche"
confirmez "Supprimer ?"
si [ "$?" -eq "0" ] ; alors
grep -v "$recherche" $LIVRE > ${LIVRE}.tmp ; mv $
{LIVRE}.tmp ${LIVRE}
autre
echo "NE PAS SUPPRIMER"
Fi
}
modifier l'article()
{
locate_single_item
recherche=`tête -$ ? $RÉSERVER | queue -1|tr ' ' '.'`
if [ -z "${recherche}" ] ; alors
retourner
Fi
list_items "$recherche"
thisline=`grep -i "$search" $BOOK`
anciennom=`echo $thisline|cut -d":" -f1`
oldphone=`echo $thisline|cut -d":" -f2`
oldemail=`echo $thisline|cut -d":" -f3`
echo "RECHERCHE : $recherche"
grep -v "$recherche" $LIVRE > ${LIVRE}.tmp ; mv ${LIVRE}.tmp
${LIVRE}
echo -en "Nom [ $anciennom ] "
lire le nom
if [ -z "$nom" ]; alors
nom=$ancien nom
Fi
find_lines "^${nom} :"
if [ `num_lines "^${name}:"` -ne "0" ] ; alors
echo "Désolé, $name a déjà une entrée."
retourner
Fi
echo -en "Téléphone [ $oldphone ] "
lire le téléphone
if [ -z "$phone" ]; alors
téléphone=$ancientéléphone
Fi
echo -en "E-mail [ $ancienemail ] "
lire un e-mail
if [ -z "$email" ]; alors
e-mail=$ancien e-mail
Fi
echo "${nom} :${téléphone} :${email}" > $LIVRE
}