0% ont trouvé ce document utile (0 vote)
18 vues49 pages

Documentation sur les scripts shell

Transféré par

Anse Dorel
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats DOC, PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
18 vues49 pages

Documentation sur les scripts shell

Transféré par

Anse Dorel
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats DOC, PDF, TXT ou lisez en ligne sur Scribd

1.

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

La programmation de scripts shell est un peu mauvaise presse parmi


certains administrateurs de systèmes Unix. Cela est normalement dû à
l'une des deux choses suivantes :
o La vitesse à laquelle un programme interprété s'exécute par rapport
à un programme C, ou même à un programme Perl interprété.
o Puisqu'il est facile d'écrire un simple script shell de type batch, il
existe de nombreux scripts shell de mauvaise qualité.
C'est en partie à cause de cela qu'il y a un certain machisme associé à la
création de bons scripts shell. Des scripts qui peuvent être utilisés comme
programmes CGI, par exemple, sans trop perdre en vitesse par rapport à
Perl (bien que les deux perdraient par rapport à C, dans de nombreux cas,
si la vitesse était le seul critère).
Il existe un certain nombre de facteurs qui peuvent entrer dans des scripts
shell bons, propres et rapides.
o Le critère le plus important doit être une mise en page claire et
lisible.
o Deuxièmement, évitez les commandes inutiles.
Une mise en page claire fait la différence entre un script shell apparaissant
comme de la "magie noire" et un qui est maintenu et compris.
Vous pouvez être pardonné de penser qu'avec un script simple, ce n'est
pas un problème trop important, mais deux choses ici satisfaisantes d'être
gardées à l'esprit.
1. Tout d'abord, un script simple deviendra, plus souvent que prévu, un
script volumineux et complexe.
2. , si personne d'autre ne peut comprendre comment cela fonctionne,
vous serez chargé de l'entretenir vous-même pour le reste de votre vie !
Quelque chose à propos des scripts shell semble les rendre
particulièrement susceptibles d'être mal indentés, et puisque les
principales structures de contrôle sont if/then/else et les boucles,
l'indentation est essentielle pour comprendre ce que fait un script.
Une faiblesse dans de nombreux scripts shell est des lignes telles que :
chat / tmp / monfichier | grep "machaîne"
qui fonctionnerait beaucoup plus vite que :
grep "machaîne" / tmp / monfichier
Pas grand-chose, vous pouvez considérer ; le système d'exploitation doit
charger l' /bin/grepexécutable, qui est raisonnablement petit de 75600
octets sur mon système, ouvrir une pipemémoire pour le transfert,
charger et procéder à l' /bin/catexécutable, qui est encore plus petit de
9528 octets sur mon système, l' attacher à l'entrée du tuyau et laissez-le
couler.
Bien sûr, ce genre de choix est la raison d'être du système d'exploitation,
et il est normalement assez efficace pour le faire. Mais si cette était
commandée dans une boucle exécutée plusieurs fois, l'économie de ne
pas localiser et charger l' catexécutable, configurer et libérer le tube, peut
faire une différence, en particulier dans, disons, un environnement CGI où
il y a suffisamment d 'autres facteurs pour ralentir les choses sans que le
script lui-même ne soit trop un obstacle. Certains Unix sont plus efficaces
que d'autres dans ce qu'ils appellent "construire et supprimer des
processus" - c'est-à-dire les charger, les exécuter et les effacer à
nouveau. Mais aussi bon que soit votre goût d'Unix pour faire cela,
À la suite de cela, vous entendrez peut-être parler du prix Useless Use of
Cat (UUoC), également connu dans certains cercles sous le nom de The
Award for The Most Gratuitous Use Of The Word Cat In A Serious
Shell [Link] temps en temps. C'est purement une
façon pour les paires de se contrôler et de s'assurer que les choses sont
bien faites.
Ce qui m'amène à autre a choisi : ne vous affichez jamais trop proche de
vos propres scripts shell ; de par leur nature, la source ne peut pas être
fermée. Si vous fournissez un script shell à un client, il peut l'inspecter
assez facilement. Alors autant accepter qu'il soit inspecté par toute
personne à qui vous le passerez ; utilisez-le à votre avantage avec la GPL -
encouragez les gens à vous faire part de leurs commentaires et de leurs
corrections de bogues gratuitement !

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

Vous vous y attendez probablement ! Vous pouvez même procéder


simplement :
$ echo Bonjour le monde Bonjour le monde $Bonjour Monde Monde

Apportons maintenant quelques modifications.


Tout d'abord, notez qu'il a echorencontré UN espace entre ses
paramètres. Mettez quelques espaces entre "Bonjour" et
"Monde". Qu'attendez-vous de la sortie ? Qu'en est-il de mettre un
caractère TAB entre eux ?
Comme toujours avec la programmation shell, essayez-le et voyez.
Le rendu est exactement le même ! Nous appelons le echoprogramme
avec deux arguments ; il ne se soucie pas plus que cpdes écarts entre
eux. Maintenant, modifiez à nouveau le code :
#!/bin/sh # Ceci est un commentaire ! echo "Hello World" #
C'est aussi un commentaire !

Cette fois ça marche. Vous vous y attendez probablement aussi si vous


avez de l'expérience avec d'autres langages de programmation. Mais la
clé pour comprendre ce qui se passe avec des commandes et des scripts
shell plus complexes est de comprendre et d'être capable d'expliquer :
POURQUOI ?
echoa été maintenant appelé avec UN seul argument - la chaîne "Hello
World". Il l'imprime exactement.
Le point à comprendre ici est que le shell analyse les arguments AVANT de
les transmettre au programme appelé. Dans ce cas, il supprime les
guillemets mais passe la chaîne comme un argument.
Comme dernier exemple, noté le script suivant. essayez de prédire le
résultat avant de l'exécuter :
[Link]

#!/bin/sh # Ceci est un commentaire ! echo "Hello World" #


C'est aussi un commentaire ! echo "Hello World" echo "Hello *
World" echo Hello * World echo Hello World echo "Hello" World
echo Hello " " World echo "Hello "*" World" echo `hello` world
echo 'hello' world

