Source de cours/bash.php

<?
  
require ("../page.inc");
  require (
"lessons.inc");
 
  
$currentPage = new LessonPage("bash");

  
$currentPage->setContent('');

  
$currentPage->addChapter('exec''Exécuter une commande''
<p>
<acronym lang="en" xml:lang="en" title="Bourne Again SHell">bash</acronym> est le shell le plus utilisé avec <acronym lang="en" xml:lang="en" title="GNU\'s Not Unix">GNU</acronym>/Linux. Le shell permet de donner des instructions au système en ligne de commande.
</p><p>
Lorsque l\'on tape une instruction, le premier mot est la commande elle-même. Ensuite viennent les paramètres éventuels. Le shell va alors essayer de trouver à quoi la commande correspond dans l\'ordre suivant.
</p><p>
Tout d\'abord si la commande contient une spécification d\'emplacement, le programme se trouvant dans ce répertoire est exécuté s\'il s\'y trouve et si l\'utilisateur a les droits adéquats. Sinon une erreur est affichée.
</p><p id="alias">
Ensuite bash regarde quels <strong>alias</strong> sont définis. Un alias est un moyen d\'exécuter une commande par un raccourci. Pour que par exemple le fait de taper mon_alias exécute ma_commande, il faudra déclarer l\'alias comme ceci&nbsp;:
</p>
<code class="terminal">&gt; alias mon_alias=\'ma_commande\'</code>
<p>Les guillemets ne sont utiles que si la commande a des paramètres (c\'est à dire qu\'elle contient des espaces). Le contenu de ma_commande sera évalué selon le même schéma pour trouver le programme à exécuter.
</p><p>
Si on tape après&nbsp;:
</p>
<code class="terminal">&gt; mon_alias paramètre1 paramètre2</code>
<p>
Tout se passe comme si ma_commande avait été tapé à la place de mon_alias. Il va donc aussi avoir les paramètres.
</p><p>
Si aucun alias n\'est trouvé, ce sont alors les <strong>fonctions</strong> qui sont examinées. Une fonction est un moyen de regrouper plusieurs commandes. Et de faire des traitements plus complexe qu\'avec un alias. Une fonction se déclare comme ceci&nbsp;:
</p>
<code class="terminal">&gt; function ma_fonction() { ma_commande1; ma_commande2; }</code>
<p>
Pour la définition de la fonction, on peut omettre le mot-clé fonction ou les parenthèses, mais au moins un des deux doit être présent.
</p><p>
Le ; (point-virgule) permet de séparer les commandes et aussi de les terminer. A l\'intérieur de la fonction, des <strong>variables positionnelles</strong> ($1, $2, ...) sont définies automatiquement par le shell. Elles correspondent aux paramètres passés à la fonction. Considérons l\'exemple suivant&nbsp;:
</p>
<code class="terminal">&gt; function mon_affichage { echo "SAIT : $1"; }</code>
<p>
On aura alors le résultat suivant&nbsp;:
</p>
<code class="terminal">&gt; mon_affichage Bonjour<br />
SAIT : Bonjour</code>
<p>
Dans le cas où aucune fonction correspondant n\'existe, le shell consulte ses <strong>commandes intégrées</strong> (shell builtin commands). Avec bash, on peut citer comme exemple de commandes intégrées cd (pour changer le répertoire courant) ou echo vu précédemment.
</p><p>
Enfin le shell parcourt les répertoires contenus dans la <a href="#vars">variable d\'environnement</a> <strong>$PATH</strong> pour y chercher un programme du nom tapé. Si celui-ci est trouvé, il est exécuté, sinon une erreur est affichée.
</p><p>
Un exemple de variable $PATH&nbsp;:
</p>
<code class="terminal">&gt; echo $PATH<br />
/home/tian/bin:/usr/local/bin:/usr/bin:/bin</code>
<p>
Si deux programmes avec le même nom se situent dans /usr/local/bin et /bin, c\'est le premier qui sera exécuté.
</p><p>
En résumé voici l\'ordre d\'évaluation si la commande est exécutée sans spécification de chemin&nbsp;:
</p>
<ul>
<li>alias</li>
<li>fonction</li>
<li>commandes intégrées</li>
<li>exécutables dans un des répertoires de $PATH</li>
</ul>
<p>
Si tout cela échoue, un message d\'erreur sera affiché. La commande <strong>type</strong> permet de savoir à quelle catégorie appartient une commande. Dans les deux premiers cas, on a la correspondance de l\'alias ou de la fonction. Les commandes intégrées sont indiquées par un texte générique et pour le dernier cas, le chemin de l\'exécutable est affiché. 
</p><p>
Lorsque l\'on exécute une commande, bash crée un <strong>sous-shell</strong> pour le lancer. C\'est-à-dire que cela se passe comme si on avait lancé une nouvelle fois bash. Pour modifier ce comportement, on peut placer un . (point) en début de ligne. Cela provoquera alors l\'exécution dans le shell courant. Exemple&nbsp;:
</p>
<code class="terminal">&gt; . ma_commande</code>
<p id="substi">
Pour terminer cette première partie, signalons que le shell peut aussi procéder à des développements de noms de fichiers avant l\'exécution des programmes. Il y a pour cela une syntaxe relativement simple de motifs. bash va remplacer le motif par tous les noms de fichiers correspondant à celui-ci selon les règles suivantes&nbsp;:
</p>
<ul>
<li>* peut correspondre à n\'importe quelle suite de caractères, y compris aucun caractère.</li>
<li>? peut correspondre à n\'importe quel caractère unique.</li>
<li>[ ] correspond à un des caractères spécifié entre les crochets. Une suite de caractères successifs est indiquée par un - (tiret) placé entre les bornes.</li>
</ul>
<p>Par exemple [xX1-9]mod* sera remplacé par tous les noms de fichiers commençant par un x, un X ou n\'importe quel chiffre, suivi par la chaîne mod puis ensuite n\'importe quel chaîne de caractères. Cette substitution est effectuée avant l\'exécution du programme qui recevra donc comme paramètres cette liste.
</p>
'
);

  
$currentPage->addChapter('history''Historique des commandes''
<p>
bash conserve les dernières commandes tapées afin de pouvoir facilement les retrouver. Elles sont sauvegardées en mémoire pour le shell courant si plusieurs sont lancés. Et lorsque celui-ci est quitté, elles sont écrites dans le fichier .bash_history qui se trouve à la racine du répertoire personnel de l\'utilisateur (~). Ce fichier est celui par défaut, son nom peut être changé par la <a href="#vars">variable d\'environnement</a> <strong>$HISTFILE</strong>. Le nombre de précédentes commandes sauvegardées en mémoire est défini par la variable <strong>$HISTSIZE</strong> (par défaut 500) et le nombre devant être sauvegardées dans le fichier par la variable <strong>$HISTFILESIZE</strong> (également 500 par défaut). Les commandes sont sauvegardées avant la substitution éventuelle faite (si des caractères *, ? ou [ ] sont présents).
</p><p>
Pour parcourir l\'historique, il suffit d\'utiliser les touches fléchées (à gauche du pavé numérique). La flèche vers le <strong>haut</strong> permet de parcourir les commandes dans l\'ordre inverse de leur exécution et la flèche vers le <strong>bas</strong> dans l\'autre sens. Peuvent également être utilisées respectivement les combinaisons <strong>Ctrl P</strong> (Previous) et <strong>Ctrl N</strong> (Next). Pour les habitués de l\'éditeur de texte <a rel="external" xml:lang="en" lang="en" href="http://www.gnu.org/software/emacs/emacs.html" hreflang="en">Emacs</a>, ce sont les combinaisons de touches de ce logiciel qui peuvent être utilisées pour éditer les commandes entrées.
</p><p>
De même que dans cet éditeur, on peut rechercher parmi les commandes précédentes. Pour cela il faut taper <strong>Ctrl R</strong> (Touche Ctrl en bas du clavier puis en la maintenant, la touche R). Apparaît alors une invite permettant la recherche de la forme suivante&nbsp;:
</p>
<code class="terminal">
(reverse-i-search)`\':<br />
</code>
<p>
Qui remplace alors l\'invite de commande habituelle. La recherche se fait de manière incrémentale. C\'est à dire qu\'au fur et à mesure que l\'on tape des caractères, la plus récente ligne de l\'historique trouvée sera affichée. Pour interrompre la recherche, on peut taper <strong>Ctrl G</strong> ou une des touches fléchées (gauche et droite permettant alors de l\'éditer). La touche Entrée permet d\'exécuter la commande affichée.
</p>
'
);

  
$currentPage->addChapter('vars''Variables d\'environnement''
<p>Ces variables permettent de stocker des valeurs pour pouvoir les utiliser plus tard. Elles n\'ont pas besoin d\'être déclarées. bash les créera dès la première utilisation. Pour <strong>affecter</strong> à la variable VARIABLE la valeur VALEUR, on tapera&nbsp;:
</p>
<code class="terminal">&gt; VARIABLE=VALEUR<br />
</code><p>
Le signe = permet de faire l\'affectation. Il est indispensable de ne pas mettre d\'espace avant ou après ce signe, ce qui est une erreur fréquente.
</p><p>
VALEUR ne doit pas contenir d\'espace non plus. Si on veut affecter une chaîne de caractères en comprenant, on utilisera des <a href="#quote">guillemets</a>&nbsp;:
</p>
<code class="terminal">&gt; PHRASE="une valeur avec des espaces"<br />
</code>
<p>
Pour accéder ensuite à la <strong>valeur</strong> d\'une variable, on préfixe son nom avec $ (dollar). Celui-ci est sensible à la casse. Le moyen le plus simple de tester la valeur d\'une variable est d\'utiliser la commande <strong>echo</strong> qui affiche ce qu\'on lui passe en paramètre.
</p>
<code class="terminal">&gt; echo $PHRASE<br />
une valeur avec des espaces
</code>
<p>
On peut également accéder à la valeur d\'une variable en rajoutant des accolades autour du nom, ce qui est parfois utile.
</p>
<code class="terminal">&gt; phrase="une phrase avec des espace"<br />
&gt; echo ${phrase}s<br />
une phrase avec des espaces
</code>
<p>
Sans les accolades, bash aurait essayé d\'évaluer la variable $phrases

</p><p>
La définition d\'une variable d\'environnement, comme vu précédemment, limite sa visibilité au shell courant. C\'est à dire que dans un sous-shell, on ne pourra pas accéder à sa valeur.
</p><p>
La commande <strong>export</strong> permet d\'étendre la définition d\'une variable aux autres shells créés à partir de celui courant (soit explicitement, soit en lançant une commande). Elle peut s\'utiliser sur une variable déjà créée, ou l\'initialisation peut être faite en même temps.
</p>
<code class="terminal">&gt; variable_existante=valeur1<br />
&gt; export variable_existante<br />
&gt; export nouvelle_variable=valeur2<br />
</code>
<p>
On remarquera que la variable n\'est pas préfixée par le $ lors de l\'appel à export. Si tel avait été le cas, ce serait la variable de nom valeur1 (inexistante dans cette exemple) qui aurait été exportée.
</p><p>
bash permet aussi d\'avoir des variables <strong>tableaux</strong>. Celles-ci sont des tableaux associatifs. La clé pour y accéder peut être n\'importe quelle chaîne de caractères. Par défaut ce seront des numériques commençant à 0. Les tableaux sont aussi créés automatiquement. Pour initialiser un tableau en une seule opération&nbsp;:
</p>
<code class="terminal">&gt; tableau1=(valeur0 valeur1 valeur2)</code>
<p>
On accède ensuite à un élément par ${tableau[clé]} où clé sera ici un nombre entre 0 et 2.
</p>
<code class="terminal">&gt; echo ${tableau1[2]}<br />
valeur2</code>
<p>
On peut par cette même notation affecter une valeur à l\'élément du tableau. Si on affecte la valeur à un élément qui n\'existe pas encore, il sera créé. Il ne doit pas nécessairement être à la suite du dernier élément. On peut créer celui d\'indice 5 sans que les 3 et 4 n\'existent.
</p>
<code class="terminal">&gt; ${tableau1[5]}=valeur5</code>
<p>
Pour créer un tableau en n\'utilisant pas les indices par défaut, on utilisera la syntaxe suivante&nbsp;:
</p>
<code class="terminal">&gt; tableau2=([clé0]=valeur0 [clé1]=valeur1 [clé2]=valeur2)</code>
<p>
Où les clés peuvent être n\'importe quelles chaînes de caractères. A un tableau créé avec les index numériques par défaut, on peut très bien ajouter un élément dont la clé est une chaîne de caractères.
</p>
'
);

  
$currentPage->addChapter('quote''Utilisation des guillemets''
<p>
Il y a 3 types de guillemets à utiliser.
</p><p>
En premier lieu il y a les <strong>guillemets doubles</strong> ("). Leur rôle est de regrouper plusieurs chaînes de caractères séparées par des espaces. Si un programme accepte un seul paramètre, l\'exécution suivante génèrera une erreur&nbsp;:
</p>
<code class="terminal">&gt; ma_commande une phrase en paramètre<br />
Erreur&nbsp;: ma_commande n\'accepte qu\'un paramètre</code>
<p>
Car là on lui a passé 4 paramètres. Alors que dans le cas suivant, ma_commande recevra un seul paramètre contenant la chaîne complète&nbsp;:
</p>
<code class="terminal">&gt; ma_commande "une phrase en paramètre"<br />
Résultat de ma_commande</code>
<p>
Cela est aussi utile pour affecter à une variable une chaîne de caractères comprenant des espaces.
</p><p>
Si une variable est contenue entre les doubles guillemets (avec le $ devant le nom), elle est remplacée par sa valeur.
</p><p>
Les <strong>guillemets simples</strong> (\') jouent le même rôle. La différence vient du fait qu\'aucune substitution de variable n\'est effectuée. La chaîne comprise entre \' \' conservera les caractères $
</p><p>
Les derniers sont les <strong>guillemets inversés</strong> (`). Ils permettent d\'exécuter une commande. La chaîne comprise entre ` ` vaudra le résultat retourné par la commande. Les variables sont remplacées par leurs valeurs avant l\'évaluation de la commande. Cela est souvent utilisé pour affecter un résultat à une variable. Pour l\'exemple suivant, la commande intégrée <strong>pwd</strong> permet d\'afficher le répertoire courant.
</p>
<code class="terminal">&gt; REPERTOIRE=`pwd`<br />
&gt; echo $REPERTOIRE<br />
/home/tian</code>
<p>
Il existe une notation équivalente à la précédente. La commande peut être placée entre $( ). L\'exemple devient alors&nbsp;:
</p>
<code class="terminal">&gt; REPERTOIRE=$(pwd)</code>
'
);

  
$currentPage->addChapter('arithm''Expressions arithmétiques''
<p>
On peut facilement évaluer des expressions arithmétiques avec bash. Pour cela, on utilise la notation <strong>$(( ))</strong> qui sera remplacée par le résultat du calcul mathématique. Voici les opérations pouvant être utilisées par ordre de priorité inverse (extrait de la page de manuel de bash)&nbsp;:
</p>
<ul>
<li><strong>- +</strong> plus et moins unaire</li>
<li><strong>! ~</strong> négations logique et binaire</li>
<li><strong>* / %</strong> multiplication, division, reste</li>
<li><strong>+ -</strong> addition, soustraction</li>
<li><strong>&lt;&lt; &gt;&gt;</strong> décalage arithmétique à gauche et à droite</li>
<li><strong>&lt;= &gt;= &lt; ></strong> comparaisons</li>
<li><strong>== !=</strong> égalité et différence</li>
<li><strong>&amp;</strong> ET binaire</li>
<li><strong>^</strong> OU exclusif binaire</li>
<li><strong>|</strong> OU binaire</li>
<li><strong>&amp;&amp;</strong> ET logique</li>
<li><strong>||</strong> OU logique</li>
<li><strong>= *= /= %= += -= &lt;&lt;= &gt;&gt;= &amp;= ^= |=</strong> assignations</li>
</ul>
<p>
bash effectuera le remplacement par la valeur avant l\'exécution des commandes éventuelles. La commande suivante est une erreur&nbsp;:
</p>
<code class="terminal">&gt; $((variable=3+2))<br />
bash: 5: command not found<br />
</code>
<p>
La variable contiendra bien la valeur 5 à la suite de cette action. Mais bash a remplacé l\'expression par sa valeur, donc tout se passe comme si on avait uniquement tapé 5 à l\'invite (qui se trouve être un programme inconnu).
</p><p>
Une autre notation est possible en mettant <strong>$[ ]</strong> autour de l\'expression à la place de $(( )).
</p>
'
);

  
$currentPage->addChapter('redirect''Redirections''
<p>
Par défaut un programme lit depuis le clavier et écrit ses résultats sur l\'écran. On parle d\'<strong>entrée et sortie standards</strong> qui sont tous deux des <strong>flux</strong>. Mais ce comportement peut être modifié à l\'aide des redirections qui vont modifier les entrée et sortie du programme sans qu\'il ne s\'en rende compte.
</p><p>
Pour que le résultat d\'un programme soit enregistré dans un fichier plutôt qu\'affiché, il faut utiliser <strong>&gt;</strong> suivi du nom du fichier. <strong>ls</strong> sans argument permet d\'afficher la liste des fichiers et répertoires présents dans le répertoire courant. Après cette commande&nbsp;:
</p>
<code class="terminal">&gt; ls &gt; liste_fichiers</code>
<p>
Le fichier liste_fichiers contiendra ce que l\'on aurait vu s\'afficher dans le terminal sans la redirection. Si le fichier existait, il est détruit et remplacé par le nouveau contenu. Pour ajouter des données à la fin d\'un fichier existant, on utilisera <strong>&gt;&gt;</strong> à la place&nbsp;:
</p>
<code class="terminal">&gt; ls &gt;&gt; liste_fichiers</code>
<p>
De la même manière, on peut dire à un programme de lire depuis un fichier plutôt que depuis le clavier. Cela se fait à l\'aide de <strong>&lt;</strong> suivi du nom du fichier. Le programme <strong>cat</strong> sans argument affiche ce qu\'il lit sur son entrée standard.
</p>
<code class="terminal">&gt; cat &lt; liste_fichiers</code>
<p>
Provoquera l\'affichage du fichier précédent. Cela est utile pour automatiser le traitement fait par un programme interactif. On peut placer dans un fichier la liste des commandes à lui faire exécuter, et ensuite lui passer en redirigeant son entrée standard depuis ce fichier.
</p><p>
Avec <strong>&lt;&lt;</strong> on peut indiquer en une seule fois tout ce qui doit être passé en entrée puis lancer l\'exécution ensuite. Après &lt;&lt; il doit y avoir une chaîne de caractères quelconques. Une ligne comprenant uniquement cette chaîne indiquera la fin des entrées. Il est donc important de la choisir de sorte à ce qu\'elle n\'apparaisse pas dans la liste de données.
</p>
<code class="terminal">&gt; cat &lt;&lt;DELIMITEUR<br />
&gt; ligne 1<br />
&gt; ligne 2<br />
&gt; DELIMITEUR<br />
ligne 1<br />
ligne 2
</code>
<p>
On peut enfin rediriger la sortie standard d\'un programme vers l\'entrée standard d\'un autre, cela afin de les enchaîner et/ou de filtrer les résultats. Cela se fait à l\'aide de <strong>|</strong> appelé <strong>pipe</strong> en anglais ou <strong>tube</strong> en français. <a href="gnu.php#grep">grep</a> est un outil permettant de ne garder que les lignes comportant un certain motif.
</p>
<code class="terminal">&gt; ls | grep txt</code>
<p>
Cet exemple permet de n\'afficher que les fichiers et répertoires comprenant txt dans leur nom.
</p><p>
Pour terminer ceci, citons un outil utile, <strong>tee</strong>. Il permet de dupliquer un flux. Il redirige ce qu\'il reçoit en entrée à la fois dans un fichier passé en paramètre et sur sa sortie. Ceci sert lorsqu\'une longue commande est exécutée et que l\'on souhaite sauvegarder les résultats tout en les examinant éventuellement au fur et à mesure. Exemple&nbsp;:
</p>
<code class="terminal">&gt; ls | tee liste_fichiers</code>
'
);
  
$currentPage->display();
?>