-=[ Auteur: lia, plonky at gmail dot com ]=- Introduction Le shell est un outil fondamental dans les systèmes Unix. Il sert principalement d'interface entre l'utilisateur et le système d'exploitation. C'est lui en particulier qui s'occupe d'exécuter les commandes que l'utilisateur saisit. Mais sa fonction ne s'arrête pas là, il offre à l'utilisateur toute une palette de service, tel que la possibilité de manipuler les processus comme par exemple les communications par le biais des tubes, de manipuler des fichiers en utilisant les redirections. Il offre aussi un environnement d'exécution qui affecte aussi bien son comportement que le comportement des processus dont il a la charge. De part la diversité des systèmes Unix, et l'ancienneté de ces systèmes, il est normal que cela se ressente dans la variété des shells qui sont disponibles. Au fil des ans, certains shells se sont distingués, le korn shell (ksh), les shells tcsh et csh, le bourne shell (sh) et son poussin le bash. Il existe d'autres shells mais leur diffusion est limitée. Une caractéristique des shells est de posséder un langage de script. Un langage de script se distingue des langages de programmation habituels du fait qu'un script n'est pas transformé en exécutable binaire autonome qui contient du code machine, mais a besoin d'un programme qui interprète le script à chaque exécution de celui-ci. Un fameux langage de script est le dénommé BASIC qui fut un des langages de script les plus utilisés dans le monde. Comme le shell a pour vocation d'aider l'utilisateur dans la gestion des fichiers et des processus, les langages de script associés sont particulièrement adaptés à cet usage. L'écriture de script pour faciliter l'administration système est très courant dans l'industrie, qui bien souvent tend à minimiser les coûts humains de maintenance et gestion de ces systèmes. Dans la suite de ce document, nous nous attacherons particulièrement au langage de script 'bourne shell' (sh) qui est installé par défaut sur tout système Unix. Ce shell d'ailleurs ne présente pas beaucoup d'intérêt pour une utilisation interactive, mais le fait qu'il soit installé en standard sur tout type de système Unix, garantie la portabilité des scripts. Pour écrire un shell script il n'est pas besoin d'outil spécifique, seul votre éditeur de texte préféré fera l'affaire (emacs, vi , .). Il existe deux manières d'invoquer un script shell. La première est d'exécuter le shell dans lequel il est écrit, avec comme argument le nom du script (par exemple : sh monscript.sh). La deuxième manière consiste d'une part à rajouter au fichier comme première ligne un appel au shell dans lequel le script est programmé en commençant la ligne par ' # !' et en fournissant ensuite le chemin vers l'exécutable du shell en question ( dans notre cas cela donne : # !/bin/sh). Ensuite il suffit de rendre exécutable le script au yeux du système à l'aide de la commande chmod. Il suffit d'invoquer le script par son nom dans la ligne commande pour l'exécuter (par exemple ./monscript.sh). Le langage sh Le langage du shell sh possède des structures de control tel que IF..THEN..ELSE, FOR, WHILE. Il fournit plusieurs moyens d'accéder aux variables en lecture et en écriture. Il propose aussi une extension des possibilités de redirection, et un certain nombre de commande interne. Création de variable La syntaxe pour assigner une variable est la suivante : nom=valeur. Si cette variable n'existait pas auparavant, elle est de facto crée, sinon l'ancienne valeur est écrasée. Une variable fraîchement crée est toujours une variable locale. C'est à dire qu'elle appartient au contexte du shell en cours d'exécution et à ses processus fils. Pour assigner une valeur qui contient des espaces, il suffit d'englober la valeur autour de double guillemet (par exemple toto="toto ta"). Il est important qu'il n'y ai pas d'espace entre '=' et la valeur. Pour accéder aux variables, il existe plusieurs moyens : . $nom est remplacé par la valeur de la variable . ${nom} est remplacé par la valeur de la variable, cela est utile lorsque la chaîne de caractère est immédiatement suivie par une autre chaîne (${poids}kg, affichera 17kg si poid=17) . ${nom-mot} est remplacé par la valeur de nom si la variable est initialisée et par mot sinon . ${nom=mot} assigne la valeur mot à nom si cette variable n'est pas initialisée et est remplacé ensuite par la valeur de nom. . ${nom ?mot} est remplacé par la valeur de nom si cette variable est initialisée et sinon renvoie mot sur la sortie d'erreur et le script se termine. Si mot est vide alors un message d'erreur standard est affiché. Pour initialiser une variable à partir de l'entrée standard (généralement le clavier ou une redirection) il faut utiliser la commande read. Elle assigne successivement les mots entrés aux noms de variables donnés en argument. Si le nombre de mot est supérieur aux nombre de variable, alors la dernière variable écope du surplus de mot. Pour exporter une variable locale à l'environnement, il faut utiliser la commande export ( par exemple export nom=toto). La commande env permet d'afficher le contenu de l'environnement. Pour rendre une variable nom modifiable, il suffit d'utiliser la commande readonly, avec le nom de la variable en argument. Le shell sh fournit un certain de variable locales prédefinies. . $@ est la liste des paramètres de la dernière commande exécutée . $# est le nombre de paramètre de la dernière commande exécutée . $ ? est la valeur de sortie de la dernière commande exécutée . $$ est le PID du processus en cours d'exécution (généralement le shell qui interprète le script. Le shell sh possède un outil d'évaluation d'expression arithmétique nommé expr. Il supporte une variété d'opérateur : */%, +-, => >= < <= !=, &, | . Il supporte aussi des opérations sur les chaînes de caractères comme : match, substr, index, lenght. Le shell sh possède un outil de test d'expression judicieusement appelé test. Une liste non exhaustive des options est donnée : . -f nomdefichier, teste si le fichier nomdefichier existe . -l chaîne, teste sur la chaîne est non vide . -n chaîne, teste si la chaîne a au moins un caractère. . -z chaîne, teste si la chaîne est vide . str1 = str2 est vrai si str1 est égal à str2 . str1 != str2 est vrai si str1 est différent de str2 . int1 -eq int2 est vrai si int1 est égal à int2 . int1 -ne int2 est vrai si int1 est différent de int2 . int1 -gt int2 est vrai si int1 est plus grand que int2 . int1 -ge int2 est vrai si int1 est plus grand ou égal à int2 . int1 -lt int2 est vrai si int1 est plus petit que int2 . int1 -le int2 est vrai si int1 est plus petit ou égal à int2 . !expr est vrai si expr est faux . expr1 -a expr2 est vrai si exrp1 et expr2 sont vrais . expr1 -o expr2 est vrai si expr1 ou expr2 sont vrais. Les structures de contrôle La première structure est case .. in .. esac, qui permet plusieurs branchements suivant la valeur d'une variable. Par exemple : case $toto in "1") date ;; "2") pwd ;; *) echo choix illégale ;; La structure for.. do ..done permet de faire des traitement sur une variable qui va prendre des valeurs successives, ces valeurs étant fournit par une liste si cette variable n'est pas déjà une liste. Par exemple for i in *.zip do gunzip $i done La structure if ..then .. fi permet de faire des branchements conditionnels suite à un ou plusieurs tests. Par exemple : if test $nombre -eq 0 then echo 0 else echo non zero fi Il est possible de faire un test de manière différente, nous aurions pu écrire '[ $nombre -eq 0]' à la place de 'test $nombre -eq 0'. La structure while ..do ..done évalue une série de commande et exécute une deuxième série de commande si la première a réussi. Par exemple while [ $x -le $1] do echo $x x=`expr $x + 1` done Voici en teneur les spécificités du langage de script sh. Englober une expression par le caractère ` provoque l'évaluation de cette expression. Voici maintenant des exemples des scripts assez simple dont vous pouvez vous inspirer pour l'écriture de vos propres scripts. Script 1 : #!/bin/sh echo menu test program stop=0 while test $stop -eq 0 do cat<> $charfile done #script awk qui choisit une ligne au hasard dans un fichier #on le cree de toute piece awkscript="/tmp/awkscript.awk.$$" trap "/bin/rm -f $awkscript" 0 cat < $awkscript BEGIN { srand() } {s[NR]= \$0 } END {print s[randint(NR)] } function randint(n) {return int (n * rand() + 1)} EOF toto="exec `echo awk -f $awkscript $charfile `" cherchecar() { c1=$($toto) sleep 1 c2=$($toto) sleep 1 c3=$($toto) sleep 1 c4=$($toto) sleep 1 c5=$($toto) sleep 1 c6=$($toto) } echo fichier de mot de passe genere le `date` >$fichierpass for user in /home/* do cherchecar echo $user : $c1$c2$c3$c4$c5$c6 >>$fichierpass done Ce script crée un fichier newpass qui contient de nouveau de mot de passe que le script génère à son exécution, et ceci pour tous les utilisateurs de la machine (répertoires dans /home) Script 7 #!/bin/sh boucle=0 while test $boucle -eq 0 do echo Saisir un ou plusieurs noms de fichier read filename # for filename # do linecount="1" echo $filename while read line do echo "${linecount}: $line" linecount=`expr $linecount + 1` done < $filename # done # echo voulez continuer ? # echo 0- oui # echo 1- non # read boucle echo Voulez-vous continuer \? echo 1- oui echo 2- non read reponse case $reponse in "1") ;; "2") boucle=1 ;; *)echo "Mauvaise saisie" ;; esac done exit 0 Voilà, je vous souhaite de bien vous amuser lors de la programmation de vos prochains script shell.