Bonjour Monde Bonjour Monde Monde Bonjour Monde * " Monde"


Est-ce que tout est comme vous l'espériez ? Si non, ne vous inquiétez
pas ! Ce ne sont que quelques-unes des choses que nous couvrirons dans
ce tutoriel ... et oui, nous utiliserons des commandes plus puissantes
que echo!
4. Variables - Partie I

Presque tous les langages de programmation existants sur le concept


de variables - un nom symbolique pour un morceau de mémoire auquel
nous pouvons attribuer des valeurs, lire et manipuler son contenu. Le shell
Bourne ne fait pas exception, et cette section présente cette idée. Ceci est
approfondi dans Variables - Partie II qui examine les variables qui nous
sont définies par l'environnement.
Revenons à notre premier exemple de Hello World. Cela pourrait être fait
en utilisant des variables (bien que ce soit un exemple si simple qu'il ne le
justifie pas vraiment !)
Notez qu'il ne doit pas y avoir d'espaces autour du signe
"=" : VAR=valueworks;VAR = valuene fonctionne pas. Dans le premier
cas, le shell voit le symbole "=" et traite la commande comme une
affectation de variable. Dans le second cas, le shell suppose que VAR doit
être le nom d'une commande et essaie de l'exécuter.
Si vous y réfléchissez, cela a du sens - comment pourriez-vous lui dire
d'exécuter la commande VAR avec son premier argument étant "=" et son
deuxième argument étant "valeur" ?
Entrez le code suivant dans [Link] :
[Link]

#!/bin/sh
MON_MESSAGE = "Bonjour le monde" echo $MON_MESSAGE

Cela affecte la chaîne "Hello World" à la variable MY_MESSAGEpuis echosur


la valeur de la variable.
Notez que nous avons besoin des guillemets autour de la chaîne Hello
World. Alors que nous infligeons nous en sortir echo Hello Worldcar echo
accepter n'importe quel nombre de paramètres, une variable ne peut
contenir qu'une seule valeur, donc une chaîne avec des espaces doit être
entre guillemets pour que le shell sache qu'il doit tout traiter comme un
seul. Sinon, le shell essaiera d'exécuter la commande Worldaprès avoir
désignéMY_MESSAGE=Hello
Le shell ne se soucie pas des types de variables ; ils peuvent stocker des
chaînes, des entiers, des nombres réels - tout ce que vous voulez.
Les personnes habituées à Perl peuvent en être très satisfaites ; si vous
avez grandi avec C, Pascal ou pire encore Ada, cela peut sembler assez
étrange.
En vérité, ceux-ci sont tous stockés sous forme de chaînes, mais les
routines qui attendent un nombre peuvent les traiter comme telles.
Si vous affectez une chaîne à une variable puis essayez d'y ajouter 1, vous
ne vous en sortirez pas :
$ x = "bonjour"
$ expr $x + 1 expr : argument non numérique $+ 1

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

Mario Bacinsky m'a gentiment fait remarquer que j'avais à l'origine


manqué les guillemets doubles à la ligne 3, ce qui signifiait que le
guillemet simple dans le mot "tu es" était inégalé, provoquant une
erreur. C'est ce genre de chose qui peut rendre fou un programmeur shell,
alors faites attention à eux !
Cela utilise la commande intégrée au shell readqui a allumé une ligne de
l'entrée standard dans la variable fournie.
Notez que même si vous lui donnez votre nom complet et n'utilisez pas de
guillemets doubles autour de la echocommande, il sort toujours
correctement. Comment est-ce fait? Avec la MY_MESSAGEvariable
précédente, nous devions mettre des guillemets doubles autour d'elle pour
la définir.
Ce qui se passe, c'est que la readcommande place automatiquement des
guillemets autour de son entrée, afin que les espaces soient traités
correctement. (Vous aurez besoin de citer la sortie, bien sûr - par
exemple echo "$MY_MESSAGE").

Portée des variables


Les variables dans le shell Bourne n'ont pas besoin d'être déclarées,
comme c'est le cas dans des langages comme C. Mais si vous essayez de
lire une variable non déclarée, le résultat est la chaîne vide. Vous
n'obtenez aucun avertissement ou erreur. Cela peut provoquer des bogues
subtils - si vous attribuez puis Ensuite, vous n'obtiendrez rien (car le
deuxième
MY_OBFUSCATED_VARIABLE=Hello
OBFUSCATED
echo $MY_OSFUCATED_VARIABLE
est mal orthographié).
Il existe une commande appelée exportqui a un effet fondamental sur la
portée des variables. Afin de vraiment savoir ce qui se passe avec vos
variables, vous devrez comprendre comment cela est utilisé.
Créer un petit script shell, [Link] :
[Link]

#!/bin/sh echo "MAVAR est : $MAVAR" MYVAR = "bonjour" echo


"MAVAR est : $MAVAR"

Exécutez maintenant le script :


$ ./ mavar2 . sh
MYVAR est :
MYVAR est : bonjour
MYVAR n'a été défini sur aucune valeur, il est donc vide. Ensuite, nous lui
donnons une valeur, et il a un résultat attendu.
Exécutez maintenant :
$ MAVAR = bonjour
$ ./ mavar2 . sh
MYVAR est :
MYVAR est : bonjour
Ce n'est toujours pas réglé ! Ce qui se passe ?!
Lorsque vous appelez [Link] votre shell interactif, un nouveau
shell est généré pour exécuter le script. C'est en partie à cause de
la #!/bin/shligne au début du script, dont nous avons parlé plus tôt .
Nous avons besoin de exportla variable pour qu'elle soit générée par un
autre programme - y compris un script shell. Cône :
$ export MAVAR
$ ./ mavar2 . sh
MYVAR est : bonjour
MYVAR est : bonjour
Regardez maintenant la ligne 3 du script : cela change la valeur
de MYVAR. Mais il n'y a aucun moyen que cela soit renvoyé à votre shell
interactif. essayez de lire la valeur de MYVAR :
$ écho $ MAVAR
bonjour
$
Une fois le script shell terminé, son environnement est
détruit. Mais MYVARconservez sa valeur hellodans votre shell interactif.
Afin de recevoir les changements d'environnement du script, nous
devons sourcer le script - cela exécute effectivement le script dans notre
propre shell interactif, au lieu de générer un autre shell pour l'exécuter.
Nous pouvons sourcer un script via le fichier "." (point) commande :
$MAVAR = bonjour
$ echo $MAVAR
bonjour
$ . ./ mavar2 . sh MYVAR est : bonjour MYVAR est : bonjour $
echo $MYVAR bonjour

