|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Auteur: Emmanuel Viennet pour GTR 2ème année (23/09/04) Introduction au Système UNIXSéance 3 : Shell bashPourquoi utiliser bash ?Bash est une version évoluée du shell sh (le "Bourne shell"). Le shell peut être utilisé comme un simple interpréteur de commande, mais il est aussi possible de l'utiliser comme langage de programmation interprété (scripts). La connaissance du shell est indispensable au travail de l'administrateur unix :
Autres versions de shellIl existe plusieurs versions de shell : sh (ancêtre de bash), csh (C shell), ksh (Korn shell), zsh, etc. Nous avons choisi d'enseigner bash car il s'agit d'un logiciel libre, utilisé sur toutes les distributions récentes de Linux et de nombreuses autres variantes d'UNIX. Connaissant bash, l'apprentissage d'un autre shell sur le terrain ne devrait pas poser de difficultésShell ou Python ?Nous avons vu qu'il était possible d'écrire des programmes en shell. Pour de nombreuses tâches simples, c'est effectivement très commode. Néanmoins, le langage shell est forcément assez limité; pour des programmes plus ambitieux il est recommandé d'utiliser des langages plus évolués comme Python ou Perl, voire des langages compilés (C, C++) si l'on désire optimiser au maximum les performances (au prix de coûts de développement plus importants).Les mauvais côtés des shellLe shell possède quelques inconvénients :
Variables et évaluationLes variables sont stockées comme des chaînes de caractères. Les variables d'environnement sont des variables exportées aux processus (programmes) lancés par le shell. Les variables d'environnement sont gérées par UNIX, elles sont donc accessibles dans tous les langages de programmation (voir plus loin). Pour définir une variable :$ var='ceci est une variable'Attention : pas 'espaces autour du signe égal. Les quotes (apostrophes) sont nécessaires si la valeur contient des espaces. C'est une bonne habitude de toujours entourer les chaînes de caractères de quotes. Pour utiliser une variable, on fait précéder son nom du caractère "$" : $ echo $varou encore : $ echo 'bonjour, ' $varDans certains cas, on doit entourer le nom de la variable par des accolades :
$ X=22
$ echo Le prix est ${X}0 euros
affiche "Le prix est de 220 euros" (sans accolades autour de X, le shell ne
pourrait pas savoir que l'on désigne la variable X et non la variable X0).
Lorsqu'on utilise une variable qui n'existe pas, le bash renvoie une chaîne
de caractère vide, et n'affiche pas de message d'erreur (contrairement à la
plupart des langages de programmation, y compris csh) :
$ echo Voici${UNE_DROLE_DE_VARIABLE}!
affiche "Voici!".
Pour indiquer qu'une variable doit être exportée dans l'environnement (pour
la passer aux commandes lancée depuis ce shell), on utilise la commande
export :
$ export Xou directement : $ export X=22 Accès aux variables d'environnement dans des programmes
Évaluation, guillemets et quotesAvant évaluation (interprétation) d'un texte, le shell substitue les valeurs des variables. On peut utiliser les guillemets (") et les quotes (') pour modifier l'évaluation :
Expressions arithmétiquesNormalement, bash traite les valeurs des variables comme des chaînes de caractères. On peut effectuer des calculs sur des nombres entiers, en utilisant la syntaxe $(( ... )) pour délimiter les expressions arithmétiques:$ n=1 $ echo $(( n + 1 )) 2 $ p=$((n * 5 / 2 )) $ echo $p 2 Découpage des cheminsLes scripts shell manipulent souvent chemins (pathnames) et noms de fichiers. Les commandes basename et dirname sont très commodes pour découper un chemin en deux partie (répertoires, nom de fichier) :$ dirname /un/long/chemin/vers/toto.txt /un/long/chemin/vers $ basename /un/long/chemin/vers/toto.txt toto.txt Évaluation de commandesIl est courant de stocker le résultat d'une commande dans une variable. Nous entendons ici par "résultat" la chaîne affichée par la commande, et non son code de retour. Bash utilise plusieurs notations pour cela : les back quotes (`) ou les parenthèses :$ REP=`dirname /un/long/chemin/vers/toto.txt` $ echo $REP /un/long/chemin/versou, de manière équivalente : $ REP=$(dirname /un/long/chemin/vers/toto.txt) $ echo $REP /un/long/chemin/vers(attention encore une fois, pas d'espaces autour du signe égal). La commande peut être compliquée, par exemple avec un tube : $ Fichiers=$(ls /usr/include/*.h | grep std) $ echo $Fichiers /usr/include/stdint.h /usr/include/stdio_ext.h /usr/include/stdio.h /usr/include/stdlib.h /usr/include/unistd.h Découpage de chaînesBash possède de nombreuses fonctionnalités pour découper des chaînes de caractères. L'une des plus pratiques est basée sur des motifs. La notation ## permet d'éliminer la plus longue chaîne en correspondance avec le motif :
$ Var='tonari no totoro'
$ echo ${Var##*to}
ro
ici le motif est *to, et la plus longue correspondance "tonari no
toto"1. Cette forme est utile pour
récupérer l'extension (suffixe) d'un nom de fichier :
$ F='rep/bidule.tgz'
$ echo ${F##*.}
tgz
La notation # (un seul #) est similaire mais élimine la
plus courte chaîne en correspondance :
$ Var='tonari no totoro'
$ echo ${Var#*to}
nari no totoro
De façon similaire, on peut éliminer la fin d'une chaîne :
$ Var='tonari no totoro'
$ echo ${Var%no*}
tonari
Ce qui permet de supprimer l'extension d'un nom de fichier :
$ F='rep/bidule.tgz'
$ echo ${F%.*}
rep/bidule
% prend la plus courte correspondance, et %% prend la plus
longue :
$ Y='archive.tar.gz'
$ echo ${Y%.*}
archive.tar
$ echo ${Y%%.*}
archive
Exécution conditionnelleL'instruction if permet d'exécuter des instructions si une condition est vraie. Sa syntaxe est la suivante :if [ condition ] then action fiaction est une suite de commandes quelconques. L'indentation n'est pas obligatoire mais très fortement recommandée pour la lisibilité du code. On peut aussi utiliser la forme complète : if [ condition ] then action1 else action2 fiou encore enchaîner plusieurs conditions : if [ condition1 ] then action1 elif [ condition2 ] then action2 elif [ condition3 ] then action3 else action4 fi Opérateurs de comparaisonLe shell étant souvent utilisé pour manipuler des fichiers, il offre plusieurs opérateurs permettant de vérifier diverses conditions sur ceux-ci : existence, dates, droits. D'autres opérateurs permettent de tester des valeurs, chaînes ou numériques. Le tableau ci-dessous donne un aperçu des principaux opérateurs :
Scripts shellUn script bash est un simple fichier texte exécutable (droit x) commençant par les caractères #!/bin/bash (doivent être les premiers caractères du fichier). Voici un exemple de script :
#!/bin/bash
if [ "${1##*.}" = "tar" ]
then
echo $1 est une archive tar
else
echo $1 n\'est pas une archive tar
fi
Ce script utilise la variable $1, qui est le premier argument passé
sur la ligne de commande.
Arguments de la ligne de commandeLorsqu'on entre une commande dans un shell, ce dernier sépare le nom de la commande (fichier exécutable ou commande interne au shell) des arguments (tout ce qui suit le nom de la commande, séparés par un ou plusieurs espaces). Les programmes peuvent utiliser les arguments (options, noms de fichiers à traiter, etc). En bash, les arguments de la ligne de commande sont stockés dans des variables spéciales :
#!/bin/bash echo 'programme :' $0 echo 'argument 1 :' $1 echo 'argument 2 :' $2 echo 'argument 3 :' $3 echo 'argument 4 :' $4 echo "nombre d'arguments :" $# echo "tous:" $*Exemple d'utilisation, si le script s'appelle "myargs.sh" : $ ./myargs.sh un deux trois programme : ./myargs.sh argument 1 : un argument 2 : deux argument 3 : trois argument 4 : nombre d'arguments : 3 tous: un deux trois Autres structures de contrôleNous avons déjà évoqué l'instruction if et les conditions. On utilise souvent des répétitions (for) et des choix multiples (case).Boucle forComme dans d'autre langages (par exemple python), la boucle for permet d'exécuter une suite d'instructions avec une variable parcourant une suite de valeurs. Exemple :for x in un deux trois quatre do echo x= $x doneaffichera : x= un x= deux x= trois x= quatreOn utilise fréquemment for pour énumérer des noms de fichiers, comme dans cet exemple : for fichier in /etc/rc* do if [ -d "$fichier" ] then echo "$fichier (repertoire)" else echo "$fichier" fi doneOu encore, pour traiter les arguments passés sur la ligne de commande : #!/bin/bash for arg in $* do echo $arg done Instruction caseL'instruction case permet de choisir une suite d'instruction suivant la valeur d'une expression :case "$x" in go) echo "demarrage" ;; stop) echo "arret" ;; *) echo "valeur invalide de x ($x)'' esacNoter les deux ; pour signaler la fin de chaque séquence d'instructions. Définition de fonctionsIl est souvent utile de définir des fonctions. La syntaxe est simple :
mafonction() {
echo "appel de mafonction..."
}
mafonction
mafonction
qui donne :
appel de mafonction... appel de mafonction...Voici pour terminer un exemple de fonction plus intéressant :
tarview() {
echo -n "Affichage du contenu de l'archive $1 "
case "${1##*.}" in
tar)
echo "(tar compresse)"
tar tvf $1
;;
tgz)
echo "(tar compresse gzip)"
tar tzvf $1
;;
bz2)
echo "(tar compresse bzip2)"
cat $1 | bzip2 -d | tar tvf -
;;
*)
echo "Erreur, ce n'est pas une archive"
;;
esac
}
Plusieurs points sont à noter :
EXERCICE 1 - Créer un script test-fichier, qui précisera le type du fichier passé en paramètre, ses permissions d'accès pour l'utilisateur Exemple de résultats : Le fichier /etc est un répertoire "/etc" est accessible par root en lecture écriture exécution Le fichier /etc/smb.conf est un fichier ordinaire qui n'est pas vide "/etc/smb.conf" est accessible par jean en lecture. EXERCICE 2 - Afficher le contenu d'un répertoire Écrire un script bash listedir.sh permettant d'afficher le contenu d'un répertoire en séparant les fichiers et les (sous)répertoires. Exemple d'utilisation : $ ./listdir.shaffichera : -------------- Fichiers dans /etc/rc.d -------------------- rc rc.local rc.sysinit -------------- Repertoires dans /etc/rc.d -------------------- init.d rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d EXERCICE 3 - Lister les utilisateurs Écrire un script bash affichant la liste des noms de login des utilisateurs définis dans /etc/passwd ayant un UID supérieur à 500. Indication : for in $(cat /etc/passwd) permet de parcourir les lignes du dit fichier. EXERCICE 4 - lecture au clavier La commande bash read permet de lire une chaîne au clavier et de l'affecter à une variable. exemple : echo -n "Entrer votre nom: " read nom echo "Votre nom est $nom"La commande file affiche des informations sur le contenu d'un fichier (elle applique des règles basées sur l'examen rapide du contenu du fichier). Les fichier de texte peuvent être affiché page par page avec la commande more. 1- Tester ces trois commandes; 2- Écrire un script qui propose à l'utilisateur de visualiser page par page chaque fichier texte du répertoire spécifié en argument. Le script affichera pour chaque fichier texte (et seulement ceux là) la question "voulez vous visualiser le fichier machintruc ?". En cas de réponse positive, il lancera more, avant de passer à l'examen du fichier suivant. EXERCICE 5 - itération, chaînes de caractères, expressions 1- On a un répertoire peuplé de fichiers dont les noms sont de la forme dcp_1234.jpg ou DCP_1234.JPG, ou encore DCP_1234.jpg, etc, où 1234 est une suite de chiffres quelconques. Écrire un shell script qui renomme tous ces fichiers, pour obtenir photo_124.jpg (toujours en minuscules). Les script prendra les noms des fichiers à traiter en argument sur la ligne de commande. 2- On peut lancer un émulateur de terminal coloré avec la commande xterm -bg nom_de_couleurOn crée un fichier colors.txt contenant des noms de couleurs standards (voir par exemple /usr/X11R6/lib/X11/rgb.txt), un nom par ligne. La variable spéciale de bash $RANDOM permet de générer un nombre entier aléatoire entre 0 et 32767 (voir man bash). Écrire un script qui ouvre une fenêtre terminal avec un couleur de fond aléatoire, choisie dans la liste colors.txt. Ajouter un bouton à la barre d'outils qui lance un terminal coloré via votre script. Tester. EXERCICE 6 - Étude d'un script de service unix Ouvrir le fichier /etc/init.d/network dans Emacs (en lecture seule). ce script est responsable du lancement et de l'arrêt du service "réseau", et est en général lancé via la commande "service network start". Vous n'êtes sans doute pas encore en mesure de comprendre tous les détails de ce script, mais devez pouvoir en comprendre l'architecture et répondre aux questions suivantes. 1- Quels sont les arguments acceptés par le script sur la ligne de commande ? Que fait il si on donne un argument invalide ? 2- Que fait le script si le fichier /etc/sysconfig/network n'existe pas ? 3- Où est définie la fonction bash "action" ? 4- A quoi sert le fichier /var/lock/subsys/network ? Footnotes:1Pour ceux qui ne maîtrisent pas le japonais, je crois que $Var signifie "mon voisin Totoro".File translated from TEX by TTH, version 3.66. On 13 May 2005, 15:04. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||