Le changement a maintenant de nouveau fait son apparition dans notre


coquille ! C'est ainsi que fonctionne votre
fichier .profileou .bash_profile, par exemple.
Notez que dans ce cas, nous n'avons pas besoin de export MYVAR.
Merci à sway d'avoir souligné que j'avais initialement dit echo MYVARci-
dessus, pas echo $MYVARcomme il se doit. Un autre exemple d'erreur
facile à faire avec les scripts shell. Une autre chose qui mérite d'être
mentionnée à ce stade à propos des variables est de considérer le script
shell suivant :
#!/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

Pensez au résultat que vous attendez. Par exemple, si vous saisissez


"steve" comme USER_NAME, le script doit-il créer steve_file ?
En fait non. Cela provoquera une erreur sauf s'il existe une variable
appelée USER_NAME_file. Le shell ne sait pas où se termine la variable et
où commence le reste. Comment pouvons-nous définir cela ?
La réponse est que nous convenons la variable elle-même entre
accolades :
[Link]

#!/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"

Le shell sait maintenant que nous faisons référence à la


variable USER_NAMEet que nous voulons qu'elle soit suffixée par
" _file". Cela peut être la cause de la chute de nombreux nouveaux
programmeurs de scripts shell, car la source du problème peut être
difficile à localiser.
Notez également les guillemets autour "${USER_NAME}_file"- si
l'utilisateur a saisi "Steve Parker" (notez l'espace), alors sans les
guillemets, les arguments passés à touchêtre Steveet Parker_file- c'est-
à-dire que nous dirions effectivement touch Steve Parker_file, qui est
deux fichiers à touchéditer, pas une. Les guillemets évitent cela. Merci à
Chris pour cette mise en lumière.
5. Caractères génériques

Les caractères génériques ne sont vraiment rien de nouveau si vous avez


déjà utilisé Unix précédemment.
Cependant, leur utilité dans les scripts shell n'est pas évidente. Cette
section est vraiment juste pour amener les anciennes cellules grises à
penser à ce que les choses ressemblent lorsque vous êtes dans un script
shell - prédire quel est l'effet de l'utilisation de différentes syntaxes. Cela
sera utilisé plus tard, en particulier dans la section Boucles .
Pensez d'abord à la façon dont vous copiez tous les fichiers
de /tmp/avers /tmp/b. Tous les fichiers .txt ? Tous les fichiers .html ?
J'espère que vous aurez trouvé :

$ cp / tmp / a /* / tmp / b / $ cp / tmp / a /*. txt / tmp / b


/ $ cp / tmp / a /*. html / tmp / b /

Maintenant, commentez-vous les fichiers /tmp/a/sans utiliser ls


/tmp/a/ ?
Que diriez echo /tmp/a/*-vous ? Quelles sont les deux principales
différences entre ceci et la lssortie ? Comment cela peut-il être utile ? Ou
une entrave ?
Comment pourriez-vous renommer tous les fichiers .txt en .bak ? Notez
que

$ 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"?

$ echo "Bonjour \"Monde\""


Le premier et le dernier " caractères enveloppent le tout dans un seul
paramètre passé à echoafin que l'espacement entre les deux mots soit
conservé tel quel. Mais le code :
$ echo "Bonjour "Monde""
serait interprété comme trois paramètres :
1. "Bonjour "
2. Monde
3. ""
La sortie serait donc
Bonjour le monde
Notez que nous perdons entièrement les guillemets. C'est parce que les
premiers et deuxièmes guillemets marquent les espaces Hello et
suivants ; le deuxième argument est un "Monde" sans guillemets et le
troisième argument est la chaîne vide ; "".
Merci à Patrick d'avoir signalé que ceci :

$ echo "Bonjour "Monde""


n'est en fait qu'un seul paramètre (pas d'espaces entre les paramètres
cités), et que vous pouvez tester cela en remplaçant la echocommande
par (par exemple) ls.
La plupart des caractères ( *, ', etc.) ne sont pas interprétés (c'est-à-dire
qu'ils sont pris littéralement) en les nommés entre guillemets (""). Ils sont
pris tels quels et transmis à la commande appelée. Un exemple utilisant
l'astérisque (*) :

$ é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

La plupart des langages ont le concept de boucles : si nous voulons


répéter une tâche vingt fois, nous ne voulons pas avoir à taper le code
vingt fois, avec peut-être un léger changement à chaque fois.
En conséquence, nous avons des boucles foret whiledans le shell
Bourne. C'est un peu moins de fonctionnalités que d'autres langages, mais
personne n'a prétendu que le shell de programmation avait la puissance
de C.
Pour les boucles
forles boucles parcourent un ensemble de valeurs jusqu'à ce que la liste
soit épuisée :
[Link]

#!/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.

Les deux-points ( :) sont toujours évalués à vrai ; si son utilisation peut


parfois être nécessaire, il est souvent préférable d'utiliser une véritable
condition de sortie. Comparez quitter la boucle ci-dessus avec celle ci-
dessous ; voyez ce qui est le plus élégant. Pensez également à certaines
situations dans lesquelles chacune serait plus utile que l'autre :

[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"

Notez que nous pouvons utiliser le point-virgule ( ;) pour joindre deux


lignes ensemble. Ceci est souvent fait pour économiser un peu d'espace
dans des ifinstructions simples.
La barre oblique inverse ( \) sert un objectif similaire, mais opposé : elle
indique au shell que ce n'est pas la fin de la ligne, mais que la ligne
suivante doit être traitée comme faisant partie de la ligne courante. C'est
utile pour la lisibilité. Il est d'usage d'indenter la ligne suivante après une
barre oblique inverse ( \) ou un point virgule ( ;).
Par exemple, le point-virgule ( ;) est souvent utilisé comme ceci pour
joindre les mots-clés ifet then:
if [ "$X" -nt "/etc/passwd" ] ; alors [ "$X" - nt
"/etc/passwd" ]; alors
echo "X est un fichier plus récent que /etc/passwd""X
est un fichier plus récent que /etc/passwd"
FiFi
tandis que la barre oblique inverse ( \) est utilisée pour diviser la
commande à une seule ligne sur deux lignes dans le fichier de script shell,
à des fins de lisibilité :
[ "$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"
Comme nous le voyons dans ces exemples, testpeut effectuer de
nombreux tests sur les nombres, les chaînes et les noms de fichiers.
Merci à Aaron d'avoir souligné que -a, -e(les deux signifiant "fichier
existe"), -S(fichier est un socket), -nt(fichier est plus récent que), -
ot(fichier est plus ancien que), -ef(les fontes références au même fichier )
et -O(fichier appartenant à l'utilisateur exécutant le test) ne sont pas
disponibles dans le shell Bourne traditionnel (par exemple, /bin/sh sur
Solaris, AIX, HPUX, etc.).
Il existe une manière plus simple d'écrire des ifinstructions : les
commandes &&et ||donnent du code à effectuer si le résultat est vrai ou
faux, respectivement.
#!/bin/ch
[ $X -ne 0 ] && echo "X n'est pas nul" || echo "X est zéro"[
$X - ne 0 ] && echo "X n'est pas nul" || echo "X est zéro"
[ -f $X ] && echo "X est un fichier" || echo "X n'est pas un
fichier"[ - f $X ] && echo "X est un fichier" || echo "X n'est
pas un fichier"
[ -n $X ] && echo "X est de longueur non nulle" || \[ - n $X ]
&& echo "X est de longueur non nulle" || \
echo "X est de longueur nulle" "X est de longueur nulle"
Cette syntaxe est possible car il existe un fichier (ou un shell intégré) [qui
est appelé lié à test. Soyez prudent en utilisant cette construction,
cependant, car une utilisation excessive peut conduire à un code très
difficile à lire. La if...then...else...structure est beaucoup plus
lisible. L'utilisation de la [...]construction est recommandée pour les
boucles tandis que et les vérifications d'intégrité triviales avec obligent
vous ne voulez pas trop distraire le lecteur.
Notez que lorsque vous utilisez X sur une valeur non numérique, les
premières comparaisons entraînent le message :
[Link] : [ : expression entière attendue avant -lt . sh : [ :
expression entière attendue avant - lt
[Link] : [ : expression entière attendue avant -gt . sh : [ :
expression entière attendue avant - gt
[Link] : [ : expression entière attendue avant -le . sh : [ :
expression entière attendue avant - le
[Link] : [ : expression entière attendue avant -ge . sh : [ :
expression entière attendue avant - ge
En effet, les comparaisons -lt, -gt, -le et -ge ne sont conçues que pour les
entiers et ne fonctionnent pas sur les chaînes. Les comparaisons de
chaînes, telles que !=traiteront volontiers "5" comme une chaîne, mais il
n'y a aucun moyen sensé de traiter "Hello" comme un entier, donc les
comparaisons d'entiers se plaignent.
Si vous voulez que votre script shell se comporte plus gracieusement,
vous devrez vérifier le contenu de la variable avant de la tester - peut-être
quelque chose a choisi comme ceci :

echo -fr "Veuillez deviner le nombre magique : "- fr "Veuillez


deviner le nombre magique : "
lire X
echo $X | | grep "[^0-9]" > /dev/null 2>&1"[^0-9]" > / dev /
null 2 >& 1
si [ "$?" [ "$ ?" -eq "0" ] ; alorséq "0" ] ;
# Si le grep a trouvé autre chose que 0-9# Si le grep a
trouvé autre chose que 0-9
# alors ce n'est pas un entier.# alors ce n'est pas un
entier.
echo "Désolé, je voulais un numéro""Désolé, je voulais un
numéro"
autre
# Le grep n'a trouvé que 0-9, donc c'est un entier. # Le
grep n'a trouvé que 0-9, donc c'est un entier.
# Nous pouvons en toute sécurité faire un test dessus. #
Nous pouvons en toute sécurité faire un test dessus.
si [ "$X" -eq "7" ] ; alors si [ "$X" - éq "7" ] ; alors
echo "Vous avez entré le nombre magique !" "Vous avez
entré le nombre magique !"
Fi Fi
Fi Fi
De cette façon, vous echopouvez envoyer un message plus significatif à
l'utilisateur et quitter avec élégance. La $?variable est expliquée
dans Variables - Partie II , et grepest une bête compliquée, alors
voilà : grep [0-9]trouvez les lignes de texte qui contiennent des chiffres
(0-9) et éventuellement d'autres caractères, donc le caret ( ^) dans grep
[^0-9]ne trouve que les lignes qui ne se composent que de chiffres. On
peut alors prendre l'inverse (en agissant sur l'échec, pas sur la
réussite). D'accord ? Le >/dev/null 2>&1dirige toute sortie ou erreur vers
le périphérique "null" spécial, au lieu d'aller vers l'écran de l'utilisateur.
Un grand merci à Paul Schermerhorn pour m'avoir corrigé - cette page
prétendait que grep -v [0-9]cela fonctionnerait, mais c'est clairement
beaucoup trop simpliste.
Nous pouvons utiliser test in while boucles comme suit :
[Link]

#!/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 )

Vous avez dit :Vous avez dit :


$
Cela peut être rangé avec un autre test dans la boucle :
#!/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
si [ -n "$X" ]; alorssi [ - n "$X" ] ; alors
echo "Vous avez dit : $X""Vous avez dit : X $"
FiFi
FiniFini
Notez également que j'ai utilisé deux syntaxes différentes pour ifles
déclarations sur cette page. Ceux-ci sont :
si [ "$X" -lt "0" ] [ "$X" - lt "0" ]
alorsalors
echo "X est inférieur à zéro""X est inférieur à zéro"
FiFi

.......... et .................. et ........

si [ -n "$X" ]; alorssi [ - n "$X" ] ; alors


echo "Vous avez dit : $X""Vous avez dit : X $"
FiFi

Vous devez avoir une pause entre l' ifinstruction et


la thenconstruction. Cela peut être un point-virgule ou un retour à la ligne,
peu importe lequel, mais il doit y avoir l'un ou l'autre entre le ifet
le then. Ce serait bien de dire simplement :
si [ -n "$X" ] [ - n "$X" ]
echo "Vous avez dit : $X""Vous avez dit : X $"
mais les thenet fisont absolument obligatoires.
9. Cas

L' caseinstruction évite de parcourir tout un ensemble d' if .. then ..


elseinstructions. Sa syntaxe est vraiment très simple :

[Link]
#!/bin/ch

echo "Parlez-moi s'il vous plait..." while : do

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 !

D'accord, ce n'est donc pas le meilleur causeur du monde ; ce n'est qu'un


exemple !

essayer de l'exécuter et vérifier comment cela fonctionne...


.$ / conversation . s'il te
plaît , parle-moi ...
salut
Salut toi-même ! Que pensez-vous de la politique ?

Désolé, je ne comprends pas


au revoir
À la prochaine !

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

Il existe un ensemble de variables qui sont déjà définies pour vous, et la


plupart d'entre elles ne peuvent pas avoir de valeurs qui leur sont
attribuées.
Ceux-ci peuvent contenir des informations utiles, qui peuvent être utilisées
par le script pour connaître l'environnement dans lequel il s'exécute.

Le premier ensemble de variables que nous examinons sont $0 ..


$9et $#.
La variable $0est le nom de base du programme tel qu'il a été appelé.
$1 .. $9sont les 9 premiers paramètres supplémentaires avec lesquels le
script a été appelé.
La variable $@est tous les paramètres $1 .. whatever. La variable $*, est
similaire, mais ne conserve aucun espace, et entre guillemets, donc
"Fichier avec espaces" devient "Fichier" "avec" "espaces". Ceci est
similaire à echoce que nous avons vu dans A First Script . En règle
générale, utilisez $@et évitez $*.
$#est le nombre de paramètres avec lesquels le script a été appelé.
Prenons un exemple de script :
[Link]

#!/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 :

echo "Mon nom est `basename $0`"


"Mon nom est `basename $0`"
$#et $1 .. $9sont définis automatiquement par le shell.
Nous pouvons prendre plus de 9 paramètres en utilisant
la shiftcommande ; regardez le script ci-dessous :

[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

tentera de s'exécuter, /usr/local/bin/my-commandce qui devrait se


terminer avec une valeur de zéro si tout s'est bien passé, ou une valeur
différente de zéro en cas d'échec. Nous pouvons ensuite gérer cela en
vérifiant la valeur de $?après avoir appelé la commande. Cela permet de
rendre les scripts robustes et plus intelligents.
Les candidatures qui se composent bien doivent retourner zéro en cas de
succès. D'où la citation :
L'une des principales causes de la chute de l'Empire romain était que,
faute de zéro, ils n'avaient aucun moyen d'indiquer la fin réussie de leurs
programmes C. (Robert Firth)
Les deux autres variables principales définies pour vous par
l'environnement sont $$et $!. Ce sont deux numéros de processus.
La $$variable est le PID (Process IDentifier) du shell en cours
d'exécution. Cela peut être utile pour créer des fichiers temporaires, par
exemple /tmp/my-script.$$si plusieurs instances du script peuvent se
produire en même temps et qu'elles ont tous besoin de leurs propres
fichiers temporaires.
La $!variable est le PID du dernier processus d'arrière-plan effectué. Ceci
est utile pour suivre le processus au fur et à mesure de son travail.
Une autre variable intéressante est IFS. C'est le séparateur de champ
interne . La valeur par défaut est SPACE TAB NEWLINE, mais si vous la
modifiez, il est plus simple d'en faire une copie, comme indiqué :
[Link]

#!/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).

Utilisation des valeurs par défaut


Considérez l'extrait de code suivant qui invite l'utilisateur à entrer, mais
acceptez les valeurs par défaut :

#!/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 :

echo - fr "Quel est ton nom [ `whoami` ] "


Lis mon nom
echo "Votre nom est : ${myname:-`whoami`}"
Cela pourrait être considéré comme un cas particulier - nous utilisons la
sortie de la commande whoami, qui imprime votre nom de connexion
(UID). L'exemple le plus canonique consiste à utiliser le texte fixe, comme
ceci :

echo "Votre nom est : ${myname:-John Doe}"


Comme pour les autres utilisations des backticks, `whoami`s'exécute dans
un sous-shell, de sorte que toute cdcommande ou définition de toute autre
variable, dans les backticks, n'affectera pas le shell en cours d'exécution.

Utilisation et définition des valeurs par défaut


Il existe une autre syntaxe, ":=", qui définit la variable sur la valeur par
défaut si elle n'est pas définie :

echo "Votre nom est : ${myname:=John Doe}"


Cette technique signifie que tout accès ultérieur à la $mynamevariable
obtiendra toujours une valeur, soit saisie par l'utilisateur, soit "John Doe"
dans le cas contraire.

12. Programmes externes

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.

De cette façon, nous n'exécutons le ralenti findqu'une seule fois, ce qui


réduit de moitié environ le temps d'exécution du script.
Nous discuterons d'exemples spécifiques plus loin dans la section Trucs et
astuces de ce didacticiel.
13. Fonctions

Une caractéristique souvent négligée de la programmation de scripts shell


Bourne est que vous pouvez facilement écrire des fonctions à utiliser dans
votre script. Cela se fait généralement de deux manières; avec un simple
script, la fonction est simplement déclarée dans le même fichier que celui
où elle est appelée.
Cependant, lors de l'écriture d'une suite de scripts, il est souvent plus
facile d'écrire une "bibliothèque" de fonctions utiles et de sourcer ce
fichier au début des autres scripts qui utilisent les fonctions. Cela sera
montré plus tard .
La méthode est la même mais c'est fait; nous utiliserons principalement la
première méthode ici. La deuxième méthode (bibliothèque) est
fondamentalement la même, sauf que la commande

. ./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
}

### Le script principal commence ici

echo "Le script a été appelé avec $@"


x=1
echo "x est $x"
mafonction 1 2 3
echo "x est $x"
Le script, lorsqu'il est appelé en tant que [Link] a b c, donne la sortie
suivante :

Le script a été appelé avec abc


x est 1
Je m'appelais comme : 1 2 3
x est 2
Les $@paramètres sont modifiés dans la fonction pour refléter la façon
dont la fonction a été appelée. La variable x, cependant, est effectivement
une variable globale - myfuncelle a été modifiée, et cette modification est
toujours effective lorsque le contrôle revient au script principal.
Une fonction sera appelée dans un sous-shell si sa sortie est canalisée
ailleurs - c'est-à-dire que " myfunc 1 2 3 | tee [Link]" dira toujours "x
est 1" la deuxième fois. C'est parce qu'un nouveau processus shell est
appelé pour pipe myfunc(). Cela peut rendre le débogage très
frustrant ; Astrid avait un script qui a soudainement échoué lorsque le " |
tee" a été ajouté, et il n'est pas immédiatement évident pourquoi cela doit
être. Le teedoit être démarré avant la fonction à gauche du tuyau ; avec
l'exemple simple de " ls | grep foo", alors grepdoit être démarré en
premier, avec son stdinensuite lié au stdoutde lsune
fois lsdémarré. Dans le script shell, le shell a déjà été démarré avant
même que nous sachions que nous allions passer par tee, donc le système
d'exploitation doit démarrertee, puis démarrez un nouveau shell pour
appeler myfunc(). C'est frustrant, mais cela vaut la peine d'en être
conscient.
Les fonctions ne peuvent pas non plus modifier les valeurs avec lesquelles
elles ont été appelées - cela doit être fait en modifiant les variables elles-
mêmes, et non les paramètres transmis au script.
Un exemple le montre plus clairement :
#!/bin/ch

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"
}}

### Le script principal commence ici### Le script principal


commence ici

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
}}

pendant que :tandis que :


fairefaire
echo "Entrez un nombre :""Entrez un numéro :"
lire x
factorielle $x
Fini Fini
Comme promis, nous allons maintenant discuter brièvement de l'utilisation
des bibliothèques entre les scripts shell. Celles-ci peuvent également être
utilisées pour définir des variables communes, comme nous le verrons.
[Link]

# [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 $

pour je dans *$FROMpour je dans * $FROM


fairefaire
j=`nom de base $i $FROM`= `nom de base $i $FROM`
mv $i ${j}$TO{ j } $TO
FiniFini
}}
[Link]

#!/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"
}}

## Le script principal commence ici## Le script principal


commence ici

adduser bob letmein Bob Holness de BlockbustersBob Holness de


Blockbusters
ADDUSER_RETURN_CODE=$ ?= $ ?
if [ "$ADDUSER_RETURN_CODE" -eq "1" ] ; alorsif [
"$ADDUSER_RETURN_CODE" - eq "1" ] ; alors
echo "Quelque chose s'est mal passé avec useradd""Quelque
chose s'est mal passé avec useradd"
elif [ "$ADDUSER_RETURN_CODE" -eq "2" ] ; alorselif [
"$ADDUSER_RETURN_CODE" - eq "2" ] ; alors
echo "Quelque chose s'est mal passé avec passwd""Quelque
chose s'est mal passé avec passwd"
autreautre
echo "Bob Holness ajouté au système.""Bob Holness a ajouté
au système."
FiFi
Ce script vérifie les deux appels externes qu'il effectue
( useraddet passwd) et informe l'utilisateur s'ils échouent. La fonction
définit ensuite un code de retour de 1 pour indiquer tout problème
avec useradd, et 2 pour indiquer tout problème avec passwd. De cette
façon, le script appelant sait où se situe le problème.
Pendant longtemps, ce tutoriel cochait "$?" les deux fois, plutôt que de
définir ADDUSER_RETURN_CODE=$?, puis de regarder la valeur
de ADDUSER_RETURN_CODEchaque fois. C'était un bug (merci à Elyza de
l'avoir signalé). Vous devez enregistrer $?, car dès que vous exécutez une
autre commande, telle que if, sa valeur sera remplacée. C'est pourquoi
nous sauvegardons la adduservaleur de retour dans
la $ADDUSER_RETURN_CODEvariable, avant d'agir sur son
contenu. $ADDUSER_RETURN_CODEest certain de rester le même; $?
changera à chaque commande exécutée.
14. Trucs et astuces

Consultez [Link]/tips/ pour plus de conseils et astuces à jour


Le contenu ci-dessous est, pour être honnête, plutôt obsolète. la partie
/tips de ce site Web contient de plus en plus de conseils utilisables.

Unix regorge d'utilitaires de manipulation de texte, dont certains des plus


puissants seront abordés dans cette section de ce didacticiel. La
signification de ceci est que pratiquement tout sous Unix est du
texte. Pratiquement tout ce à quoi vous pouvez penser est contrôlé soit
par un fichier texte, soit par une interface de ligne de commande (CLI). La
seule chose que vous ne pouvez pas automatiser à l'aide d'un script shell
est un utilitaire ou une fonctionnalité uniquement graphique. Et sous Unix,
il n'y en a pas trop !
Vous avez peut-être entendu dire qu'avec *nix, "tout est un fichier" - c'est
vrai.
Nous avons quelques sous-sections ici... ce qui suit est des conseils
généraux, des trucs et astuces.
Script CGI
Codes de sortie et contrôle de flux
Attendez-vous à un remplacement simple
Utiliser le piège pour savoir quand vous avez été interrompu - comme un
CTRL-C, etc.
Solution de contournement pour la dichotomie 'echo -n' vs 'echo \c'
Exemple documenté d'un script réel que j'ai écrit - il aide les utilisateurs à
configurer un modem SpeedTouch et est disponible
sur [Link] .
Nous avons déjà montré plus haut une utilisation de la commande simple
mais efficace cut. Nous discuterons ici de quelques exemples parmi les
programmes externes les plus courants à utiliser.
grepest un utilitaire extrêmement utile pour le programmeur de script
shell.
Un exemple de grep serait :
#!/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 : $steves""Les entrées sont : $steves"
Ce script semble correct s'il n'y a qu'une seule
correspondance. Cependant, s'il y a deux lignes dans /etc/passwd
contenant le mot « steve », alors le shell interactif affichera :
$> grep -i steve /etc/passwd> grep - je steve / etc / passwd
steve:x:5062:509:Steve Parker:/home/steve:/bin/bash: x : 5062
: 509 : Steve Parker :/ home / steve :/ bin / bash
fred:x:5068:512:Fred Stevens:/home/fred:/bin/bash: x : 5068 :
512 : Fred Stevens :/ home / fred :/ bin / bash
$> grep -i steve /etc/passwd |cut -d: -f1> grep - i steve /
etc / passwd | couper - d : - f1
Steve
Fred

Mais le script affichera :


Les entrées sont : steve fred
sont : steve fred
En mettant le résultat dans une variable, nous avons changé les NEWLINEs
en espaces ; la page de shmanuel nous indique que le premier caractère
de $IFSsera utilisé à cette fin. IFS est <space><tab><cr> par
défaut. Peut-être que nous voulions garder les NEWLINEs : cela pourrait
être mieux si nous transformions les espaces en NEWLINEs... C'est un
travail pourtr :
#!/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'"$Steve" | tr ' ' '\012'
Notez que trtraduit les espaces en caractère octal 012 (NEWLINE).
Une autre utilisation courante de trest son utilisation de range... il peut
convertir du texte en majuscules ou en minuscules, par exemple :

#!/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 :

Cette ligne est [Link] ligne est correcte .


Cette ligne contient un gros mot. Traiter avec [Link]
ligne contient un mauvais mot . Traiter avec soin .
Cette ligne est bien [Link] ligne est très bien aussi .
Grep supprimerait toute la deuxième ligne, ne laissant qu'un fichier de
deux lignes ; sed changerait le fichier pour lire :
Cette ligne est [Link] ligne est correcte .
Cette ligne contient un . Traiter avec [Link] ligne
contient un . Traiter avec soin .
Cette ligne est bien [Link] ligne est très bien aussi .
Indice Telnet
C'est une technique utile que j'ai récupérée de l'utilitaire Explorer de
Sun. Bien que telnet ne soit plus utilisé sur les serveurs, il est toujours
utilisé par certains périphériques réseau, tels que les concentrateurs de
terminaux, etc. En créant un script comme celui-ci, votre propre script ou
à partir d'une ligne de commande, vous pouvez exécuter :
$ ./[Link] | telnet
./ telnet1 . sh | telnet
Quelques personnes m'ont posé des questions à ce sujet et ont eu
tendance à les orienter vers la suite de code attendue, qui est assez
complexe et volumineuse; ce code devrait être assez portable entre les
systèmes (tant qu'ils ont egrep). Si cela ne fonctionne pas sur votre
système, essayez d'utiliser GNU grep avec le commutateur -q, ou un grep
propriétaire et dirigez-vous vers /dev/null. C'est quand même beaucoup
plus facile que prévu pour l'installation.
[Link]

#!/bin/ch
hôte=[Link]= 127,0 . 0,1
port=23= 23
connexion = steve= Steve
passwd=bonjourlà= bonjour ici
cmd="ls /tmp"= "ls /tmp"

echo open ${host} ${port}{ hôte } $ { port }


dormir 11
echo ${login}{ connexion }
dormir 11
echo ${passwd}{ mot de passe }
dormir 11
echo ${cmd}{ cmd }
dormir 11
sortie d'écho
Cependant, Sun ajoute un code intelligent de vérification des erreurs
(notez que les variables que vous pouvez définir et exporter à partir de
votre shell ou script shell actuel, pour éviter de stocker les mots de passe
dans des fichiers lisibles) :
$ ./[Link] | telnet > fichier1
./ telnet2 . sh | telnet > fichier1
[Link]

#!/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="$"= "$"

echo open ${host} ${port}{ hôte } $ { port }


dormir 11
tout=${timeout}= $ { délai d'attente }
tandis que [ "${tout}" -ge 0 ]tandis que [ "${tout}" - ge 0 ]
fairefaire
si tail -1 "${fichier}" 2>/dev/null | egrep -e "login:"
> /dev/nullsi queue - 1 "${fichier}" 2 >/ dev / null | egrep -
e "login:" > / dev / null
alorsalors
echo "${login}""${connexion}"
dormir 11
tout=-5=- 5
ContinuezContinuez
autreautre
dormir 11
tout=`expr ${tout} - 1`= `expr ${tout} - 1`
FiFi
FiniFini

if [ "${tout}" -ne "-5" ]; alorsif [ "${tout}" -ne " -5 " ];


alors
sortie 11
FiFi

tout=${timeout}= $ { délai d'attente }


tandis que [ "${tout}" -ge 0 ]tandis que [ "${tout}" - ge 0 ]
fairefaire
si tail -1 "${fichier}" 2>/dev/null | egrep -e "Mot de
passe :" > /dev/nullsi queue - 1 "${fichier}" 2 >/ dev / null
| egrep -e " Mot de passe :" > / dev / null
alorsalors
echo "${passwd}""${passwd}"
dormir 11
tout=-5=- 5
ContinuezContinuez
autreautre
si tail -1 "${fichier}" 2>/dev/null | egrep -e "$
{invite}" > /dev/nullsi queue - 1 "${fichier}" 2 >/ dev / null
| egrep - e "${invite}" > / dev / null
alorsalors
tout=-5=- 5
autreautre
dormir 11
tout=`expr ${tout} - 1`= `expr ${tout} - 1`
FiFi
FiFi
FiniFini

if [ "${tout}" -ne "-5" ]; alorsif [ "${tout}" -ne " -5 " ];


alors
sortie 11
FiFi

> ${fichier}> $ { fichier }

echo ${cmd}{ cmd }


dormir 11
sortie d'écho
Notez qu'avec cette version, la sortie est saisie dans file1, et que ce
fichier est en fait utilisé par le script pour vérifier sa progression. J'ai
ajouté "> ${file}"pour que la sortie reçue dans le fichier ne soit que la
sortie de la commande, pas le processus de connexion également.

15. Référence rapide

Il s'agit d'un guide de référence rapide sur la signification de certaines des


commandes et des codes les moins faciles à deviner des scripts shell. De
par leur nature, ils sont également assez difficiles à trouver à l'aide des
moteurs de recherche. Ces exemples incluent la gestion des
processus , les arguments des scripts shell et les conditions
des scripts shelltest .
Comman
de La description Exemple

Exécutez la commande
& précédente en arrière-plan ls &

if [ "$foo" -ge "0" ] &&


&& ET logique [ "$foo" -le "9"]

if [ "$foo" -lt "0" ] ||


|| OU logique [ "$foo" -gt "9" ]

^ Début de ligne grep "^foo"

$ Fin de ligne grep "foo$"

= Égalité de chaîne (cf. -eq) if [ "$foo" = "bar" ]

! NON logique if [ "$foo" != "bar" ]

$$ PID du shell actuel echo "my PID = $$"


PID de la dernière
$! commande en arrière-plan ls & echo "PID of 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 premier paramètre


1$ de la commande courante echo "My first argument is $1"

Nom du neuvième
paramètre de la
9$ commande actuelle echo "My ninth argument is $9"

Tous les paramètres de la


commande actuelle (en
préservant les espaces et
$@ les guillemets) echo "My arguments are $@"

Tous les paramètres de la


commande actuelle (ne
préservant pas les espaces
$* et les guillemets) echo "My arguments are $*"

-eq Égalité numérique if [ "$foo" -eq "9" ]

-ne Inégalité numérique if [ "$foo" -ne "9" ]

-lt Moins que if [ "$foo" -lt "9" ]

-le Inférieur ou égal if [ "$foo" -le "9" ]

-gt Plus grand que if [ "$foo" -gt "9" ]

-ge Meilleur que ou égal if [ "$foo" -ge "9" ]

La chaîne est de longueur


-z nulle if [ -z "$foo" ]

La chaîne n'est pas de


-n longueur nulle if [ -n "$foo" ]

-NT Plus récent que if [ "$file1" -nt "$file2" ]

-ré Est un directeur if [ -d /bin ]


-F est un fichier if [ -f /bin/ls ]

-r Est un fichier lisible if [ -r /bin/ls ]

-w Est un fichier inscriptible if [ -w /bin/ls ]

-X Est un fichier exécutable if [ -x /bin/ls ]

function myfunc() { echo


( ... ) Définition de la fonction hello }

16. Coquille interactive

Voici quelques conseils rapides pour utiliser le shell UNIX ou Linux de


manière interactive. Personnellement, je recommande le shell bash pour la
plupart des utilisations interactives ; il est disponible sur à peu près toutes
les saveurs *nix, et très agréable à utiliser comme shell de
connexion. Cependant, le shell racine doit toujours être /bin/sh, qu'il pointe
vers bash ou Bourne shell.
frapper

bash dispose d'outils de recherche d'historique très pratiques ; les touches


fléchées haut et bas feront défiler l'historique des commandes
précédentes. Plus utilement, Ctrl + r effectuera une recherche inversée,
correspondant à n'importe quelle partie de la ligne de
commande. Appuyez sur ESC et la commande sélectionnée sera collée
dans le shell actuel pour que vous puissiez la modifier si nécessaire.
Si vous souhaitez répéter une commande que vous avez exécutée
auparavant et que vous savez par quels caractères elle a commencé, vous
pouvez le faire :
bash$ ls / tmp
( liste des fichiers dans / tmp )
bash$ touch / tmp / foo
bash$ ! je
ls / tmp
( liste des fichiers dans / tmp , incluant maintenant / tmp /
foo )
En plus des touches fléchées, vous pouvez utiliser PageUp et PageDn pour
naviguer jusqu'au début et à la fin de la ligne de commande.
ksh

Vous pouvez rendre ksh plus utilisable en ajoutant des commandes


d'historique, en mode viou emacs. Il existe plusieurs façons de procéder,
selon les circonstances exactes. set -o vi, ksh -o vi, ou exec ksh -o
vi(où "vi" peut être remplacé par "emacs" si vous préférez le mode
emacs).
Si vous souhaitez démarrer une session ksh à partir d'un autre shell
interactif, vous pouvez simplement appeler ksh comme ceci :
csh % # oh non, c'est csh !
csh % ksh
ksh$ # ouf, c'est mieux
ksh$ # faites quelques trucs sous ksh
ksh$ # puis laissez-le à l'invite csh :
ksh$ sortie
csh %
Cela démarrera une nouvelle session ksh, que vous pourrez quitter et
revenir au shell précédent. Alternativement, vous pouvez remplacer le csh
(ou n'importe quel shell) par un shell ksh, avec la execcommande :
csh % # oh non, c'est csh !
csh % exec ksh
ksh$ # faire des trucs sous ksh
ksh$ sortie

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] ]

Supprimez l'ancien enregistrement et ajoutez le nouveau. Vous pouvez


également modifier l'enregistrement existant, bien que cela puisse être
plus difficile.
Points bonus
o Autoriser les options d'annulation (utiliser des entrées
"spéciales" (^d, CR, ^c, etc))
o Ajouter l'option "Confirmer".
o Offrez des modes interactifs / non interactifs. (c'est-à-dire une
version basée sur un menu et une option basée sur la ligne de commande
(CLI).
o Jouez avec getopt pour la version CLI.
Conseils / Choses à penser
o Format d'enregistrement suggéré : Utilisez les deux-points pour
séparer les champs.
John Smith : 54321:john@[Link]
o De cette façon, vous pouvez utiliser "cut -d:" pour lire les champs.
o Pensez à utiliser IFS comme alternative à cette méthode.
o Pensez à utiliser l'espace (" ") pour séparer les champs et
convertissez tous les espaces de l'entrée en traits de soulignement ("_"),
puis reconvertissez-les pour l'affichage.
Une réponse que j'ai trouvée est ci-dessous; ne lisez que si vous êtes
# [Link]
# (c) Steve Parker

# 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
}

Vous aimerez peut-être aussi