Guide avancé d'écriture des scripts Bash

05 Janvier 2003

Par MendelCooper

Une exploration en profondeur de l'art subtil de la programmation shell


Avant de commencer
1. Introduction
1.1. Pourquoi la programmation Shell?
1.2. Lancement avec un #!
Appeler le script
Exercices préliminaires
2. Bases
2.0. Caractères spéciaux
2.1. Introduction aux variables et aux paramètres
Substitution de variable
Affectation de variable
Les variables Bash ne sont pas typées
Types spéciaux de variables
2.2. Guillemets
2.3. Sortie et code de sortie (ou d'état)
2.4. Tests
Constructions de tests
Exercice
Opérateurs de test de fichiers
Opérateurs de comparaison (binaire)
Tests if/then imbriqués
Tester votre connaissance des tests
2.5. Opérations et thèmes relatifs
Opérateurs
opérateurs de bits
Constantes numériques
3. Après les bases
3.0. Les variables revisitées
Variables internes
Manipuler les chaînes de caractères
Manipuler des chaînes de caractères avec awk
Discussion plus avancée
Substitution des paramètres
Substitution de paramètres et/ou expansion
Typer des variables: declare ou typeset
Références indirectes aux variables
$RANDOM: générer un nombre aléatoire
La construction en double parenthèse
3.1. Boucles et branchements
Boucles
Boucles imbriquées
Contrôle de boucles
Tests et branchements
3.2. Commandes internes et intégrées
Commandes de contrôle des jobs
3.3. Filtres externes, programmes et commandes
Les commandes basiques
Commandes complexes
Commandes de date et d'heure
Commandes d'analyse de texte
Commandes pour les fichiers et l'archivage
Commandes de communications
Commandes de contrôle du terminal
Commandes mathématiques
Commandes diverses
3.4. Commandes système et d'administration
Exercice 1
Exercice 2
3.5. Substitution de commandes
La sortie des commandes peut être utilisée comme argument d'une autre commande, pour affecter une variable, voire pour génerer la liste des arguments dans une boucle for .
3.6. Expansion arithmétique
3.7. Redirection d'E/S (entrées/sorties)
Utiliser exec
Rediriger les blocs de code
Applications
3.8. Documents en ligne
3.9. Récréation
4. Thèmes avancés
4.1. Expressions rationnelles
Une brève introduction aux expressions rationnelles
Remplacement
4.2. Sous-shells
4.3. Shells restreints
4.4. Substitution de processus
4.5. Fonctions
Fonctions complexes et complexité des fonctions
Variables locales
Les variables locales rendent la récursion possible.
4.6. Alias
4.7. Constructeurs de listes
4.8. Tableaux
4.9. Fichiers
4.10. /dev et /proc
/dev
/proc
4.11. Des Zéros et des Nulls
4.12. Déboguage
4.13. Options
4.14. Trucs et astuces
4.15. Ecrire des scripts avec style
Feuille de style non officielle d'écriture de scripts
4.16. Divers
Shells et scripts interactifs et non interactifs
Scripts d'invocation
Tests et comparaisons: Alternatives
Récursion
Coloriser des scripts
Optimisations
Astuces assorties
Problèmes de sécurité
Problèmes de portabilité
Scripts sous Windows
4.17. Bash, version 2
5. Notes finales
Note de l'auteur
A propos de l'auteur
Outils utilisés pour produire ce livre
Matériel
Logiciel et Impression
Crédits
6. Bibliographie
7. Appendice : Scripts contribués
8. Appendice : Petit guide sur Sed et Awk
Sed
Awk
9. Appendice : Codes de sortie ayant une signification particulière
10. Appendice : Une introduction détaillée sur les redirections d'entrées/sorties
11. Appendice : Localisation
12. Appendice : Commandes d'historique
13. Appendice : Un exemple de fichier .bashrc
14. Appendice : Convertir des fichiers Batch DOS en Scripts Shell
15. Appendice : Exercices
Analyse de scripts
Ecriture de scripts
16. Appendice : Droits d'utilisation
17. Appendice : Copyright


Avant de commencer

Pour Anita, la source de toute magie

Ce tutoriel ne suppose aucune connaissance de la programmation de scripts, mais permet une progression rapide vers un niveau intermédiaire/avancé d'instructions tout en se plongeant dans de petites astuces du royaume d'UNIX. Il est utile comme livre, comme manuel permettant d'étudier seul, et comme référence et source de connaissance sur les techniques de programmation de scripts. Les exercices et les exemples grandement commentés invitent à une participation active du lecteur avec en tête l'idée que

la seule façon pour vraiment apprendre la programmation de scripts est d'écrire des scripts

.La dernière mise à jour de ce document, comme une "archive tar" compressée avec bzip2 incluant à la fois le source SGML et le HTML généré, peut être téléchargée à partir du site personnel de l'auteur. Voir le journal des modifications pour un historique des révisions.


1. Introduction

Le shell est un interpréteur de commandes. Plus qu'une simple couche isolante entre le noyau du système d'exploitation et l'utilisateur, il est aussi un langage de programmation assez puissant. Un programme shell, appelé un script, est un outil facile à utiliser pour construire des applications en "agglutinant" ensemble des appels système, outils, utilitaires et binaires compilés. Virtuellement, le répertoire entier des commandes UNIX, des utilitaires et des outils est disponible pour être appelé à partir d'un script shell. Si ce n'était pas suffisant, les commandes shell internes, telles que les constructions de tests et de boucles, donnent une puissance et une flexibilité supplémentaires aux scripts. Les scripts shell conviennent exceptionnellement bien pour les tâches d'administration du système et pour d'autres routines répétitives ne réclamant pas les particularités d'un complet langage de programme structuré.


1.1. Pourquoi la programmation Shell?

Une connaissance fonctionnelle de la programmation shell est essentielle à quiconque souhaite devenir bon en administration système, même pour ceux qui ne pensent pas avoir à écrire réellement un script un jour. Pensez qu'au démarrage de la machine Linux, des scripts shell du répertoire /etc/rc.d sont exécutés pour restaurer la configuration du système et permettre la mise en fonctionnement des services. Une compréhension détaillée de ces scripts de démarrage est importante pour analyser le comportement d'un système, et éventuellement le modifier.

Savoir écrire des scripts shell n'est pas difficile à apprendre, car d'une part les scripts peuvent être construits par petites sections et d'autre part il n'existe qu'un petit ensemble d'opérateurs et d'options (1) spécifiques aux shell à connaître. La syntaxe est simple et directe, similaire à une suite d'appels de différents utilitaires à la ligne de commande, et il n'existe que peu de "règles" à apprendre. La plupart des petits scripts fonctionne du premier coup, et le déboguage, même des plus longs, est assez simple.

Un script shell est une méthode "rapide et sale" de prototyper une application complexe. Avoir même un sous-ensemble limité de fonctionnalités dans un script shell, même lent, est souvent une utile première étape lors d'un projet de développement. De cette façon, la structure de l'application peut être testée, et les problèmes majeurs trouvés avant d'effectuer le codage final en C, C++, Java, ou Perl.

La programmation shell ramène à la philosophie classique des UNIX, c'est à dire, de casser des projets complexes en de plus simple sous-tâches et d'assembler des composants et des utilitaires. Beaucoup considèrent ceci comme une meilleure, ou tout du moins plus esthétique, approche de la résolution de problèmes, plutôt que d'utiliser une des nouvelles générations de languages tout puissant, comme Perl, qui essaient de tout faire pour tout le monde, mais au prix de vous forcer à changer votre processus de réflexion pour vous adapter à l'outil.

Quand ne pas utiliser les scripts shell pour des tâches demandant beaucoup de ressources, et spécialement lorsque la rapidité est un facteur (tri, hachage, etc.); pour des procédures impliquant de nombreuses et complexes opérations mathématiques, et spécialement pour de l'arithmétique à virgule flottante, des calculs à précision arbitraire, ou des nombres complexes (optez plutôt pour le C++ ou le FORTRAN dans ce cas); pour une portabilité inter-platforme (utilisez le C à la place); pour des applications complexes, où une programmation structurée est nécessaire (typage de variables, prototypage de fonctions, etc.); pour des applications critiques sur lesquelles vous miser votre avenir ou celui de la société; pour des situations où la sécurité est importante, où vous avez besoin de garantir l'intégrité de votre système et de vous protéger contre les intrusions, et le vandalisme; pour des projets consistant en de nombreux composants avec des dépendances inter-verrouillées; lorsque des opérations importantes sur des fichiers sont requises (Bash est limité à un accès série, ligne par ligne, ce qui est particulièrement maladroit et inefficace);, si des tableaux multi-dimensionel sont nécessaires; si vous avez besoin de structures de données, telles que des listes chaînées ou des arbres; si vous avez besoin de générer ou de manipuler des graphiques ou une interface utilisateur (GUI); lorsqu'un accès direct au matériel est nécessaire; lorsque vous avez besoin d'accéder à un port à un socket I/O; si vous avez besoin d'utiliser des bibliothèques ou une interface propriétaire; pour des applications propriétaires, à sources fermées (les shells sont forcément Open Source).

Si une des raisons ci-dessus s'applique, considérez l'utilisation d'un langage de scripts plus puissant, peut-être Perl, Tcl, Python, Ruby, voire un langage compilé de haut niveau tel que C, C++ ou Java. Même dans ce cas, prototyper l'application avec un script shell peut toujours être une étape utile du développement.

Nous utiliserons Bash, un acronyme pour "Bourne-Again Shell" et un calembour sur le désormais classique Bourne Shell de Stephen Bourne. Bash est devenu un standard de facto pour la programmation de scripts sur tous les types d'UNIX. La plupart des principes discutés dans ce livre s'applique également à l'écriture de scripts avec d'autres shells tels que le Korn Shell, à partir duquel dérive certaines des fonctionnalités de Bash, (2) , le shell C et ses variantes. (Notez que la programmation du shell C n'est pas recommendée dû à certains problèmes inhérents, comme indiqué en octobre 1993 sur un message Usenet par Tom Christiansen).

Ce qui suit est un tutoriel sur l'écriture de scripts shell. Il dépend en grande partie d'exemples illustrant les fonctionnalités variées du shell. Autant que possible, les scripts en exemple ont été testés, et certains d'entre eux peuvent même être utiles dans la vraie vie. Le lecteur devrait utiliser les exemples de l'archive des sources (quelque-chose.sh), (3) leur donner le droit d'exécution (

chmod u+rx nom_du_script

), et les lancer pour voir ce qui ce passe. Si les sources de l'archive ne sont pas disponibles, alors copier/coller à partir de la version HTML, PDF ou texte. Faites attention que certains scripts introduisent des fonctionnalités avant qu'elles ne soient expliquées, et que ceci pourrait réclamer du lecteur de s'en passer temporairement.

Sauf mention contraire, l'auteur du livre a écrit les scripts d'exemples qui suivent.


1.2. Lancement avec un #!

Dans le cas le plus simple, un script n'est rien de plus qu'une liste de commandes système enregistrée dans un fichier. A tout le moins, cela permet d'éviter l'effort d'une frappe complète de cette séquence particulière de commandes à chaque fois qu'elle doit être appelée.

cleanup: Un script pour nettoyer les fichiers de trace dans /var/log
# cleanup # A lancé comme root, bien sûr. cd /var/log cat /dev/null > messages cat /dev/null > wtmp echo "Logs nettoyés."

Il n'y a rien d'inhabituel ici, juste un ensemble de commandes qui pourraient tout aussi bien être appelées les unes après les autres à partir de la ligne de commande sur la console ou dans une xterm. Les avantages de les placer dans un script vont bien au delà de ne pas avoir encore à les retaper. Le script peut facilement être modifié, personnalisé ou généralisé pour une application particulière.

cleanup: Une version améliorée et généralisée du script précédent.
#!/bin/bash # cleanup, version 2 # A exécuter en tant que root, bien sûr. REP_LOG=/var/log ROOT_UID=0 # Seuls les utilisateurs avec $UID 0 ont les privilèges de root. LIGNES=50 # Nombre de lignes sauvées par défaut. E_XCD=66 # On ne peut pas changer de répertoire? E_NONROOT=67 # Code de sortie si non root. if [ "$UID" -ne "$ROOT_UID" ] then echo "Vous devez être root pour exécuter ce script." exit $E_NONROOT fi if [ -n "$1" ] # Teste si un argument est présent en ligne de commande (non vide). then lignes=$1 else lignes=$LIGNES # Par défaut, s'il n'est pas spécifié sur la ligne de commande. fi # Stephane Chazelas suggère ce qui suit #+ comme une meilleure façon de vérifier les arguments en ligne de commande, #+ mais c'est un peu trop avancé à ce stade du tutoriel. # # E_MAUVAISARGS=65 # Argument non numérique (mauvais format de l'argument) # # case "$1" in # "" ) lignes=50;; # *[!0-9]*) echo "Usage: `basename $0` fichier-a-nettoyer"; exit $E_MAUVAISARGS;; # * ) lignes=$1;; # esac # #* Passer au chapitre "Boucle" pour comprendre tout ceci. cd $REP_LOG if [ `pwd` != "$REP_LOG" ] # ou if [ "$PWD" != "$REP_LOG" ] # Pas dans /var/log? then echo "Impossible d'aller dans $REP_LOG." exit $E_XCD fi # Double vérification du bon répertoire, pour ne pas poser problème avec le # fichier de traces. # bien plus efficace: # # cd /var/log || { # echo "Impossible d'aller dans le répertoire nécessaire." >&2 # exit $E_XCD; # } tail -$lignes messages > mesg.temp # Sauvegarde la dernière section du fichier # de traces. mv mesg.temp messages # Devient le nouveau répertoire de traces. # cat /dev/null > messages #* Plus nécessaire, car la méthode ci-dessus est plus saine. cat /dev/null > wtmp # ': > wtmp' et '> wtmp' ont le même effet. echo "Traces nettoyées." exit 0 # Un code de retour zéro du script indique un succès au shell.

Comme vous pouvez ne pas vouloir supprimer toutes les traces système, cette variante du premier script conserve la dernière section des traces intacte. Vous découvrirez en permanence de nouvelles façons pour affiner des scripts précédemment écrits et améliorer ainsi leur efficacité.

Le sha-bang ( #!) en entête de ce fichier indique à votre système que ce fichier est un ensemble de commandes pour l'interpréteur indiqué. Les caractères #! sont en fait un "nombre magique" composé de deux octets (4) , un marqueur spécial qui désigne un type de fichier, ou dans ce cas, un script shell exécutable (voir man magic pour plus de détails sur ce thème fascinant). Tout de suite après le sha-bang se trouve un chemin. C'est le chemin vers le programme qui interprète les commandes de ce script, qu'il soit un shell, un langage de programmation ou un utilitaire. Ensuite, cet interpréteur de commande exécute les commandes dans le script, en commençant au début (ligne 1 du script), en ignorant les commentaires. (5)

Chacune des lignes d'entête du script ci-dessus appelle un interpréteur de commande différent, qu'il soit /bin/sh, le shell par défaut (bash dans un système Linux) ou autre chose. (6) Utiliser

#!/bin/sh

, par défaut Bourne Shell dans la plupart des variantes commerciales d'UNIX, rend le script portable aux machines non-Linux, malheureusement en faisant le sacrifice des fonctionnalités spécifiques à Bash. Le script se conformera néanmoins au standard sh de POSIX (7) .

Notez que le chemin donné à "sha-bang" doit être correct, sinon un message d'erreur -- habituellement "Command not found" -- sera le seul résultat du lancement du script.

#! peut être omis si le script consiste seulement en un ensemble de commandes système génériques, sans utiliser de directives shell interne. Le second exemple, ci-dessus, requiert l'initial #!, car la ligne d'affectation des variables,

lines=50

, utilise une construction spécifique au shell. Notez encore que

#!/bin/sh

appelle l'interprétateur shell par défaut, qui est /bin/bash sur une machine Linux.

IMPORTANT:Ce tutoriel encourage une approche modulaire de la construction d'un script. Prenez note et collectionnez des astuces sous forme de "blocs simples" de code pouvant être utiles pour de futurs scripts. Eventuellement, vous pouvez construire une bibliothèque assez étendue de routines bien conçues. Comme exemple, le début du script suivant teste si le script a été appelé avec le bon nombre de paramètres.

IMPORTANT:


Appeler le script

Après avoir écrit le script, vous pouvez l'appeler avec

sh nom_script

(8), ou avec

bash nom_script

. (Il n'est pas recommandé d'utiliser

sh nom_script

, car cela désactive la lecture de stdin à l'intérieur du script.) Il est bien plus aisé de rendre le script directement exécutable avec un chmod. Soit
chmod 555 nom_script (donne les droits de lecture/écriture à tout le monde) (9)

soit
chmod +rx nom_script (donne les droits de lecture et d'exécution à tout le monde)chmod u+rx nom_script (donne les droits de lecture et d'exécution seulement à son propriétaire)

Maintenant que vous avez rendu le script exécutable, vous pouvez le tester avec

./nom_script

(10). S'il commence par une ligne "sha-bang" (NdT : un dièse "#" suivi d'un point d'exclamation), appeler le script appelle le bon interpréteur de commande.

Enfin, après les tests et le débogage final, vous voudrez certainement le déplacer dans /usr/local/bin (en tant que root, bien sûr), pour le rendre accessible pour vous et pour tous les autres utilisateurs du système. Le script pourra alors être appelé en tapant simplement nom_script[ENTER] à la ligne de commande.


Exercices préliminaires

  1. Les administrateurs système écrivent souvent des scripts pour automatiser certaines tâches. Donnez quelques exemples où de tels scripts sont utiles.
  2. Écrivez un script qui, lors de son exécution, donne la date et l'heure, la liste tous les utilisateurs connectés, et le temps d'utilisation (uptime) du système. Enfin, le script doit sauvegarder cette information dans un fichier journal.

2. Bases


2.0. Caractères spéciaux

  • #
  • Commentaire.Les lignes commençant avec un # (à l'exception de #!) sont des commentaires.
  • Les commentaires peuvent apparaître en fin de ligne.Les commentaires peuvent aussi suivre un blanc au début d'une ligne. Attention:Un commentaire ne peut pas être suivi d'une commande sur la même ligne. Il n'existe pas de façon de terminer le commentaire, pour que le "vrai code" commence dans la même ligne. Utilisez une nouvelle ligne pour la commande suivante. Remarque:Bien sûr, un # échappé dans une instruction echo ne commence pas un commentaire. De la même manière, un # apparaît dans certaines constructions de substitution de paramètres et dans les expressions numériques constantes. Les caractères standard de guillemet et d'échappement ( ' \) échappent le #. Certaines opérations de filtrage de motif font aussi appel au #.

  • ;
  • Séparateur de commande.[point-virgule] Permet de placer deux commandes ou plus sur la même ligne.
  • Notez que le "; " a parfois besoin d'être échappé.

  • ;;
  • Terminateur dans une sélection par cas case.[double point-virgule]


  • .
  • Commande point.[point] Équivalent à source (voir Inclure un fichier de données). C'est une commande intégrée de Bash.


  • .
  • Point, comme composant d'un nom de fichier.Dans les noms de fichiers, le point sert de préfixe aux fichiers "cachés" , qui sont les fichiers que ls ne montre normalement pas.
  • bash$ touch .fichier_caché bash$ ls -l total 10 -rw-r--r-- 1 bozo 4034 Jul 18 22:04 donnée1.carnet_d_adresses -rw-r--r-- 1 bozo 4602 May 25 13:58 donnée1.carnet_d_adresses.bak -rw-r--r-- 1 bozo 877 Dec 17 2000 boulot.carnet_d_adresse bash$ ls -al total 14 drwxrwxr-x 2 bozo bozo 1024 Aug 29 20:54 ./ drwx------ 52 bozo bozo 3072 Aug 29 20:51 ../ -rw-r--r-- 1 bozo 4034 Jul 18 22:04 donnée1.carnet_d_adresses -rw-r--r-- 1 bozo 4602 May 25 13:58 donnée1.carnet_d_adresses.bak -rw-r--r-- 1 bozo 877 Dec 17 2000 boulot.carnet_d_adresse -rw-rw-r-- 1 bozo bozo 0 Aug 29 20:54 .fichier_caché
  • En ce qui concerne les noms des répertoires, un seul point représente le répertoire courant, et deux points de suite indiquent le répertoire parent.
  • bash$ pwd /home/bozo/projets bash$ cd . bash$ pwd /home/bozo/projets bash$ cd .. bash$ pwd /home/bozo/
  • Le point apparaît souvent comme répertoire de destination d'une commande de déplacement de fichiers.
  • bash$ cp /home/bozo/travail_en_cours/débarras/* .


  • .
  • Filtrage d'un caractère par le point.Pour le filtrage de caractères au sein d'une expression régulière, un "point" correspond à un seul caractère.


  • "
  • Citation partielle.[guillemet double] "CHAÎNE" empêche l'interprétation de la plupart des caractères spéciaux présents dans la CHAÎNE. Voir aussi .


  • '
  • Citation totale.[guillemet simple] 'CHAÎNE' empêche l'interprétation de tous les caractères spéciaux présents dans la CHAÎNE. Ces guillemets sont plus puissants que ". Voir aussi .


  • ,
  • Opérateur virgule.L'opérateur virgule relie une suite d'opérations arithmétiques. Toutes sont évaluées, mais seul le résultat de la dernière est renvoyé.


  • \
  • Échappement.[anti-slash]
  • \X
  • "échappe" le caractère X. Cela a pour effet de "mettre X entre guillemets" , et est équivalent à 'X'. Le \ peut être utilisé pour mettre " et ' entre guillemets, pour pouvoir les écrire sous forme littérale.
  • Voir pour une explication plus détaillée des caractères échappés.

  • /
  • Séparateur du chemin d'un fichier.[barre oblique] Sépare les composants d'un nom de fichier (comme dans /home/bozo/projets/Makefile).
  • C'est aussi l'opérateur arithmétique de division.

  • `
  • Substitution de commandes.[guillemet inversé] `commande` rend la sortie de commande disponible pour initialiser une variable. Connu sous le nom de guillemets inversés.


  • :
  • Commande nulle.[deux-points] Il s'agit de l'équivalent shell d'un "NOP" (no op, c'est-à-dire "pas d'opération" ). Elle peut être considérée comme un synomyme pour la commande intégrée true. La commande ": " est elle-même une commande intégrée Bash, et son état de sortie est "vrai" (0).
  • Boucle sans fin :Sert de bouche-trou dans un test if/then:Sert de bouche-trou quand on attend une opération binaire, voir Utiliser des opérations arithmétiques et les paramètres par défaut.Sert de bouche-trou quand on attend une commande dans un document en ligne. Voir Document en ligne Anonyme.Évalue une suite de variables en utilisant la substitution de paramètres (comme dans Utiliser la substitution et les messages d'erreur). Expansion de variable / remplacement d'une sous-chaîne .En combinaison avec l'opérateur de redirection , tronque un fichier à la taille zéro, sans changer ses permissions. Crée le fichier s'il n'existait pas auparavant. Voir aussi Utiliser tail pour surveiller le fichier des traces système.En combinaison avec l'opérateur de redirection , met à jour la date et l'heure de modification et d'accès (
  • : nouveau_fichier
  • ). Crée le fichier s'il n'existait pas. C'est équivalent à touch. Remarque:Cela s'applique aux fichiers réguliers, mais pas aux tubes, aux liens symboliques et à certains fichiers spéciaux. Peut servir à commencer une ligne de commentaire bien que ce ne soit pas recommandé. Utiliser # pour un commentaire désactive la vérification d'erreur pour le reste de la ligne, donc vous pouvez y mettre pratiquement n'importe quoi. En revanche ce n'est pas le cas avec :. Le ": " sert aussi de séparateur de champ, dans /etc/passwd et dans la variable $PATH.
  • bash$ echo $PATH /usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games


  • !
  • Inverse le sens d'un test ou d'un état de sortie.L'opérateur ! inverse l'état de sortie de la commande à laquelle il est appliqué (voir Inverser une condition en utilisant !). Il inverse aussi la conclusion d'un opérateur de test. Cela peut, par exemple, changer le sens d'un "égal" ( = ) en un "différent" ( != ). L'opérateur ! est un mot-clé Bash.
  • Dans un autre contexte, le ! apparaît aussi dans les références indirectes de variable.Dans un contexte encore différent, à partir de la ligne de commande, le ! appelle le mécanisme d'historique de Bash (voir ). Notez que dans un script, ce mécanisme est désactivé.

  • *
  • Joker.[astérisque] Le caractère * sert de "joker" pour l'expansion des noms de fichiers dans le remplacement. Utilisé seul, il correspond à tous les noms de fichiers d'un répertoire donné.
  • bash$ echo * abs-book.sgml add-drive.sh agram.sh alias.sh
  • L'astérisque * représente aussi tout un ensemble (parfois vide) de caractères dans une expression régulière.

  • *
  • Opérateur arithmétique.Dans le contexte des opérations arithmétiques, * est une multiplication.
  • La double astérisque ** est l'opérateur exponentiel.

  • ?
  • Opérateur de test.A l'intérieur de certaines expressions, le ? indique un test pour une condition.
  • Dans une construction entre parenthèses doubles, ? sert d'opérateur à trois arguments dans le style du C. Voir Manipulation, ç la façon du C, de variables.Dans une expression de substitution de paramètres, le ? teste si une variable a été initialisée.

  • ?
  • Joker.Le caractère ? sert de joker pour un seul caractère pour l'expansion d'un nom de fichier dans un remplacement, et également représente un caractère dans une expression régulière étendue.


  • $
  • Substitution de variable.
  • Un $ préfixant un nom de variable donne la valeur que contient cette variable.

  • $
  • Fin de ligne.Dans une expression régulière, un $ signifie la fin d'une ligne de texte.


  • ${}
  • Substitution de paramètres.


  • $*
  • Paramètres de position.


  • $?
  • Variable contenant l'état de sortie.La variable $? contient l'état de sortie d'une commande, d'une fonction ou d'un script.


  • $$
  • Variable contenant l'identifiant du processus.La variable $$ contient le numéro de processus du script dans lequel elle apparaît.


  • ()
  • Groupe de commandes.
  • IMPORTANT:Une liste de commandes entre parenthèses lance un sous-shell. IMPORTANT:Les variables comprises dans ces parenthèses, à l'intérieur du sous-shell, ne sont pas visibles par le reste du script. Le processus parent, le script, ne peut pas lire les variables créées dans le processus fils, le sous-shell.
  • Initialisation de tableaux.


  • {xxx, yyy, zzz, ...}
  • Expansion d'accolades.
  • Une commande peut agir sur un liste de fichiers séparés par des virgules à l'intérieur d'accolades (11). L'expansion de noms de fichiers (remplacement) s'applique aux fichiers contenus dans les accolades. Attention:Aucune espace n'est autorisée à l'intérieur des accolades sauf si les espaces sont comprises dans des guillemets ou échappées. Attention:
  • echo {fichier1,fichier2}\ :{\ A," B",' C'}
  • Attention:fichier1 : A fichier1 : B fichier1 : C fichier2 : A fichier2 : B fichier2 : C

  • {}
  • Bloc de code.[accolade] Aussi connu sous le nom de "groupe en ligne" , cette construction crée une fonction anonyme. Néanmoins, contrairement à une fonction, les variables d'un bloc de code restent visibles par le reste du script.
  • bash$ { local a; a=123; } bash: local: can only be used in a function
  • Le bloc de code entouré par des accolades peut utiliser la redirection d'E/S (entrées/sorties).
  • Blocs de code et redirection d'E/S
    #!/bin/bash # Lit les lignes dans /etc/fstab. Fichier=/etc/fstab { read ligne1 read ligne2 } < $Fichier echo "La première ligne dans $Fichier est :" echo "$ligne1" echo echo "La deuxième ligne dans $Fichier est :" echo "$ligne2" exit 0
  • Sauver le résultat d'un bloc de code dans un fichier
    #!/bin/bash # rpm-check.sh # Recherche une description à partir d'un fichier rpm, et s'il peut être #+ installé. #+ Sauvegarde la sortie dans un fichier. # #+ Ce script illustre l'utilisation d'un bloc de code. SUCCES=0 E_SANSARGS=65 if [ -z "$1" ] then echo "Usage: `basename $0` fichier-rpm" exit $E_SANSARGS fi { echo echo "Description de l'archive :" rpm -qpi $1 # Requête pour la description. echo echo "Liste de l'archive :" rpm -qpl $1 # Requête pour la liste. echo rpm -i --test $1 # Requête pour savoir si le fichier rpm est installable. if [ "$?" -eq $SUCCES ] then echo "$1 est installable." else echo "$1 n'est pas installable." fi echo } > "$1.test" # Redirige la sortie de tout le bloc vers un fichier. echo "Les résultats du test rpm sont dans le fichier $1.test" # Voir la page de manuel de rpm pour des explications sur les options. exit 0
  • Remarque:Contrairement à un groupe de commandes entre parenthèses, comme ci-dessus, un bloc de code entouré par des accolades ne sera pas lancé dans un sous-shell. (12)

  • {} \;
  • Chemin.Principalement utilisé dans les constructions find. Ce n'est pas une commande intégrée du shell.
  • Remarque:Le "; " termine l'option -exec d'une séquence de commandes find. Il a besoin d'être échappé pour que le shell ne l'interprète pas.

  • [ ]
  • Test.
  • Teste l'expression entre [ ]. Notez que [ fait partie de la commande intégrée test (et en est un synonyme), ce n'est pas un raccourci vers la commande externe /usr/bin/test.

  • [[ ]]
  • Test.
  • Teste l'expression entre [[ ]] (mot-clé du shell).Voir les explications sur la structure [[ ... ]].

  • [ ]
  • Élément d'un tableau.
  • Accolés au nom d'un tableau, les crochets indiquent l'indice d'un élément.

  • [ ]
  • suite de caractères
  • Dans une expression régulière, les crochets désignent une suite continue de caractères devant servir de motif.

  • (( ))
  • Expansion d'entiers.
  • Développe et évalue une expression entière entre (( )).Voir les explications sur la structure (( ... )).

  • >
  • Redirection.
  • nom_script >nom_fichier
  • redirige la sortie de nom_script vers le fichier nom_fichier et écrase nom_fichier s'il existe déjà.
  • commande &>nom_fichier
  • redirige à la fois stdout et stderr de commande vers nom_fichier.
  • commande >&2
  • redirige stdout de commande vers stderr.
  • nom_script >>nom_fichier
  • ajoute la sortie de nom_script à la fin du fichier nom_fichier. Si le fichier n'existe pas déjà, il sera créé.
  • Substitution de processus.
  • (commande)>
  • (commande)
  • Dans un autre contexte, les caractères et agissent comme des opérateurs de comparaison de chaînes de caractères.Dans un contexte encore différent, les caractères et agissent comme des opérateurs de comparaison d'entiers. Voir aussi Utiliser expr.


  • Redirection utilisée dans un document en ligne.



  • Comparaison ASCII.


  • \
  • Délimitation d'un mot dans une expression régulière.
  • bash$
  • grep '\mot\' fichier_texte


  • |
  • Tube.Passe la sortie de la commande précédente à l'entrée de la suivante. Cette méthode permet de chaîner les commandes ensemble.
  • Un tube, méthode classique de communication inter-processus, envoie le canal stdout d'un processus au canal stdin d'un autre processus. Dans un cas typique, une commande, comme cat ou echo, envoie un flux de données à un filtre qui opérera des transformations sur ces données.
  • cat $nom_fichier | grep $mot_recherché
  • La sortie d'une commande ou de plusieurs commandes doit être envoyée à un script via un tube. Maintenant, envoyons par le tube la sortie de ls -l à ce script. bash$ ls -l | ./uppercase.sh -RW-RW-R-- 1 BOZO BOZO 109 APR 7 19:49 1.TXT -RW-RW-R-- 1 BOZO BOZO 109 APR 14 16:48 2.TXT -RW-R--R-- 1 BOZO BOZO 725 APR 20 20:56 FICHIER-DONNEES Remarque:Le canal stdout de chaque processus dans un tube doit être lu comme canal stdin par le suivant. Si ce n'est pas le cas, le flux de données va se bloquer et le tube ne se comportera pas comme il devrait. Remarque:Un tube tourne en tant que processus fils, et ne peut donc modifier les variables du script. Remarque:Si une des commandes du tube échoue, l'exécution du tube se termine prématurément. Dans ces conditions, on a un tube cassé et on envoie un signal SIGPIPE.

  • >|
  • Force une redirection (même si l' option noclobber est mise en place)Ceci va forcer l'écrasement d'un fichier déjà existant.


  • ||
  • Opérateur OU logique.Dans une structure de test , l'opérateur || a comme valeur de retour 0 (succès) si l'un des deux est vrai.



  • Faire tourner la tâche en arrière-plan.Une commande suivie par un fonctionnera en tâche de fond.
  • bash$ sleep 10 & [1] 850 [1]+ Done sleep 10
  • A l'intérieur d'un script, les commandes et même les boucles peuvent tourner en tâche de fond.
  • Lancer une boucle en tâche de fond
    #!/bin/bash # background-loop.sh for i in 1 2 3 4 5 6 7 8 9 10 # Première boucle. do echo -n "$i " done & # Lance cette boucle en tâche de fond. # S'exécutera quelques fois après la deuxième boucle. echo # Ce 'echo' ne s'affichera pas toujours. for i in 11 12 13 14 15 16 17 18 19 20 # Deuxième boucle. do echo -n "$i " done echo # Ce 'echo' ne s'affichera pas toujours. # ====================================================== # La sortie attendue de ce script: # 1 2 3 4 5 6 7 8 9 10 # 11 12 13 14 15 16 17 18 19 20 # Mais, quelque fois, vous obtenez: # 11 12 13 14 15 16 17 18 19 20 # 1 2 3 4 5 6 7 8 9 10 bozo $ # (Le deuxième 'echo' ne s'exécute pas. Pourquoi?) # Occasionnellement aussi: # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # (Le premier 'echo' ne s'exécute pas. Pourquoi?) # Et très rarement: # 11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20 # La boucle en avant plan s'exécute avant celle en tâche de fond. exit 0
  • Attention:Une commande tournant en tâche de fond à l'intérieur d'un script peut faire s'arrêter le script, attendant un appui sur une touche. Heureusement, il est possible d'y remède.


  • Opérateur ET logique.Dans une structure de test, l'opérateur renvoie 0 (succès) si et seulement si les deux conditions sont vraies.


  • -
  • Option, préfixe.Introduit les options pour les commandes ou les filtres. Sert aussi de préfixe pour un opérateur.
  • COMMANDE -[Option1][Option2][...]
  • ls -al
  • sort -dfu $nom_fichier
  • set -- $variable


  • -
  • Redirection à partir de ou vers stdin ou stdout[tiret]
  • Notez que dans ce contexte le signe "-" n'est pas en lui-même un opérateur Bash, mais plutôt une option reconnue par certains utilitaires UNIX qui écrivent dans stdout ou lisent dans stdin, tels que tar, cat, etc.
  • bash$ echo "quoiquecesoit" | cat - quoiquecesoit
  • Lorsqu'un nom de fichier serait attendu, un - redirige la sortie vers stdout (vous pouvez le rencontrer avec
  • tar cf
  • ), ou accepte une entrée de stdin, plutôt que d'un fichier. C'est une méthode pour utiliser un outil principalement destiné à manipuler des fichiers comme filtre dans un tube.
  • bash$ file Usage: file [-bciknvzL] [-f namefile] [-m magicfiles] file...
  • Tout seul sur la ligne de commande, file échoue avec un message d'erreur. Ajoutez un "-" pour pouvoir vous en servir. Le shell attend alors une entrée de l'utilisateur.
  • bash$ file - abc standard input: ASCII text bash$ file - #!/bin/bash standard input: Bourne-Again shell script text executable
  • Maintenant la commande accepte une entrée de stdin et l'analyse. Le "-" peut être utilisé pour envoyer stdout à d'autres commandes via un tube, ce qui permet quelques astuces comme l'ajout de lignes au début d'un fichier.Par exemple vous pouvez utiliser diff pour comparer un fichier avec une partie d'un autre fichier :
  • grep Linux fichier1 | diff fichier2 -
  • Finalement, un exemple réel utilisant - avec tar.
  • Sauvegarde de tous les fichiers modifiés dans les dernières 24 heures
    #!/bin/bash # Sauvegarde dans une archive tar compressée tous les fichiers #+ du répertoire courant modifiés dans les dernières 24 heures. FICHIERSAUVE=backup archive=${1:-$FICHIERSAUVE} # Si aucun nom de fichier n'est spécifié sur la ligne de commande, #+ nous utiliserons par défaut "backup.tar.gz." tar cvf - `find . -mtime -1 -type f -print` > $archive.tar gzip $archive.tar echo "Répertoire $PWD sauvegardé dans un fichier archive \"$archive.tar.gz\"." # Stephane Chazelas indique que le code ci-dessus échouera si il existe trop #+ de fichiers ou si un nom de fichier contient des espaces blancs. # Il suggère les alternatives suivantes: # ------------------------------------------------------------------- # find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar" # avec la version GNU de "find". # find . -mtime -1 -type f -exec tar rvf "$archive.tar" '{}' \; # portable aux autres UNIX, mais plus lent. # ------------------------------------------------------------------- exit 0
  • Attention:Les noms de fichiers commençant avec un "-" peuvent poser problème lorsqu'ils sont couplés avec le "-" opérateur de redirection. Votre script doit détecter de tels fichiers et leur ajouter un préfixe approprié, par exemple ./-NOMFICHIER, $PWD/-NOMFICHIER, ou $NOMCHEMIN/-NOMFICHIER. Attention:Il y aura probablement un problème si la valeurx d'une variable commence avec un -.

  • -
  • Répertoire courant précédent.[tiret] cd - revient au répertoire précédent, en utilisant la variable d'environnement $OLDPWD.
  • Attention:Ne confondez pas "-" utilisé dans ce sens avec l'opérateur de redirection "-" vu précédemment. L'interprétation du "-" dépend du contexte dans lequel il apparaît.

  • -
  • Moins.Le signe moins est une opération arithmétique.


  • =
  • Égal.Opérateur d'affectation.
  • Dans un autre contexte, le signe = est un opérateur de comparaison de chaînes de caractères.

  • +
  • Plus.Opérateur arithmétique d'addition.
  • Dans un autre contexte, le + est un opérateur d'expression régulière.

  • +
  • Option.Option pour une commande ou un filtre.
  • Certaines commandes, intégrées ou non, utilisent le + pour activer certaines options et le - pour les désactiver.

  • %
  • Modulo.Opérateur arithmétique modulo (reste d'une division entière).
  • Dans un autre contexte, le % est un opérateur de reconnaissance de modèles.

  • ~
  • Répertoire de l'utilisateur.[tilde] Le ~ correspond à la variable interne $HOME. ~bozo est le répertoire de l'utilisateur bozo, et ls ~bozo liste son contenu. ~/ est le répertoire de l'utilisateur courant et ls ~/ liste son contenu.
  • bash$ echo ~bozo /home/bozo bash$ echo ~ /home/bozo bash$ echo ~/ /home/bozo/ bash$ echo ~: /home/bozo: bash$ echo ~utilisateur-inexistant ~utilisateur-inexistant


  • ~+
  • Répertoire courant.Correspond à la variable interne $PWD.


  • ~-
  • Répertoire courant précédent.Correspond à la variable interne $OLDPWD.


  • ^
  • Début de ligne.Dans une expression régulière, un "^" correspond au début d'une ligne de texte.


  • Caractères de contrôle
  • Modifient le comportement d'un terminal ou de l'affichage d'un texte.Un caractère de contrôle est une combinaison CONTROL + touche.
  • Ctrl-CTermine un job en avant-plan. Ctrl-DSe déconnecte du shell (similaire à un exit).C'est le caractère "EOF" (End Of File, fin de fichier), qui termine aussi l'entrée de stdin. Ctrl-G "CLOCHE" (bip). Ctrl-HBackspace. Ctrl-JRetour chariot. Ctrl-LFormfeed (efface l'écran du terminal), a le même effet que la commande clear. Ctrl-MNouvelle ligne. Ctrl-UEfface une ligne de l'entrée. Ctrl-ZMet en pause un job en avant-plan.

  • Espace blanc
  • Fonctionne comme un séparateur, séparant les commandes ou les variables.Les espaces blancs sont constitués d'espaces, de tabulations, de lignes blanches ou d'une combinaison de ceux-ci. Dans certains contextes, tels que les affectations de variable, les espaces blancs ne sont pas permis, et sont considérés comme une erreur de syntaxe.
  • Les lignes blanches n'ont aucun effet sur l'action d'un script, et sont plutôt utiles pour séparer visuellement les différentes parties.La variable $IFS est une variable spéciale définissant pour certaines commandes le séparateur des champs en entrée. Elle a pour valeur par défaut un espace blanc.


2.1. Introduction aux variables et aux paramètres

Les variables sont au coeur de tout langage de programmation ou de script. Elles apparaissent dans les opérations arithmétiques et les manipulations de quantités, les analyses de chaînes de caractères, et sont indispensables pour raisonner de façon abstraite à l'aide de symbôles, des unités représentant quelque chose d'autre. Une variable n'est rien d'autre qu'un lieu ou un ensemble de lieux dans la mémoire de l'ordinateur contenant un élément de donnée.


Substitution de variable

Le nom d'une variable est un paramètre fictif pour sa valeur, les données qu'elle contient. L'action de référencer sa valeur est appelée substitution de variable.

  • $
    Commençons par distinguer soigneusement le nom d'une variable et sa valeur. Si
  • variable1
  • est le nom d'une variable, alors
  • $variable1
  • est une référence à sa valeur, l'élément de donnée qu'elle contient. Les seules fois où une variable apparaît "nue" , sans le symbôle $ en préfixe, est lorsqu'elle est déclarée ou assignée, lorsqu'elle est détruite, lorsqu'elle est exportée, ou dans le cas particulier d'une variable désignant un signal (voir Récupérer la sortie). Les affectations s'effectuent avec un = (comme dans var1=27), dans une déclaration read, ou en début de boucle (for var2 in 1 2 3).Entourer une valeur référencée d'apostrophes doubles (" ") n'interfère pas avec la substitution de variable. On appelle cette action la "protection faible" . Utiliser une apostrophe simple (' ') provoque une utilisation littérale du nom de la variable, et aucune substitution n'est effectuée. C'est ce que l'on appelle la "protection forte" . Voir pour une discussion détaillée.Notez que
  • $variable
  • est en fait une forme simplifiée de
  • ${variable}
  • . Dans les contextes où la syntaxe
  • $variable
  • provoque une erreur, la forme complète peut marcher (voir , plus bas).
  • Affectation de variable et substitution
    #!/bin/bash # Variables: affectation et substitution a=375 bonjour=$a #------------------------------------------------------------------------- # Aucun espace de chaque côté du signe = n'est permis lorsque qu'on initialise des variables. # Si "VARIABLE =valeur", #+ le script tente de lancer la commande "VARIABLE" avec en argument "=valeur". # Si "VARIABLE= valeur", #+ le script tente de lancer la commande "valeur" avec #+ la variable d'environnement "VARIABLE" déclarée à "". #------------------------------------------------------------------------- echo bonjour # Pas une référence à une variable, juste la chaine "bonjour" echo $bonjour echo ${bonjour} # Identique à ce qui précède echo "$bonjour" echo "${bonjour}" echo bonjour="A B C D" echo $bonjour # A B C D echo "$bonjour" # A B C D # Comme on peut le voir echo $bonjour et echo "$bonjour" donnent des résultats différents # Protéger une variable préserve les espaces. echo echo '$bonjour' # $bonjour # Désactive le réferencement de variables à l'aide d'apostrophes simples #+ ce qui provoque l'interprétation littérale du "$". # Noter l'effet des différents types de protection. bonjour= # L'affecte d'une valeur nulle echo "\$bonjour (null value) = $bonjour" # Noter qu'affecter une valeur nulle à une variable n'est pas la même chose #+ que de la "détruire" bien que le résultat final soit le même (voir plus bas). # -------------------------------------------------------------- # Il est tout à fait possible d'affecter plusieurs variables sur une même ligne, #+ si elles sont séparées par des espaces. # Attention cela peut rendre le script difficile à lire et peut ne pas être portable. var1=variable1 var2=variable2 var3=variable3 echo echo "var1=$var1 var2=$var2 var3=$var3" # Peut causer des problèmes avec de vieilles versions de "sh". # -------------------------------------------------------------- echo; echo nombres="un deux trois" autres_nomnres="1 2 3" # En cas d'espaces à l'intérieur d'une variable, une protection est nécessaire. echo "nombres = $nombres" echo "autres_nombres = $autres_nombres" # autres_nombres = 1 2 3 echo echo "variable_non_initialisee = $variable_non_initialisee" # Une variable non initialisée a une valeur vide (pas de valeur du tout). variable_non_initialisee= # Declaration sans initialisation #+ (même chose que de lui assigner une valeur vide comme au-dessus) echo "variable_non_initialisee = $variable_non_initialisee" # Elle a encore une valeur nulle. uninitialized_variable=23 # On lui affecte une valeur. unset variable_non_initialisee # On la desafffecte. echo "variable_non_initialisee = $variable_non_initialisee" # Elle a encore une valeur nulle echo exit 0
  • Attention:Une variable non initialisée a une valeur "nulle" - pas de valeur assignée du tout (pas zéro!). Utiliser une variable avant de lui avoir assigné une valeur est généralement cause de problèmes. Attention:Il est néanmoins possible de réaliser des opérations arithmétiques sur une variable non initialisée. Voir aussi Un script (inutile) qui se charge lui-même.


Affectation de variable

  • =
    L'opérateur d'affectation (pas d'espace avant et après) Attention:Ne pas confondre ceci = et -eq, qui teste, plutôt qu'affecte ! Attention:Noter cependant que = peut être un opérateur d'affectation ou de test, suivant le contexte.
  • Affectation de variable : de base
    #!/bin/bash # Variables nues echo # Quand une variable est-elle "nue", c'est-à-dire qu'il lui manque le signe '$'? # Quand on lui affecte une valeur, plutôt que quand elle est référencée. # Affectation a=879 echo "La valeur de \"a\" est $a." # Affectation utilisant 'let' let a=16+5 echo "La valeur de \"a\" est maintenant $a." echo # Dans une boucle 'for' (en fait, un type d'affectation déguisée) echo -n "Les valeurs de \"a\" dans la boucle sont: " for a in 7 8 9 11 do echo -n "$a " done echo echo # Dans une instruction 'read' (un autre type d'affectation) echo -n "Entrez \"a\" " read a echo "La valeur de \"a\" est maintenant $a." echo exit 0
  • Affectation de variable , de base et élaboré
    #!/bin/bash a=23 # Cas simple echo $a b=$a echo $b # Maintenant, allons un peu plus loin (substitution de commande). a=`echo Hello!` # Affecte le résultat de la commande 'echo' à 'a' echo $a # Notez que utiliser un point d'exclamation dans une substitution de commandes #+ ne fonctionnera pas à partir de la ligne de commande, #+ car ceci déclenche le mécanisme d'historique de Bash. # Néanmoins, à l'intérieur d'un script, les fonctions d'historique sont #+ désactivées. a=`ls -l` # Affecte le résultat de la commande 'ls -l' à 'a' echo $a # Néanmoins, sans guilleets, supprime les tabulations et les #+ retours chariots. echo echo "$a" # La variable entre guillemets préserve les espaces blancs. # (Voir le chapitre sur les "Guillemets.") exit 0
  • Affectation de variable utilisant le mécanisme $(...) (une méthode plus récente que l'apostrophe inverse)


Les variables Bash ne sont pas typées

A l'inverse de nombreux langages de programmation, Bash ne regroupe pas ses variable par "type" . Essentiellement, les variables bash sont des chaînes de caractères, mais, suivant le contexte, Bash autorise des opérations entières et des comparaisons sur ses variables, le facteur décisif étant la seule présence de chiffres dans la variable.

Entier ou chaîne?
#!/bin/bash # int-or-string.sh: Entier ou chaîne de caractères? a=2334 # Entier. let "a += 1" echo "a = $a " # a = 2335 echo # Entier, toujours. b=${a/23/BB} # Substitue "BB" à "23". # Ceci transforme $b en une chaîne de caractères. echo "b = $b" # b = BB35 declare -i b # Le déclarer comme entier n'aide pas. echo "b = $b" # b = BB35 let "b += 1" # BB35 + 1 = echo "b = $b" # b = 1 echo c=BB34 echo "c = $c" # c = BB34 d=${c/BB/23} # Substitue "23" à "BB". # Ceci fait de $d un entier. echo "d = $d" # d = 2334 let "d += 1" # 2334 + 1 = echo "d = $d" # d = 2335 echo # Et à propos des variables null? e="" echo "e = $e" # e = let "e += 1" # Les opérations arithmétiques sont-elles permises sur # une variable null? echo "e = $e" # e = 1 echo # Variable null transformée en entier. # Et concernant les variables non déclarées? echo "f = $f" # f = let "f += 1" # Opérations arithmétiques permises? echo "f = $f" # f = 1 echo # Variable non déclarée transformée en entier. # Les variables dans Bash sont essentiellement non typées. exit 0

Des variables non typées ont des bons et des mauvais côtés. Elles permettent plus de flexibilité dans l'écriture des scripts (assez de corde pour se pendre soi même!) et rendent plus aisé le ciselage des lignes de code. Néanmoins elles permettent aux erreurs de s'infiltrer dans les programmes et encouragent des habitudes de code bâclé.

Il incombe au programmeur de garder une trace du type des variables du script. Bash ne le fera pas à votre place.


Types spéciaux de variables

  • variables locales
    des variables visibles seulement à l'intérieur d'un bloc de code ou d'une fonction (voir aussi variables locales dans fonctions)

  • variables d'environnement
    variables qui affectent le comportement du shell et de l'interface utilisateur Remarque:Dans un contexte plus général, chaque processus a un "environnement" , c'est-à-dire un groupe de variables contenant l'information à laquelle pourrait faire référence le processus. En ce sens, le shell se comporte comme n'importe quel processus. Remarque:Chaque fois qu'un shell démarre, il crée les variables shell correspondantes à ses propres variables d'environnement. Mettre à jour ou ajouter de nouvelles variables force le shell à mettre à jour son environnement, et tous les processus fils (les commandes qu'il exécute) héritent de cet environnement. Attention:L'espace alloué à l'environnement est limité. Créer trop de variables d'environnement ou une variable d'environnement qui utilise un espace excessif peut causer des problèmes. Attention:
  • bash$ eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ/'`" bash$ du bash: /usr/bin/du: Argument list too long
  • Attention:(Merci, S. C. pour la clarification, et pour avoir fourni l'exemple ci-dessus.)Si un script déclare des variable d'environnement, il faut qu'elles soient "exportées" , c'est-à-dire, rapportées à l'environnement local du script. C'est la fonction de la commande export . Remarque:Un script peut exporter des variables seulement aux processus fils, c'est-à-dire seulement aux commandes ou processus que ce script particulier initie. Un script invoqué depuis la ligne de commande ne peux pas ré-exporter des variables à destination de l'environnement de la ligne de commande dont il est issu. Des processus fils ne peuvent pas ré exporter de variables aux processus parents qui les ont fait naître.---

  • paramètres positionnels
    les arguments passés aux scripts depuis la ligne de commande - $0, $1, $2, $3... $0 est le nom du script lui-même, $1 est le premier argument, $2 le second, $3 le troisième, et ainsi de suite. (13) Après $9, les arguments doivent être entourés d'accolades, par exemple, ${10}, ${11}, ${12}.Les variables spéciales $* et $@ représentent tous les paramètres positionnels.
  • Paramètres positionnels
    #!/bin/bash # Appelez ce script avec au moins 10 paramètres, par exemple # ./scriptname 1 2 3 4 5 6 7 8 9 10 MINPARAMS=10 echo echo "Le nom de ce script est \"$0\"." # Ajoutez ./ pour le répertoire courant. echo "Le nom de ce script est \"`basename $0`\"." # Supprime le chemin du script (voir 'basename') echo if [ -n "$1" ] # La variable testée est entre guillemets. then echo "Le paramètre #1 est $1" # Nous avons besoin des guillemets pour échapper # fi if [ -n "$2" ] then echo "Le paramètre #2 est $2" fi if [ -n "$3" ] then echo "Le paramètre #3 est $3" fi # ... if [ -n "${10}" ] # Les paramètres supérieures à $9 doivent être compris entre #+ accolades. then echo "Le paramètre #10 est ${10}" fi echo "-----------------------------------" echo "Tous les paramètres de la ligne de commande sont: "$*"" if [ $# -lt "$MINPARAMS" ] then echo echo "Donnez-moi au moins $MINPARAMS arguments en ligne de commande!" fi echo exit 0
  • La notation avec accolades pour les paramètres positionnels permet de référencer plutôt simplement le dernier argument passé à un script sur la ligne de commande. Ceci requiert également le référencement indirect.Certains scripts peuvent effectuer différentes opérations suivant le nom sous lequel ils sont invoqués. Pour que cela fonctionne, le script a besoin de tester $0, le nom sous lequel il a été invoqué. Il doit aussi y avoir des liens symboliques vers tous les différents noms du script. Astuce:Si un script attend un paramètre en ligne de commande mais qu'il est invoqué sans, cela peut causer une affectation à valeur nulle, généralement un résultat non désiré. Une façon d'empêcher cela est d'ajouter un caractère supplémentaire des deux côtés de l'instruction d'affectation utilisant le paramètre positionnel attendu. ---
  • wh, recherche d'un nom de domaine avec whois
    #!/bin/bash # Fait une recherche 'whois nom-domaine' sur l'un des trois serveurs: # ripe.net, cw.net, radb.net # Placez ce script, nommé 'wh' dans /usr/local/bin # Requiert les liens symboliques: # ln -s /usr/local/bin/wh /usr/local/bin/wh-ripe # ln -s /usr/local/bin/wh /usr/local/bin/wh-cw # ln -s /usr/local/bin/wh /usr/local/bin/wh-radb if [ -z "$1" ] then echo "Usage: `basename $0` [nom-domaine]" exit 65 fi case `basename $0` in # Vérifie le nom du script et appele le bon serveur "wh" ) whois $1@whois.ripe.net;; "wh-ripe") whois $1@whois.ripe.net;; "wh-radb") whois $1@whois.radb.net;; "wh-cw" ) whois $1@whois.cw.net;; * ) echo "Usage: `basename $0` [nom-domaine]";; esac exit 0
  • --- La commande shift réassigne les paramètres positionnels, ce qui a le même effet que de les déplacer vers la gauche d'un rang.$1 --- $2, $2 --- $3, $3 --- $4, etc.Le vieux $1 disparaît,mais $0 (le nom du script) ne change pas. Si vous faites usage d'un grand nombre de paramètres positionnels dans un script, shift vous permet d'accèder à ceux au-delà de 10, bien que la notation notation {accolades} le permette également.
  • Utiliser shift
    #!/bin/bash # Utilisation de 'shift' pour voir tous les paramètres de position. # Nommez ce script quelque chose comme shft, #+ et appelez-le avec quelques paramètres, par exemple # ./shft a b c def 23 skidoo until [ -z "$1" ] # Jusqu'à ne plus avoir de paramètres... do echo -n "$1 " shift done echo # Retour chariot supplémentaire. exit 0
  • Remarque:La commande shift fonctionne aussi avec les paramètres passés à une fonction. Voir Astuce de valeur de retour.


2.2. Guillemets

Encadrer une chaîne de caractères avec des guillemets a pour effet de protéger les caractères spéciaux et d'empêcher leur réinterprétation ou expansion par le shell ou par un script shell. Un caractère est "spécial" si son interprétation a une autre signification que la chaîne elle-même, comme par exemple le caractère joker *.

bash$ ls -l [Vv]* -rw-rw-r-- 1 bozo bozo 324 Apr 2 15:05 VIEWDATA.BAT -rw-rw-r-- 1 bozo bozo 507 May 4 14:25 vartrace.sh -rw-rw-r-- 1 bozo bozo 539 Apr 14 17:11 viewdata.sh bash$ ls -l '[Vv]*' ls: [Vv]*: No such file or directory

Remarque : Certains programmes et utilitaires peuvent toujours réinterpréter ou étendre les caractères spéciaux placés dans une chaîne de caractères entre guillemets. Les guillemets permettent donc en particulier de protéger du shell les paramètres dans une ligne de commande, tout en laissant au programme appelé la possibilité de les étendre et réinterpréter.

Remarque :

bash$ grep '[Pp]remière' *.txt fichier1.txt:C'est la première ligne de fichier1.txt. fichier2.txt:C'est la Première ligne de fichier2.txt.

Remarque : Notez que la version sans guillemets

grep [Ff]irst *.txt

fonctionne avec le shell Bash, mais pas sous tcsh.

Il est généralement conseillé de placer une chaîne de caractères entre guillemets doubles (" ") lorsqu'elle doit contenir une variable. Ça préserve tous les caractères spéciaux à l'intérieur du nom de variable, sauf $, ` (guillemet inversé), et \ (échappement). Comme $ reste interprété comme un caractère spécial, cela permet de référencer une variable entre guillemets ("$variable"), c'est-à-dire de remplacer la variable par sa valeur (voir plus haut Affectation de variable et substitution ).

Vous pouvez utiliser les guillemets doubles pour éviter la séparation de mots. (14) Un argument compris entre guillemets doubles se présente comme un seul mot, même s'il contient des espaces blancs.

Astuce : Mettre les arguments d'une instruction echo entre doubles guillemets est seulement nécessaire lorsque la séparation de mots pose problème.

Afficher des variables bizarres
#!/bin/bash # weirdvars.sh: Affiche des variables bizarres. var="'(]\\{}\$\"" echo $var # '(]\{}$" echo "$var" # '(]\{}$" Ne fait pas de différence. echo IFS='\' echo $var # '(] {}$" \ converti en espace. echo "$var" # '(]\{}$" # Exemples ci-dessus donnés par S.C. exit 0

Les guillemets simples (' ') opèrent de manière similaire aux guillemets doubles, mais ne permettent pas de référencer des variables, car la signification spéciale de $ est désactivée. A l'intérieur des guillemets simples, tous les caractères spéciaux autres que ' sont interprétés littéralement. Vous pouvez considérer que les guillemets simples ( "citation totale" ) sont une façon plus stricte de placer entre guillemets que les guillemets doubles ( "citation partielle" ).

Remarque : Comme même le caractère d'échappement (\) obtient une interprétation littérale avec les guillemets simples, essayer d'entourer un guillemet simple avec des guillemets simples ne produira pas le résultat attendu.

Échapper est une méthode pour mettre entre guillemets un caractère seul. L'échappement (\) précédant un caractère dit au shell d'interpréter le caractère littéralement.

Attention : Avec certaines commandes et utilitaires, tels que echo et sed, échapper un caractère peut avoir l'effet inverse - cela peut activer un comportement particulier pour ce caractère.

  • utilisé avec echo et sed


  • \n
    passe à la ligne

  • \r
    renvoie le curseur en début de ligne

  • \t
    tabulation

  • \v
    tabulation verticale

  • \b
    retour en arrière

  • \a
    "alerte" (sonore ou visuelle)

  • \0xx
    le caractère dont le code ASCII en octal est 0xx
  • Caractères d'échappement
    #!/bin/bash # escaped.sh: caractères d'échappement echo; echo echo "\v\v\v\v" # Affiche \v\v\v\v litérallement. # Utilisez l'option -e avec 'echo' pour afficher les caractères d'échappement. echo "======================" echo "TABULATIONS VERTICALES" echo -e "\v\v\v\v" # Affiche 4 tabulations verticales. echo "=======================" echo "GUILLEMET DOUBLE" echo -e "\042" # Affiche " (guillemets, caractère octal ASCII 42). echo "=======================" # La construction $'\X' rend l'option -e inutile. echo; echo "RETOUR CHARIOT ET SON" echo $'\n' # Retour chariot. echo $'\a' # Alerte (son). echo "=======================" echo "GUILLEMETS" # Les versions 2 et ultérieures de Bash permettent l'utilisation de la #+ construction $'\nnn'. # Notez que dans ce cas, '\nnn' est une valeur octale. echo $'\t \042 \t' # Guillemet (") entouré par des tabulations. # Cela fonctionne aussi avec des valeurs hexadécimales, dans une construction #+ du type $'\xhhh'. echo $'\t \x22 \t' # Guillemet (") entouré par des tabulations. # Merci, Greg Keraunen, pour nous l'avoir indiqué. # Les versions précédentes de Bash permettent '\x022'. echo "=======================" echo # Affecter des caractères ASCII à une variable. # ---------------------------------------------- guillemet=$'\042' # " affecté à une variable. echo "$guillemet Ceci est un chaîne de caractères mise entre guillemets, $guillemet et ceci reste en dehors des guillemets." echo # Concaténer des caractères ASCII dans une variable. trois_soulignes=$'\137\137\137' # 137 est le code octal ASCII pour '_'. echo "$trois_soulignes SOULIGNE $trois_soulignes" echo ABC=$'\101\102\103\010' # 101, 102, 103 sont les codes octals de A, B, C. echo $ABC echo; echo echappe=$'\033' # 033 est le code octal pour l'échappement (Echap). echo "\"echappe\" s'affiche comme $echappe" # pas de sortie visible. echo; echo exit 0
  • Voir Expansion de chaîne de caractères pour d'autres exemples d'extensions de chaînes de caractères avec la structure $' '.

  • \"
    donne au guillemet sa signification littérale

  • \$
    donne au dollar sa signification littérale (un nom de variable suivant un \$ ne sera pas référencé)

  • \\
    donne à l'anti-slash sa signification littérale

Remarque : Le comportement de \ est dicté par son "auto-échappement", sa mise entre guillemets ou son apparition dans une substitution de commandes ou dans un document en ligne.

Remarque : Les élements d'une chaîne de caractères affectée à une variable peuvent être échappés, mais le caractère d'échappement seul ne peut être affecté à une variable.

Échapper un espace peut empêcher la séparation de mots dans une liste d'arguments pour une commande.

L'échappement permet également d'écrire une commande sur plusieurs lignes. Normalement, chaque ligne séparée constitue une commande différente, mais un échappement à la fin d'une ligne échappe le caractère de saut de ligne, et la séquence de la commande continue sur la ligne suivante.

Remarque : Si la ligne d'un script termine avec un |, un caractère tube, alors il n'est pas absolument nécessaire de mettre un échappement \. Il est néanmoins considéré comme une bonne pratique de programmation de toujours échapper une ligne de code qui continue sur la ligne suivante.


2.3. Sortie et code de sortie (ou d'état)

...il existe des coins sombres dans le shell Bourne et les gens les utilisent tous.

La commande exit est utilisable pour terminer un script, comme dans un programme C. Elle peut également renvoyer une valeur, qui sera disponible pour le processus parent du script.

Chaque commande renvoie un code de sortie (quelque fois nommé état de retour ). Une commande ayant réussi renvoie un 0, alors qu'une ayant échoué renvoie une valeur différente de zéro qui est habituellement interprétable comme un code d'erreur. Les commandes, programmes et utilitaires UNIX ayant bien fonctionné, renvoient un code de sortie 0 relatif à leur exécution réussie, bien qu'il y ait quelques exceptions.

De même, les fonctions dans un script et le script lui-même renvoient un code de sortie. La dernière commande exécutée dans la fonction ou le script détermine le code de sortie. A l'intérieur d'un script, une commande

exit nnn

peut être employée pour retourner un code de sortie nnn au shell. (nnn doit être un nombre décimal compris entre 0 et 255).

Remarque : Lorsqu'un script se termine avec un exit sans paramètres, le code de sortie du script est le code de sortie de la dernière commande exécutée dans le script (sans compter le exit).

$? lit le code de sortie de la dernière commande exécutée. Après la fin d'une fonction, $? donne le code de sortie de la dernière commande exécutée dans la fonction. C'est la manière de Bash de donner aux fonctions une "valeur de retour" . Après la fin d'un script, un $? sur la ligne de commande indique le code de sortie du script, c'est-à-dire celui de la dernière commande exécutée dans le script qui est, par convention,

0

en cas de succès ou un entier compris entre 1 et 255 en cas d'erreur.

exit / code de sortie
#!/bin/bash echo bonjour echo $? # Code de sortie 0 renvoyé car la commande s'est correctement # exécutée. lskdf # Commande non reconnue. echo $? # Code de sortie différent de zéro car la commande a échoué. echo exit 113 # Retournera 113 au shell. # Pour vérifier ceci, tapez "echo $?" une fois le script terminé. # Par convention, un 'exit 0' indique un succès, #+ alors qu'un code de sortie différent de zéro indique une erreur ou une #+ condition anormale.

$? est particulièrement utile pour tester le résultat d'une commande dans un script (voir Utiliser cmp pour comparer deux fichiers à l'intérieur d'un script. et Chercher les mots dans une liste pour tester leur validité).

Remarque : Le !, qualificateur logique du "non" , inverse le résultat d'un test ou d'une commande et ceci affecte son code de sortie. true # la commande intégrée "true" echo "code de sortie de \"true\" = $?" # 0 ! true echo "code de sortie de \"! true\" = $?" # 1 # Notez que "!" nécessite un espace. # !true renvoie une erreur "command not found" # Merci, S.C.

Attention : Certains codes de sortie ont une signification spéciale et ne devraient pas être employés par l'utilisateur dans un script.


2.4. Tests

Tout langage de programmation complet peut tester des conditions et agir suivant le résultat du test. Bash dispose de la commande test, de différents opérateurs à base de crochets et de parenthèses, ainsi que de la construction if/then.


Constructions de tests

  • Une construction if/then teste si l'état de la sortie d'une liste de commandes vaut 0 (car 0 indique le "succès" suivant les conventions UNIX), et dans ce cas, exécute une ou plusieurs commandes.
  • Il existe une commande dédiée appelée [ (caractère spécial crochet gauche). C'est un synonyme pour test, qui est intégré pour des raisons d'optimisation. Cette commande prend ses arguments comme des expressions de comparaisons ou comme des tests de fichiers et renvoit un état de sortie correspondant au résultat de la comparaison (0 pour vrai et 1 pour faux).
  • Avec la version 2.02, Bash a introduit la commande de test étendue [[ ... ]], réalisant des comparaisons d'une façon familière aux programmeurs venant d'autres langages. Notez que [[ est un mot clé, pas une commande.Bash considère
  • [[ $a -lt $b ]]
  • comme un seul élément, renvoyant un état de sortie.Les constructions (( ... )) et let ... renvoient aussi un état de sortie de 0 si les expressions arithmétiques qu'elles évaluaient se résolvent en une valeur non nulle. Ces constructions d'expansion arithmétique peuvent donc être utilisées pour réaliser des comparaisons arithmétiques.
  • Un if peut tester n'importe quelle commande, pas seulement des conditions entourées par des crochets.
  • Une construction if/then peut contenir des comparaisons et des tests imbriqués. L'explication détaillée du "if-test" provient de Stephane Chazelas.
Où est le vrai?
#!/bin/bash echo echo "Test de \"0\"" if [ 0 ] # zéro then echo "0 est vrai." else echo "0 est faux." fi # 0 est vrai. echo echo "Test de \"1\"" if [ 1 ] # un then echo "1 est vrai." else echo "1 est faux." fi # 1 est vrai. echo echo "Test de \"-1\"" if [ -1 ] # moins un then echo "-1 est vrai." else echo "-1 est faux." fi # -1 est vrai. echo echo "Test de \"NULL\"" if [ ] # NULL (condition vide) then echo "NULL est vrai." else echo "NULL est faux." fi # NULL est faux. echo echo "Test de \"xyz\"" if [ xyz ] # chaîne de caractères then echo "Chaîne de caractères au hasard est vrai." else echo "Chaîne de caractères au hasard est faux." fi # Chaîne de caractères au hasard est vrai. echo echo "Test de \"\$xyz\"" if [ $xyz ] # Teste si $xyz est nul, mais... # c'est seulement une variable non initialisée. then echo "Une variable non initialisée est vrai." else echo "Une variable non initialisée est faux." fi # Une variable non initialisée est faux. echo echo "Test de \"-n \$xyz\"" if [ -n "$xyz" ] # Plus correct. then echo "Une variable non initialisée est vrai." else echo "Une variable non initialisée est faux." fi # Une variable non initialisée est faux. echo xyz= # Initialisé, mais à une valeur nulle. echo "Test de \"-n \$xyz\"" if [ -n "$xyz" ] then echo "Une variable nulle est vrai." else echo "Une variable nulle est faux." fi # Une variable nulle est faux. echo # Quand "faux" est-il vrai? echo "Test de \"false\"" if [ "false" ] # Il semble que "false" ne soit qu'une chaîne de #+ caractères. then echo "\"false\" est vrai." #+ et il est testé vrai. else echo "\"false\" est faux." fi # "false" est vrai. echo echo "Test de \"\$false\"" # De nouveau, une chaîne non initialisée. if [ "$false" ] then echo "\"\$false\" est vrai." else echo "\"\$false\" est faux." fi # "$false" est faux. # Maintenant, nous obtenons le résultat attendu. echo exit 0

Exercice

Expliquez le comportement de Où est le vrai?, ci-dessus.

Remarque : Quand if et then sont sur la même ligne lors d'un test, un point-virgule doit finir l'expression if. if et then sont des mots clés. Les mots clés (et les commandes) commençant une expression doivent être terminés avant qu'une nouvelle expression sur la même ligne puisse commencer.

Remarque :

  • elif
  • elif
  • est une contraction pour else if. Le but est de faire tenir une construction if/then dans une autre construction déjà commencée.

La construction

if test condition-vraie

est l'exact équivalent de

if [ condition-true ]

. De cette façon, le crochet gauche, [, est un raccourci appelant la commande test. Le crochet droit fermant, ], ne devrait donc pas être nécessaire dans un test if , néanmoins, les dernières versions de Bash le requièrent.

Remarque : La commande test est une commande interne de Bash, permettant de tester les types de fichiers et de comparer des chaînes de caractères. Donc, dans un script Bash, test n'appelle pas le binaire externe /usr/bin/test, qui fait partie du paquet sh-utils. De même, [ n'appelle pas /usr/bin/[, qui est un lien vers /usr/bin/test.

Remarque :

bash$ type test test is a shell builtin bash$ type '[' [ is a shell builtin bash$ type '[[' [[ is a shell keyword bash$ type ']]' ]] is a shell keyword bash$ type ']' bash: type: ]: not found

Equivalences de test, /usr/bin/test, [ ], et /usr/bin/[
#!/bin/bash echo if test -z "$1" then echo "Pas arguments sur la ligne de commande." else echo "Le premier argument de la ligne de commande est $1." fi echo if /usr/bin/test -z "$1" # Même résultat que la commande intégrée "test". then echo "Pas arguments sur la ligne de commande." else echo "Le premier argument de la ligne de commande est $1." fi echo if [ -z "$1" ] # Identique fonctionnellement au bloc de code #+ ci-dessus. # if [ -z "$1" devrait fonctionner, mais... #+ Bash répond qu'un crochet fermant manque. then echo "Pas arguments sur la ligne de commande." else echo "Le premier argument de la ligne de commande est $1." fi echo if /usr/bin/[ -z "$1" # Encore une fois, fonctionnalité identique à #+ ci-dessus. # if /usr/bin/[ -z "$1" ] # Fonctionne, mais donne un message d'erreur. then echo "Pas arguments sur la ligne de commande." else echo "Le premier argument de la ligne de commande est $1." fi echo exit 0

La construction [[ ]] est l'équivalent shell de [ ]. C'est la commande étendue de test, venant de ksh88.

Remarque : Il n'est pas possible de faire de la complétion de noms de fichiers ou de la séparation de mots lorsqu'on se trouve entre [[ et ]], mais la complétion de paramètres et la substitution de commandes sont disponibles.

Astuce : Utiliser la construction [[ ... ]], au lieu de [ ... ] peut vous permettre d'éviter des erreurs de logique dans vos scripts. Par exemple, les opérateurs &&, ||, et fonctionnent à l'intérieur d'un test [[ ]] bien qu'ils génèrent une erreur à l'intérieur d'une construction [ ].

Remarque : Après un if, ni la commande test ni les crochets de test ( [ ] ou [[ ]] ) ne sont nécessaires. La construction "if COMMANDE" renvoie l'état de sortie de la COMMANDE.

Remarque : De manière identique, une condition à l'intérieur de crochets de test peut fonctionner sans if, si elle est utilisée avec une construction en liste.

La construction (( )) évalue une expression arithmétique. Si l'expression vaut 0, elle renvoie un code de sortie de 1, ou "false" . Une expression différente de 0 renvoie 0, ou "true" . Ceci est en totale contradiction avec l'utilisation des constructions test et [ ] évoquées précédemment.

Tests arithmétiques en utilisant (( ))
#!/bin/bash # Tests arithmétiques. # La construction (( ... )) évalue et teste les exepressions numériques. # Code de sortie opposé à la construction [ ... ] ! (( 0 )) echo "Le code de sortie de \"(( 0 ))\" est $?." # 1 (( 1 )) echo "Le code de sortie de \"(( 1 ))\" est $?." # 0 (( 5 > 4 )) # vrai echo "Le code de sortie de \"(( 5 > 4 ))\" est $?." # 0 (( 5 > 9 )) # faux echo "Le code de sortie de \"(( 5 > 9 ))\" est $?." # 1 (( 5 - 5 )) # 0 echo "Le code de sortie de \"(( 5 - 5 ))\" est $?." # 1 (( 5 / 4 )) # Division OK. echo "Le code de sortie de \"(( 5 / 4 ))\" est $?." # 0 (( 1 / 2 )) # Résultat de la division < 1. echo "Le code de sortie de \"(( 1 / 2 ))\" est $?." # Arrondie à 0. # 1 (( 1 / 0 )) 2>/dev/null # Division par 0... illégale. echo "Le code de sortie de \"(( 1 / 0 ))\" est $?." # 1 # Quel effet a "2>/dev/null"? # Qu'arriverait-t'il s'il était supprimé? # Essayez de le supprimer, et relancer le script. exit 0

Opérateurs de test de fichiers

  • -e
    le fichier existe

  • -f
    le fichier est un fichier ordinaire (ni un répertoire ni un fichier périphérique)

  • -s
    le fichier a une taille supérieure à zéro

  • -d
    le fichier est un répertoire

  • -b
    le fichier est un périphérique de type bloc (lecteur de disquettes, lecteur de cdroms, etc.)

  • -c
    le fichier est un périphérique de type caractère (clavier, modem, carte son, etc...)

  • -p
    le fichier est un tube nommé

  • -h
    le fichier est un lien symbolique

  • -L
    le fichier est un lien symbolique

  • -S
    le fichier est une socket

  • -t
    le fichier (descripteur) est associé avec un terminalCette option permet de tester dans un script si stdin (
  • [ -t 0 ]
  • ) ou stdout (
  • [ -t 1 ]
  • ) est un terminal.

  • -r
    le fichier dispose du droit de lecture (pour l'utilisateur ayant lancé la commande)

  • -w
    le fichier dispose du droit d'écriture (pour l'utilisateur ayant lancé la commande)

  • -x
    le fichier dispose du droit d'exécution (pour l'utilisateur ayant lancé la commande)

  • -g
    le fichier dispose du droit set-group-id (sgid) sur ce fichier ou répertoireSI un répertoire dispose du droit sgid, alors un fichier créé dans ce répertoire appartient au groupe du répertoire, et pas nécessairement au groupe de l'utilisateur qui a créé ce fichier. Ceci est utile pour un répertoire partagé par un groupe de travail.

  • -u
    le fichier dispose du droit set-user-id (suid)Un binaire appartenant à root et disposant du droit set-user-id sera lancé avec les privilèges de root, même si un utilisateur ordinaire l'utilise. (15) C'est intéressant pour les exécutables (tels que pppd et cdrecord) qui ont besoin d'accéder au matériel du système. Sans cette option, ces binaires ne pourraient pas être utilisés par un utilisateur ordinaire.
  • -rwsr-xr-t 1 root 178236 Oct 2 2000 /usr/sbin/pppd
  • Un fichier disposant du droit suid affiche un s dans ses permissions.

  • -k
    sticky bit misHabituellement connu sous le nom de "sticky bit" , le droit save-text-mode est un droit très particulier pour les fichiers. Si un fichier en dispose, celui-ci sera conservé en mémoire cache, pour un accès plus rapide. (16) Placé sur un répertoire, il restreint les droits d'écriture. Cela ajoute un t aux permissions du fichier ou du répertoire.
  • drwxrwxrwt 7 root 1024 May 19 21:26 tmp/
  • Si un utilisateur ne possède pas un répertoire qui dispose du droit sticky bit, mais qu'il a le droit d'écriture sur ce répertoire, il peut seulement supprimer les fichiers dont il est le propriétaire. Ceci empêche les utilisateurs de supprimer par inadvertance les fichiers des autres utilisateurs. Un répertoire disposant de ce droit est par exemple /tmp.

  • -O
    vous êtes le propriétaire du fichier

  • -G
    vous faites partie du groupe propriétaire du fichier

  • -N
    le fichier a été modifié depuis sa dernière lecture

  • f1 -nt f2
    le fichier f1 est plus récent que le fichier f2

  • f1 -ot f2
    le fichier f1 est plus ancien que le fichier f2

  • f1 -ef f2
    le fichier f1 et le fichier f2 sont des liens symboliques vers le même fichier

  • !
    "not" -- inverse le sens des tests précédents (renvoie vrai si la condition est fausse).

Test de liens cassés
#!/bin/bash # broken-link.sh # Ecrit par Lee bigelow ligelowbee@yahoo.com # Utilisé avec sa permission. #Un pur script shell pour trouver des liens symboliques morts et les afficher #entre guillemets pour qu'ils puissent être envoyés à xargs et être ainsi mieux #gérés :) #eg. broken-link.sh /repertoire /autrerepertoire|xargs rm # #Néanmoins, ceci est une meilleure méthode: # #find "repertoire" -type l -print0|\ #xargs -r0 fichier|\ #grep "lien symbolique mort"| #sed -e 's/^\|: *lienmort.*$/"/g' # #mais cela ne serait pas du bash pur. #Attention au système de fichiers /proc et aux liens circulaires! ############################################################## #Si aucun argument n'est passé au script, initialise repertoires au répertoire #courant. Sinon, initialise repertoires aux arguments passés. #################### [ $# -eq 0 ] && repertoires=`pwd` || repertoires=$@ #Configure la fonction verifliens pour vérifier si le répertoire en argument #ne contient pas de liens morts et pour les afficher. #Si un des éléments du répertoire est un sous-répertoire, alors envoie ce #sous-répertoire à la fonction verifliens. ########## verifliens () { for element in $1/*; do [ -h "$element" -a ! -e "$element" ] && echo \"$element\" [ -d "$element" ] && verifliens $element # Bien sûr, '-h' teste les liens symboliques, '-d' les répertoires. done } #Envoie chaque argument qui a été passé au script à la fonction verifliens #si il s'agit d'un répertoire valider. Sinon, affiche un message d'erreur et #le message d'usage. ################ for repertoire in $repertoires; do if [ -d $repertoire ] then verifliens $repertoire else echo "$repertoire n'est pas un répertoire" echo "Usage: $0 repertoire1 repertoire2 ..." fi done exit 0

Cacher le cookie jar, Un remplaçant de grep pour les fichiers binaires, Fileinfo: opérer sur une liste de fichiers contenue dans une variable, Créer un disque ram et mailformat: Formater un courrier électronique illustrent aussi l'utilisation des opérateurs de test de fichiers.


Opérateurs de comparaison (binaire)

  • -eq
    est égal à
  • if [ "$a" -eq "$b" ]


  • -ne
    n'est pas égal à
  • if [ "$a" -ne "$b" ]


  • -gt
    est plus grand que
  • if ["$a" -gt "$b" ]


  • -ge
    est plus grand ou égal à
  • if [ "$a" -ge "$b" ]


  • -lt
    est plus petit que
  • if [ "$a" -lt "$b" ]


  • -le
    est plus petit ou égal à
  • if [ "$a" -le "$b" ]



  • est plus petit que (à l'intérieur de parenthèses doubles)
  • (("$a" "$b"))


  • =
    est plus petit ou égal à (à l'intérieur de parenthèses doubles)
  • (("$a" = "$b"))



  • est plus grand que (à l'intérieur de parenthèses doubles)
  • (("$a" "$b"))


  • =
    est plus grand ou égal à (à l'intérieur de parenthèses doubles)
  • (("$a" = "$b"))


  • =
    est égal à
  • if [ "$a" = "$b" ]


  • ==
    est égal à
  • if [ "$a" == "$b" ]
  • Ceci est un synonyme de =.

  • !=
    n'est pas égal à
  • if [ "$a" != "$b" ]
  • Cet opérateur utilise la reconnaissance de modèles à l'intérieur de constructions [[ ... ]].


  • est plus petit que, d'après l'ordre alphabêtique ASCII
  • if [[ "$a" "$b" ]]
  • if [ "$a" \ "$b" ]
  • Notez que "" a besoin d'être dans une séquence d'échappement s'il se trouve à l'intérieur de
  • [ ]
  • .


  • est plus grand que, d'après l'ordre alphabêtique ASCII
  • if [[ "$a" "$b" ]]
  • if [ "$a" \ "$b" ]
  • Notez que "" a besoin d'être dans une séquence d'échappement s'il se trouve à l'intérieur de
  • [ ]
  • .Voir Un viel ami: Le tri Bubble Sort pour une application de cet opérateur de comparaison.

  • -z
    la chaîne de caractères est "vide" , c'est-à-dire qu'elle a une taille nulle

  • -n
    la chaîne de caractères n'est pas "vide" . Attention:Attention : Le test
  • -n
  • nécessite absolument que la chaîne de caractères soit entre quotes à l'intérieur des crochets de test. Utiliser une chaîne sans quotes avec
  • ! -z
  • , voire simplement la chaîne sans quote à l'intérieur des crochets (voir vérification si une chaîne nulle) fonctionne habituellement, néanmoins, c'est une pratique peu sûre. Placez toujours vos chaînes de caractères à tester entre quotes. (17)

comparaisons arithmétiques et de chaînes de caractères
#!/bin/bash a=4 b=5 # Ici "a" et "b" peuvent être traités soit comme des entiers soit comme des #+ chaînes de caractères. # Il y a un peu de flou entre les comparaisons arithmétiques et de chaînes de #+ caractères, car les variables Bash ne sont pas typées fortement. # Bash permet des opérations et des comparaisons d'entiers sur des variables #+ contenant des caractères uniquements numériques. # Faites attention. echo if [ "$a" -ne "$b" ] then echo "$a n'est pas égal à $b" echo "(comparaison arithmétique)" fi echo if [ "$a" != "$b" ] then echo "$a n'est pas égal à $b." echo "(comparaison de chaînes de caractères)" # "4" != "5" # ASCII 52 != ASCII 53 fi # Pour cette instance particulière, "-ne" et "!=" fonctionnent. echo exit 0
vérification si une chaîne nulle
#!/bin/bash # str-test.sh: Tester des chaînes nulles et sans guillemets, # "but not strings and sealing wax, not to mention cabbages and kings..." # En utilisant if [ ... ] # Si une chaîne n'a pas été initialisé, elle n'a pas de valeur définie. # Cet état est appelé "null" (ce qui n'est pas identique à zéro). if [ -n $chaine1 ] # $chaine1 n'est ni déclarée ni initialisée. then echo "La chaîne \"chaine1\" n'est pas nulle." else echo "La chaîne \"chaine1\" est nulle." fi # Mauvais résultat. # Affiche $chaine1 comme non nulle, bien qu'elle n'ait pas été initialisée. echo # Essayons de nouveau. if [ -n "$chaine1" ] # Cette fois, $chaine1 est entre guillemets. then echo "La chaîne \"chaine1\" n'est pas nulle." else echo "La chaîne \"chaine1\" est nulle." fi # Entourer les chaînes avec des crochets de test. echo if [ $chaine1 ] # Cette fois, $chaine1 est seule. then echo "La chaîne \"chaine1\" n'est pas nulle." else echo "La chaîne \"chaine1\" est nulle." fi # Ceci fonctionne. # L'opérateur de test [ ] tout seul détecte si la chaîne est nulle. # Néanmoins, une bonne pratique serait d'y mettre des guillemets ("$chaine1"). # # Comme Stephane Chazelas le dit, # if [ $string 1 ] a un argument, "]" # if [ "$string 1" ] a deux arguments, la chaîne "$chaine1" vide et "]" echo chaine1=initialisée if [ $chaine1 ] # Une fois encore, $chaine1 est seule. then echo "La chaîne \"chaine1\" n'est pas nulle." else echo "La chaîne \"chaine1\" est nulle." fi # De nouveau, cela donne le résultat correct. # Il est toujours préférable de la mettre entre guillemets ("$chaine1"), parce # que... chaine1="a = b" if [ $chaine1 ] # $chaine1 est de nouveau seule. then echo "La chaîne \"chaine1\" n'est pas nulle." else echo "La chaîne \"chaine1\" est nulle." fi # Ne pas mettre "$chaine1" entre guillemets donne un mauvais résultat ! exit 0 # Merci aussi à Florian Wisser pour le "heads up".
zmost
#!/bin/bash #Visualiser des fichiers gzip avec 'most' SANSARGS=65 PASTROUVE=66 NONGZIP=67 if [ $# -eq 0 ] # même effet que: if [ -z "$1" ] # $1 peut exister, mais doit être vide: zmost "" arg2 arg3 then echo "Usage: `basename $0` nomfichier" >&2 # Message d'erreur vers stderr. exit $SANSARGS # Renvoie 65 comme code de sortie du script (code d'erreur). fi nomfichier=$1 if [ ! -f "$nomfichier" ] # Mettre $nomfichier entre guillemets permet d'avoir #+ des espaces dans les noms de fichiers. then echo "Fichier $nomfichier introuvable!" >&2 # Message d'erreur vers stderr. exit $PASTROUVE fi if [ ${nomfichier##*.} != "gz" ] # Utilisation de crochets pour la substitution de variables. then echo "Le fichier $1 n'est pas compressé avec gzip!" exit $NONGZIP fi zcat $1 | most # Utilise le visualisateur de fichier 'most' (similaire à 'less'). # Les dernières versions de 'most' peuvent décompresser les fichiers. # Peut se substituer à 'more' ou 'less', si vous le souhaitez. exit $? # Le script renvoie le code d'erreur du tube. # En fait, "exit $?" n'est pas nécessaire, car le script retournera, pour #+ chaque cas, le code de sortie de la dernière commande exécutée.
  • -a
    et logiqueexp1 -a exp2 renvoie vrai si à la fois exp1 et exp2 sont vrais.

  • -o
    ou logique exp1 -o exp2 renvoie vrai si soit exp1 soit exp2 sont vrais.

Elles sont similaires aux opérateurs de comparaison Bash && et ||, utilisés à l'intérieur de double crochets. Les opérateurs -o et -a fonctionnent avec la commande test ou à l'intérieur de simple crochets de test.

Référez-vous à Tests de condition composés en utilisant et || et à Simuler un tableau à deux dimensions, puis son test pour voir des opérateurs de comparaison composée en action.


Tests if/then imbriqués

Les tests utilisant les constructions if/then peuvent être imbriqués. Le résultat est identique à l'utilisation de l'opérateur de comparaison composée && ci-dessus.

Voir Utiliser des tableaux et autres astuces pour gérer quatre mains aléatoires dans un jeu de cartes pour des tests de condition if/then imbriqués.


Tester votre connaissance des tests

Le fichier global xinitrc est utilisé pour lancer le serveur X. Ce fichier contient un certain nombre de tests if/then, comme le montre l'extrait suivant.

Expliquez les constructions de "test" dans l'extrait ci-dessus, puis examinez le fichier entier, /etc/X11/xinit/xinitrc, et analysez les constructions de test if/then. Vous pouvez avoir besoin de vous référer aux discussions sur grep, sed, et les expressions régulières.


2.5. Opérations et thèmes relatifs


Opérateurs

  • affectation de variable
    Initialiser ou changer la valeur d'une variable

  • =
    Opérateur d'affectation à buts multiples, qui fonctionne à la fois pour les affectations arithmétiques et de chaînes de caractères. Attention:Ne confondez pas l'opérateur d'affectation "=" avec l'opérateur de test =. Attention:

  • +
    plus

  • -
    moins

  • *
    multiplication

  • /
    division

  • **
    exponentielle

  • %
    modulo, ou mod (renvoie le reste de la division d'un entier)
  • bash$ echo `expr 5 % 3` 2
  • Cet opérateur trouve son utilité dans, entre autres choses, la génération de nombres compris dans une échelle donnée (voir Générer des nombres aléatoires et Laisser tourner RANDOM plusieurs fois) et pour le formattage de la sortie d'un programme (voir Application complexe des tableaux Exploration d'une étrange série mathématique et collatz: Séries de Collatz). Il peut même être utiliser pour générer des nombres premiers (voir primes: Générer des nombres aléatoires en utilisant l'opérateur modulo). L'opérateur Modulo revient, de manière surprenante, assez souvent dans de nombreuses astuces numériques.
  • Plus grand diviseur commun
    #!/bin/bash # gcd.sh: plus grand diviseur commun # Utilise l'algorithme d'Euclide # Le "plus grand diviseur commun" (pgcd) de deux entiers est l'entier le plus #+ important qui divisera les deux sans reste. # L'algorihtme d'Euclide utilise des divisions successives. # A chaque passe, #+ dividende --- diviseur #+ diviseur --- reste #+ jusqu'à ce que reste 0. #+ pgcd = dividende, à la dernière passe. # # Pour une excellente discussion de l'algorithme d'Euclide, voir le site # de Jim Loy, http://www.jimloy.com/number/euclids.htm. # ------------------------------------------------------ # Vérification des arguments ARGS=2 E_MAUVAISARGS=65 if [ $# -ne "$ARGS" ] then echo "Usage: `basename $0` premier_nombre deuxieme-nombre" exit $E_MAUVAISARGS fi # ------------------------------------------------------ pgcd () { # Affectation arbitraire. dividende=$1 # Il importe peu de savoir lequel est le diviseur=$2 #+ plus grand. # Pourquoi? reste=1 # Si une variable non initialisée est utilisée #+ dans la boucle, #+ cela finit en un message d'erreur lors de #+ la première passe dans la boucle. until [ "$reste" -eq 0 ] do let "reste = $dividende % $diviseur" dividende=$diviseur # Maintenant, répétez avec les deux plus #+ petits nombres. diviseur=$reste done # Algorithme d'Euclide } # Le dernier $dividende est le pgcd. pgcd $1 $2 echo; echo "PGCD de $1 et $2 = $dividende"; echo # Exercice : # -------- # Vérifier les arguments en ligne de commande pour s'assurer que ce soit des #+ entiers et quitter le script avec une erreur appropriée dans le cas contraire. exit 0


  • +=
    "plus-égal" (incrémente une variable par une constante)
  • let "var += 5"
  • renvoie dans var sa propre valeur incrémentée de 5.

  • -=
    "moins-égal" (décrémente une variable par une constante)

  • *=
    "multiplication-égal" (multiplie une variable par une constante)
  • let "var *= 4"
  • renvoie dans var sa propre valeur multipliée par 4.

  • /=
    "division-égal" (divise une variable par une constante)

  • %=
    "modulo-égal" (reste de la division de la variable avec une constante)Les opérateurs arithmétiques sont trouvés souvent dans une expression expr ou let.
  • Utiliser des opérations arithmétiques
    #!/bin/bash # Compter jusqu'à 6 de 5 façons différentes. n=1; echo -n "$n " let "n = $n + 1" # let "n = n + 1" fonctionne aussi. echo -n "$n " : $((n = $n + 1)) # ":" nécessaire parce que sinon Bash essaie d'interpréter #+ "$((n = $n + 1))" comme une commande. echo -n "$n " n=$(($n + 1)) echo -n "$n " : $[ n = $n + 1 ] # ":" nécessaire parce que sinon Bash essaie d'interpréter #+ "$[ n = $n + 1 ]" comme une commande. # Fonctionne même si "n" a été initialisé comme une chaîne de caractères. echo -n "$n " n=$[ $n + 1 ] # Fonctionne même si "n" a été initialisé comme une chaîne de caractères. #* Eviter ce type de construction, car elle est obsolète et non portable. echo -n "$n "; echo # Merci, Stephane Chazelas. exit 0


Remarque : Les variables de type entier dans Bash sont réellement de type entier long signé (32-bit), dans la plage -2147483648 à 2147483647. Une opération qui prend une variable en dehors de ces limites donnera un résultat erroné.

Attention : Bash ne comprend pas l'arithmérique à virgule flottante. Il traite les nombres contenant un point décimal comme des chaînes de caractères. Utiliser bc dans des scripts qui ont besoin de calculs en virgule flottante ou de fonctions de la bibliothèque math.


opérateurs de bits

Les opérateurs de bits font rarement une apparition dans les scripts shell. Leur utilisation principale semble d'être la manipulation et le test de valeurs lues à partir de ports ou de sockets. Le "renversement de bit" est plus intéressant pour les langages compilés, comme le C et le C++, qui fonctionnent assez rapidement pour permettre une utilisation en temps réel.


  • décalage gauche d'un bit (revient à multiplier par 2 pour chaque décalage)

  • =
    "décalage gauche-égal"
  • let "var = 2"
  • renvoie dans var sa propre valeur décalée à gauche de 2 bits (donc multipliée par 4)


  • décalage droit d'un bit (revient à diviser par 2 pour chaque position du décalage)

  • =
    "décalage droit-égal" (inverse de =)


  • et binaire

  • =
    "et-égal binaire"

  • |
    OU binaire

  • |=
    "OU-égal binaire"

  • ~
    négation binaire

  • !
    NON binaire

  • ^
    XOR binaire

  • ^=
    "XOR-égal binaire"


  • et (logique) Remarque: peut aussi, suivant le contexte, être utilisé dans une liste and pour concaténer des commandes.

  • ||
    ou (logique) Remarque:Bash teste l'état de sortie de chaque instruction liée avec un opérateur logique.
  • Tests de condition composés en utilisant et ||
    #!/bin/bash a=24 b=47 if [ "$a" -eq 24 ] && [ "$b" -eq 47 ] then echo "Le test #1 a réussi." else echo "Le test #1 a échoué." fi # ERREUR: si [ "$a" -eq 24 && "$b" -eq 47 ] # essaie d'exécuter ' [ "$a" -eq 24 ' # et échoue à trouver le ']' correspondant. # # si [[ $a -eq 24 && $b -eq 24 ]] fonctionne # (Le "&&" a une signification différente en ligne 17 qu'en ligne 6.) # Merci, Stephane Chazelas. if [ "$a" -eq 98 ] || [ "$b" -eq 47 ] then echo "Le test #2 a réussi." else echo "Le test #2 a échoué." fi # Les options -a et -o apportent une alternative au test de la condition composée. # Merci à Patrick Callahan pour avoir remarqué ceci. if [ "$a" -eq 24 -a "$b" -eq 47 ] then echo "Le test #3 a réussi." else echo "Le test #3 a échoué." fi if [ "$a" -eq 98 -o "$b" -eq 47 ] then echo "Le test #4 a réussi." else echo "Le test #4 a échoué." fi a=rhino b=crocodile if [ "$a" = rhino ] && [ "$b" = crocodile ] then echo "Le test #5 a réussi." else echo "Le test #5 a échoué." fi exit 0
  • Les opérateurs et || trouvent aussi leur utilité dans un contexte arithmétique.
  • bash$ echo $(( 1 && 2 )) $((3 && 0)) $((4 || 0)) $((0 || 0)) 1 0 1 0


  • ,
    opérateur virguleL'opérateur virgule chaîne ensemble deux ou plusieurs opérations arithmétiques. Toutes les opérations sont évaluées (avec des possibles effets indésirables), mais seule la dernière opération est renvoyée.L'opérateur virgule trouve son utilité principalement dans les boucles for. Voir Une boucle for à la C.


Constantes numériques

Un script shell interprète un nombre comme décimal (base 10), sauf si ce nombre a un certain préfixe ou une notation. Un nombre précédé par un 0 est octal (base 8). Un nombre précédé par 0x est hexadécimal (base 16). Un nombre comprenant un # est évalué comme BASE#NOMBRE (avec les restrictions sur l'étendue et la notation).

Représentation des constantes numériques
#!/bin/bash # numbers.sh: Représentation des nombre en différentes bases. # Décimal: par défaut let "dec = 32" echo "nombre décimal = $dec" # 32 # Rien qui ne sort de l'ordinaire ici. # Octal: nombres précédés par '0' (zero) let "oct = 032" echo "nombre octal = $oct" # 26 # Exprime le résultat en décimal. # ------- -- -------- -- ------- # Hexadecimal: nombres précédés par '0x' ou '0X' let "hex = 0x32" echo "nombre hexadécimal = $hex" # 50 # Exprime le résultat en décimal. # Autres bases: BASE#NOMBRE # BASE entre 2 et 64. # NUMBER doit utiliser les symboles compris dans l'intervalle BASE, voir ci-dessous. let "bin = 2#111100111001101" echo "nombre binaire = $bin" # 31181 let "b32 = 32#77" echo "nombre en base-32 = $b32" # 231 let "b64 = 64#@_" echo "nombre en base-64 = $b64" # 4094 # # Cette notation fonctionne seulement pour un intervalle limité (2 - 64) # 10 chiffres + 26 caractères minuscules + 26 caractères majuscules + @ + _ echo echo $((36#zz)) $((2#10101010)) $((16#AF16)) $((53#1aA)) # 1295 170 44822 3375 # Note importante: # ---------------- # Utiliser un chiffre en dehors de l'échelle de la notation spécifiée #+ donnera un message d'erreur. let "bad_oct = 081" # numbers.sh: let: oct = 081: valeur trop élevée pour la base (l'erreur est "081") # Les nombres octal utilisent seulement des chiffres dans l'intervalle 0 - 7. exit 0 # Merci, Rich Bartell et Stephane Chazelas, pour cette clarification.

3. Après les bases


3.0. Les variables revisitées

Utilisées proprement, les variables peuvent ajouter puissance et flexibilité à vos scripts. Ceci nécessite l'apprentissage de leurs subtilités et de leurs nuances.


Variables internes

  • Variablesintégrées
    variables affectant le comportement des scripts bash

  • $BASH
    le chemin vers le binaire Bash lui-même
  • bash$ echo $BASH /bin/bash


  • $BASH_ENV
    une variable d'environnement pointant vers le fichier de démarrage de Bash pour être lue lorsqu'un script est invoqué

  • $BASH_VERSINFO[n]
    un tableau array à six éléments contenant des informations sur la version installée de Bash. Ceci est similaire à $BASH_VERSION, ci-dessous, mais un peu plus détaillé.

  • $BASH_VERSION
    la version du Bash installé sur le système
  • bash$ echo $BASH_VERSION 2.04.12(1)-release
  • tcsh% echo $BASH_VERSION BASH_VERSION: Undefined variable.
  • Vérifier $BASH_VERSION est une bonne méthode pour déterminer quel shell est en cours d'exécution. $SHELL ne donne pas nécessairement la bonne réponse.

  • $DIRSTACK
    la valeur du dessus de la pile de répertoires (affectée par pushd et popd)Cette variable intégrée correspond à la commande dirs, néanmoins dirs affiche le contenu entier de la pile de répertoires.

  • $EDITOR
    l'éditeur par défaut invoqué par un script, habituellement vi ou emacs.

  • $EUID
    Numéro d'identifiant "effectif" de l'utilisateurNuméro d'identification quelque soit l'identité que l'utilisateur actuel assume, peut-être suite à un su. Attention:$EUID n'est pas nécessairement le même que $UID.

  • $FUNCNAME
    nom de la fonction en cours

  • $GLOBIGNORE
    Une liste de modèles de noms de fichiers à exclure de la correspondance lors d'un remplacement.

  • $GROUPS
    groupes auxquels appartient l'utilisateurC'est une liste (tableau) des numéros d'identifiant de groupes pour l'utilisateur actuel, comme enregistré dans /etc/passwd.
  • root# echo $GROUPS 0 root# echo ${GROUPS[1]} 1 root# echo ${GROUPS[5]} 6


  • $HOME
    répertoire personnel de l'utilisateur, habituellement /home/utilisateur (voir Utiliser la substitution et les messages d'erreur)

  • $HOSTNAME
    La commande hostname définit le nom du système au démarrage en utilisant un script de démarrage. Néanmoins, la fonction gethostname() initialise la variable interne Bash $HOSTNAME. Voir aussi Utiliser la substitution et les messages d'erreur.

  • $HOSTTYPE
    type de l'hôteComme $MACHTYPE, identifie le matériel du système.bash$ echo $HOSTTYPE i686

  • $IFS
    séparateur du champ de saisieCeci vaut par défaut un espace blanc (espace, tabulation et retour de chariot), mais peut être changé, par exemple pour analyser un fichier de données séparées par des virgules. Notez que $* utilise le premier caractère contenu dans $IFS. Voir Afficher des variables bizarres.
  • bash$ echo $IFS | cat -vte $ bash$ bash -c 'set w x y z; IFS=":-;"; echo "$*"' w:x:y:z
  • Attention:$IFS ne gère pas les espaces blancs de la même façon que les autres caractères. #!/bin/bash # $IFS traite les espaces blancs différemment des autres caractères. sortie_des_arguments_un_par_ligne() { for arg do echo "[$arg]" done } echo; echo "IFS=\" \"" echo "-------" IFS=" " var=" a b c " sortie_des_arguments_un_par_ligne $var # sortie_des_arguments_un_par_ligne `echo " a b c "` # # [a] # [b] # [c] echo; echo "IFS=:" echo "-----" IFS=: var=":a::b:c:::" # Identique à ci-dessus, mais substitue ":" à " ". sortie_des_arguments_un_par_ligne $var # # [] # [a] # [] # [b] # [c] # [] # [] # [] # La même chose arrive avec le séparateur de champs "FS" dans awk. # Merci, Stephane Chazelas. echo exit 0(Merci, S. C., pour cette clarification et ces exemples.)

  • $IGNOREEOF
    ignore EOF: combien de fins de fichier (control-D) le shell va ignorer avant de déconnecter.

  • $LC_COLLATE
    Souvent intégré dans les fichiers .bashrc ou /etc/profile, cette variable contrôle l'ordre d'examen dans l'expansion des noms de fichiers et les correspondances de modèles. Si elle est mal gérée, LC_COLLATE peut apporter des résultats inattendus dans le remplacement de noms de fichiers. Remarque:A partir de la version 2.05 de Bash, le remplacement de noms de fichiers ne tient plus compte des lettres en minuscules et en majuscules dans une suite de caractères entre crochets. Par exemple, ls [A-M]* correspondrait à la fois à Fichier1.txt et à fichier1.txt. Pour annuler le comportement personnalisé de la correspondance par crochets, initialisez LC_COLLATE à C par un
  • export LC_COLLATE=C
  • dans /etc/profile et/ou ~/.bashrc.

  • $LC_CTYPE
    Cette variable interne contrôle l'interprétation des caractères pour le remplacement et la correspondance de modèles.

  • $LINENO
    Cette variable est le numéro de ligne du script shell dans lequel cette variable apparaît. Elle n'a une signification que dans le script où elle apparait et est surtout utilisée pour les phases de déboguage.

  • $MACHTYPE
    type de machineIdentifie le matériel du système.bash$ echo $MACHTYPE i686

  • $OLDPWD
    ancien répertoire courant ( "OLD-print-working-directory" , ancien répertoire où vous étiez)

  • $OSTYPE
    type de système d'exploitationbash$ echo $OSTYPE linux

  • $PATH
    chemin vers les binaires, habituellement /usr/bin/, /usr/X11R6/bin/, /usr/local/bin, etc.Lorsqu'une commande est donnée, le shell recherche automatiquement les exécutables dans les répertoires listés dans le chemin. Le chemin est stocké dans la variable d'environnement, $PATH, une liste des répertoires, séparés par le symbôle ":". Normalement, le système stocke la définition de $PATH dans /etc/profile et/ou ~/.bashrc (voir ).
  • bash$ echo $PATH /bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/usr/sbin
  • PATH=${PATH}:/opt/bin
  • ajoute le répertoire /opt/bin au chemin actuel. Dans un script, il peut être avantageux d'ajouter temporairement un répertoire au chemin de cette façon. Lorsque le script se termine, ceci restaure le $PATH original (un processus fils, tel qu'un script, ne peut pas changer l'environnement du processus père, le shell). Remarque:Le "répertoire" courant, ./, est habituellement omis de $PATH pour des raisons de sécurité.

  • $PIPESTATUS
    Code de sortie de la dernière commande exécutée via un tube. De façon étonnante, ceci ne donne pas le même résultat que le code de sortie de la dernière commande exécutée.
  • bash$ echo $PIPESTATUS 0 bash$ ls -al | bogus_command bash: bogus_command: command not found bash$ echo $PIPESTATUS 141 bash$ ls -al | bogus_command bash: bogus_command: command not found bash$ echo $? 127
  • Attention: La variable $PIPESTATUS peut contenir une valeur 0 erronée dans un shell de connexion. Attention:
  • tcsh% bash bash$ who | grep nobody | sort bash$ echo ${PIPESTATUS[*]} 0
  • Attention: Les lignes ci-dessus contenues dans un script produiraient le résultat attendu, 0 1 0. Attention: Merci, Wayne Pollock pour avoir partagé ceci en apportant l'exemple ci-dessus.

  • $PPID
    Le $PPID d'un processus est l'identifiant du processus (pid) père. (18)Comparez ceci avec la commande pidof.

  • $PS1
    Ceci est l'invite principale, vue sur la ligne de commande.

  • $PS2
    La deuxième invite, vue lorsqu'une saisie supplémentaire est attendue. Elle s'affiche comme "" .

  • $PS3
    La troisième invite, affichée lors d'une boucle select (voir Créer des menus en utilisant select).

  • $PS4
    La quatrième invite, affichée au début de chaque ligne d'affichage lorsqu'un script a été appelé avec l'option -x. Elle affiche un "+" .

  • $PWD
    répertoire courant (répertoire où vous êtes actuellement)Ceci est analogue à la commande intégrée pwd.

  • $REPLY
    La valeur par défaut lorsqu'une valeur est donnée par read. Aussi applicable au menu select, mais apporte seulement le numéro de l'élément de la variable choisie, et non pas la valeur de la variable elle-même.

  • $SECONDS
    Le nombre de secondes pendant lequel le script s'exécutait.

  • $SHELLOPTS
    la liste des options activées du shell, une variable en lecture seule
  • bash$ echo $SHELLOPTS braceexpand:hashall:histexpand:monitor:history:interactive-comments:emacs


  • $SHLVL
    Niveau du shell, comment Bash est imbriqué. Si, à la ligne de commande, $SHLVL vaut 1, alors, dans un script, il sera incrémenté et prendra la valeur 2.

  • $TMOUT
    Si la variable d'environnement $TMOUT est initialisée à une valeur différente de zéro appelée time, alors l'invite shell dépassera son délai au bout de time secondes. Ceci causera une déconnexion. Remarque:Malheureusement, ceci fonctionne seulement lors de l'attente d'une saisie sur une invite de la console ou dans un xterm. Bien qu'il serait sympathique de spéculer sur l'utilité de cette variable interne pour des saisies avec expiration de délai, par exemple en combinaison avec read, $TMOUT ne fonctionnera pas dans ce contexte et est virtuellement inutile pour l'écriture de scripts shell. (Une information semble indiquer qu'un read avec délai fontionne sur ksh.)Implémenter une saisie avec délai dans un script est certainement possible, mais nécessiterait un code complexe. Une méthode est de configurer une boucle avec délai pour signaler au script lorsque le délai se termine. Ceci nécessite aussi une routine de gestion du signal pour récupérer (voir Récupérer la sortie) l'interruption générée par la boucle de délai (ouf!).
  • Saisie avec délai
    #!/bin/bash # timed-input.sh # TMOUT=3 inutile dans un script LIMITETEMPS=3 # Trois secondes dans cette instance, peut être configuré avec #+ une valeur différente. AfficheReponse() { if [ "$reponse" = TIMEOUT ] then echo $reponse else # ne pas mixer les deux interfaces. echo "Votre légume favori est le $reponse" kill $! # Kill n'est plus nécessaire pour la fonction TimerOn lancé en #+ tâche de fond. # $! est le PID du dernier job lancé en tâche de fond. fi } TimerOn() { sleep $LIMITETEMPS && kill -s 14 $$ & # Attend 3 secondes, puis envoie sigalarm au script. } VecteurInt14() { reponse="TIMEOUT" AfficheReponse exit 14 } trap VecteurInt14 14 # Interruption de temps (14) détournée pour notre but. echo "Quel est votre légume favori?" TimerOn read reponse AfficheReponse # C'est une implémentation détournée de l'entrée de temps, #+ néanmoins l'option "-t" de "read" simplifie cette tâche. # Voir "t-out.sh", ci-dessous. # Si vous avez besoin de quelque chose de réellement élégant... #+ pensez à écrire l'application en C ou C++, #+ en utilisant les fonctions de la bibliothèque appropriée, telles que #+ 'alarm' et 'setitimer'. exit 0
  • Une autre méthode est d'utiliser stty.
  • Encore une fois, saisie avec délai
    #!/bin/bash # timeout.sh # Ecrit par Stephane Chazelas, # et modifié par l'auteur de ce document. INTERVALLE=5 # intervalle timeout lecture_timedout() { timeout=$1 nomvariable=$2 ancienne_configuration_tty=`stty -g` stty -icanon min 0 time ${timeout}0 eval read $nomvariable # ou simplement read $nomvariable stty "$ancienne_configuration_tty" # Voir la page man de "stty". } echo; echo -n "Quel est votre nom? Vite!" lecture_timedout $INTERVALLE votre_nom # Ceci pourrait ne pas fonctionner sur tous les types de terminaux. # Le temps imparti dépend du terminal (il est souvent de 25,5 secondes). echo if [ ! -z "$votre_nom" ] # Si le nom est entré avant que le temps ne se soit #+ écoulé... then echo "Votre nom est $votre_nom." else echo "Temps écoulé." fi echo # Le comportement de ce script diffère un peu de "timed-input.sh". # A chaque appui sur une touche, le compteur est réinitialisé. exit 0
  • Peut-être que la méthode la plus simple est d'utiliser l'option -t de read.
  • read avec délai
    #!/bin/bash # t-out.sh (suggestion de "syngin seven") LIMITETEMPS=4 # 4 secondes read -t $LIMITETEMPS variable <&1 echo if [ -z "$variable" ] then echo "Temps écoulé, la variable n'est toujours pas initialisée." else echo "variable = $variable" fi exit 0


  • $UID
    numéro de l'identifiant utilisateurnuméro d'identification de l'utilisateur actuel, comme enregistré dans /etc/passwdC'est l'identifiant réel de l'utilisateur actuel, même s'il a temporairement endossé une autre identité avec su. $UID est une variable en lecture seule, non sujet au changement à partir de la ligne de commande ou à l'intérieur d'un script, et est la contre partie de l'intégré id.
  • Suis-je root?
    #!/bin/bash # am-i-root.sh: Suis-je root ou non? ROOT_UID=0 # Root a l'identifiant $UID 0. if [ "$UID" -eq "$ROOT_UID" ] # Le vrai "root" peut-il se lever, s'il-vous-plaît? then echo "Vous êtes root." else echo "Vous êtes simplement un utilisateur ordinaire (mais maman vous aime tout autant.)." fi exit 0 # ============================================================= # # Le code ci-dessous ne s'exécutera pas, parce que le script s'est déjà arrêté. # Une autre méthode d'arriver à la même fin: NOM_UTILISATEURROOT=root nomutilisateur=`id -nu` # Ou... nomutilisateur=`whoami` if [ "$nomutilisateur" = "$NOM_UTILISATEURROOT" ] then echo "Vous êtes root." else echo "Vous êtes juste un gars régulier." fi
  • Voir aussi cleanup: Une version améliorée et généralisée du script précédent.. Remarque:Les variables $ENV, $LOGNAME, $MAIL, $TERM, $USER et $USERNAME ne sont pas des variables intégrés à Bash. Elles sont néanmois souvent initialisées comme variables d'environnement dans un des fichiers de démarrage de Bash. $SHELL, le nom du shell de connexion de l'utilisateur, peut être configuré à partir de /etc/passwd ou dans un script d' "initialisation" , et ce n'est pas une variable intégrée à Bash. Remarque:
  • tcsh% echo $LOGNAME bozo tcsh% echo $SHELL /bin/tcsh tcsh% echo $TERM rxvt bash$ echo $LOGNAME bozo bash$ echo $SHELL /bin/tcsh bash$ echo $TERM rxvt


  • $0, $1, $2, etc.
    paramètres de positions, passés à partir de la ligne de commande à un script, passés à une fonction, ou initialisés (set) à une variable (voir Paramètres positionnels et Utiliser set avec les paramètres de position)

  • $#
    nombre d'arguments sur la ligne de commande (19) ou de paramètres de position (voir Un script d'invocation légèrement plus complexe)

  • $*
    Tous les paramètres de position, vus comme un seul mot

  • $@
    Identique à $*, mais chaque paramètre est une chaîne entre guillemets, c'est-à-dire que les paramètres sont passés de manière intacte, sans interprétation ou expansion. Ceci signifie, entre autres choses, que chaque paramètre dans la liste d'arguments est vu comme un mot séparé.
  • arglist: Affichage des arguments avec $* et $@
    #!/bin/bash # Appelez ce script avec plusieurs arguments, tels que "un deux trois". E_BADARGS=65 if [ ! -n "$1" ] then echo "Usage: `basename $0` argument1 argument2 etc." exit $E_BADARGS fi echo index=1 echo "Liste des arguments avec \"\$*\":" for arg in "$*" # Ne fonctionne pas correctement si "$*" n'est pas entre guillemets. do echo "Arg #$index = $arg" let "index+=1" done # $* voit tous les arguments comme un mot entier. echo "Liste entière des arguments vue comme un seul mot." echo index=1 echo "Liste des arguments avec \"\$@\":" for arg in "$@" do echo "Arg #$index = $arg" let "index+=1" done # $@ voit les arguments comme des mots séparés. echo "Liste des arguments vue comme des mots séparés." echo exit 0
  • Suite à un shift, $@ contient le reste des paramètres de la ligne de commande, sans le précédent $1, qui a été perdu. Le paramètre spécial $@ trouve son utilité comme outil pour filtrer l'entrée des scripts shell. La construction cat "$@" accepte l'entrée dans un script soit à partir de stdin soit à partir de fichiers donnés en paramètre du script. Voir rot13: rot13, cryptage ultra-faible. et Générer des puzzles Crypto-Citations. Attention:Les paramètres $* et $@ affichent quelque fois un comportement inconsistent et bizarre, suivant la configuration de $IFS.
  • Comportement de $* et $@ inconsistent
    #!/bin/bash # Comportement non prédictible des variables internes Bash "$*" et "$@", #+ suivant qu'elles soient ou non entre guillemets. # Gestion inconsistante de la séparation de mots et des retours chariot. set -- "Premier un" "second" "troisième:un" "" "Cinquième: :un" # Initialise les arguments du script, $1, $2, etc. echo echo 'IFS inchangée, utilisant "$*"' c=0 for i in "$*" # entre guillemets do echo "$((c+=1)): [$i]" # Cette ligne reste identique à chaque instance. # Arguments de echo. done echo --- echo 'IFS inchangée, utilisant $*' c=0 for i in $* # sans guillemets do echo "$((c+=1)): [$i]" done echo --- echo 'IFS inchangée, utilisant "$@"' c=0 for i in "$@" do echo "$((c+=1)): [$i]" done echo --- echo 'IFS inchangée, utilisant $@' c=0 for i in $@ do echo "$((c+=1)): [$i]" done echo --- IFS=: echo 'IFS=":", utilisant "$*"' c=0 for i in "$*" do echo "$((c+=1)): [$i]" done echo --- echo 'IFS=":", utilisant $*' c=0 for i in $* do echo "$((c+=1)): [$i]" done echo --- var=$* echo 'IFS=":", utilisant "$var" (var=$*)' c=0 for i in "$var" do echo "$((c+=1)): [$i]" done echo --- echo 'IFS=":", utilisant $var (var=$*)' c=0 for i in $var do echo "$((c+=1)): [$i]" done echo --- var="$*" echo 'IFS=":", utilisant $var (var="$*")' c=0 for i in $var do echo "$((c+=1)): [$i]" done echo --- echo 'IFS=":", utilisant "$var" (var="$*")' c=0 for i in "$var" do echo "$((c+=1)): [$i]" done echo --- echo 'IFS=":", utilisant "$@"' c=0 for i in "$@" do echo "$((c+=1)): [$i]" done echo --- echo 'IFS=":", utilisant $@' c=0 for i in $@ do echo "$((c+=1)): [$i]" done echo --- var=$@ echo 'IFS=":", utilisant $var (var=$@)' c=0 for i in $var do echo "$((c+=1)): [$i]" done echo --- echo 'IFS=":", utilisant "$var" (var=$@)' c=0 for i in "$var" do echo "$((c+=1)): [$i]" done echo --- var="$@" echo 'IFS=":", utilisant "$var" (var="$@")' c=0 for i in "$var" do echo "$((c+=1)): [$i]" done echo --- echo 'IFS=":", utilisant $var (var="$@")' c=0 for i in $var do echo "$((c+=1)): [$i]" done echo # Essayez ce script avec ksh ou zsh -y. exit 0 # Ce script exemple par Stephane Chazelas, # et légèrement modifié par l'auteur de ce document.
  • Remarque:Les paramètres $@ et $* diffèrent seulement lorsqu'ils sont entre des guillemets doubles.
  • $* et $@ lorsque $IFS est vide
    #!/bin/bash # Si $IFS est initialisé, mais vide, # alors "$*" et "$@" n'affiche pas les paramètres de position comme on pourrait # s'y attendre. mecho () # Affiche les paramètres de position. { echo "$1,$2,$3"; } IFS="" # Initialisé, mais vide. set a b c # Paramètres de position. mecho "$*" # abc,, mecho $* # a,b,c mecho $@ # a,b,c mecho "$@" # a,b,c # Le comportement de $* et $@ quand $IFS est vide dépend de la version de # Bash ou sh. # Personne ne peux donc conseiller d'utiliser cette "fonctionnalité" dans un # script. # Merci, S.C. exit 0



Manipuler les chaînes de caractères

Bash supporte un nombre surprenant d'opérations de manipulation de chaînes de caractères. Malheureusement, ces outils manquent d'unité. Certains sont un sous-ensemble de la substitution de paramètre et les autres font partie des fonctionnalités de la commande UNIX expr. Ceci produite une syntaxe de commande non unifié et des fonctionnalités qui se recoupent, sans parler de la confusion engendrée.

  • ${#chaine}


  • expr length $chaine


  • expr "$chaine" : '.*'


Insérer une ligne blanche entre les paragraphes d'un fichier texte
#!/bin/bash # paragraph-space.sh # Insère une ligne blanche entre les paragraphes d'un fichier texte. # Usage: $0 NOMFICHIER LONGUEUR_MINI=45 # Il peut être nécessaire de changer cette valeur. # Suppose que les lignes plus petites que $LONGUEUR_MINI caractères #+ terminent un paragraphe. while read ligne # Pour toutes les lignes du fichier... do echo "$ligne" # Afficher la ligne. longueur=${#ligne} if [ "$longueur" -lt "$LONGUEUR_MINI" ] then echo # Ajoute une ligne blanche après chaque petite ligne. fi done exit 0
  • expr match "$chaine" '$souschaine'
    $souschaine est une expression régulière.

  • expr "$chaine" : '$souschaine'
    $souschaine est une expression régulière.

  • expr index $chaine $souschaine
    Position numérique dans $chaine du premier caractère dans $souschaine qui correspond.Ceci est l'équivalent le plus proche strchr() en C.

  • ${chaine:position}
    Extrait une sous-chaîne de $chaine à la position $position.Si le paramètre $chaine est "* " ou "@ " , alors cela extrait les paramètres de position, (20) commençant à $position.

  • ${chaine:position:longueur}
    Extrait $longueur caractères d'une sous-chaîne de $chaine à la position $position.Si le paramètre $chaine est "* " ou "@ " , alors ceci extrait un maximum de $longueur du paramètre de position, en commençant à $position.

  • expr souchaine $chaine $position $longueur
    Extrait $longueur caractères à partir de $chaine en commençant à $position.

  • expr match "$chaine" '\($souschaine\)'
    Extrait $souschaine à partir du début de $chaine, et où $souschaine est une expression régulière.

  • expr "$chaine" : '\($souschaine\)'
    Extrait $souschaine à partir du début de $chaine, et où $souschaine est une expression régulière.

  • expr match "$chaine" '.*\($souschaine\)'
    Extrait $souschaine à la fin de $chaine, et où $souschaine est une expression régulière.

  • expr "$chaine" : '.*\($souschaine\)'
    Extrait $souschaine à la fin de $chaine, et où $souschaine est une expression régulière.

  • ${chaine#souschaine}
    Supprime la correspondance la plus petite de $souschaine à partir du début de $chaine.

  • ${chaine##souschaine}
    Supprime la correspondance la plus grande de $souschaine à partir du début de $chaine.

  • ${chaine%souschaine}
    Supprime la plus petite correspondance de $souschaine à partir de la fin de $chaine.

  • ${chaine%%souschaine}
    Supprime la plus grande correspondance de $souschaine à partir de la fin de $chaine.
  • Convertir des formats de fichiers graphiques avec une modification du nom du fichier
    #!/bin/bash # cvt.sh: # Convertit les fichiers image MacPaint contenus dans un répertoire dans un #+ format "pbm". # Utilise le binaire "macptopbm" provenant du paquetage "netpbm", #+ qui est maintenu par Brian Henderson (bryanh@giraffe-data.com). # Netpbm est un standard sur la plupart des distributions Linux. OPERATION=macptopbm SUFFIXE=pbm # Suffixe pour les nouveaux noms de fichiers. if [ -n "$1" ] then repertoire=$1 # Si le nom du répertoire donné en argument au script... else repertoire=$PWD # Sinon, utilise le répertoire courant. fi # Suppose que tous les fichiers du répertoire cible sont des fichiers image # + MacPaint avec un suffixe ".mac". for fichier in $repertoire/* # Filename globbing. do nomfichier=${fichier%.*c} # Supprime le suffixe ".mac" du nom du fichier #+ ('.*c' correspond à tout ce qui se trouve #+ entre '.' et 'c', inclus). $OPERATION $fichier > $nomfichier.$SUFFIXE # Redirige la conversion vers le nouveau nom du fichier. rm -f $fichier # Supprime le fichier original après sa convertion. echo "$nomfichier.$SUFFIXE" # Trace ce qui se passe sur stdout. done exit 0 # Exercice # -------- # A ce stade, ce script convertit *tous* les fichiers du répertoire courant. # Modifiez le pour qu'il renome *seulement* les fichiers dont l'extension est ".mac".


  • ${chaine/souschaine/remplacement}
    Remplace la première correspondance de $souschaine par $remplacement.

  • ${chaine//souschaine/remplacement}
    Remplace toutes les correspondances de $souschaine avec $remplacement.

  • ${chaine/#souschaine/remplacement}
    Si $souschaine correspond au début de $chaine, substitue $remplacement à $souschaine.

  • ${chaine/%souchaine/remplacement}
    Si $souschaine correspond à la fin de $chaine, substitue $remplacement à $souschaine.


Manipuler des chaînes de caractères avec awk

Un script Bash peut utiliser des fonctionnalités de manipulation de chaînes de caractères de awk comme alternative à ses propres fonctions intégrées.

Autres moyens d'extraire des sous-chaînes
#!/bin/bash # substring-extraction.sh Chaine=23skidoo1 # 012345678 Bash # 123456789 awk # Notez les différents systèmes d'indexage de chaînes: # Bash compte le premier caractère d'une chaîne avec '0'. # Awk compte le premier caractère d'une chaîne avec '1'. echo ${Chaine:2:4} # position 3 (0-1-2), longueur de quatre caractères # skid # L'équivalent awk de ${string:pos:length} est substr(string,pos,length). echo | awk ' { print substr("'"${Chaine}"'",3,4) # skid } ' # Envoyer un "echo" vide à awk lui donne une entrée factice, et donc permet d'éviter #+ de fournir un nom de fichier. exit 0

Discussion plus avancée

Pour plus d'informations sur la manipulation des chaînes de caractères dans les scripts, référez-vous à la et à la section consacrée à la commande expr. Pour des exemples de scripts, jetez un oeil sur : Utiliser expr Longueur d'une variable Correspondance de modèle dans la substitution de paramètres Renommer des extensions de fichiers: Modèles correspondant au préfixe ou au suffixe d'une chaîne de caractères


Substitution des paramètres

  • ${parametre}
    Tel que $parametre, c'est-à-dire la valeur de la variable parametre. Dans certains contextes, seule la forme la moins ambiguë de ${parametre} fonctionne.Peut être utilisé pour concaténer des variables avec des suites de caractères (strings).

  • ${parametre-par-defaut}
    Si parametre n'est pas initialisé, utilisez defaut. Remarque:${parametre-defaut} et ${parametre:-defaut} sont pratiquement équivalents. Le caractère : supplémentaire fait une différence seulement lorsque parametre a été déclaré, mais est nul. La construction du paramètre par défaut a pour principale utilisation de fournir les arguments "manquants" de la ligne de commande des scripts.Voir aussi Sauvegarde de tous les fichiers modifiés dans les dernières 24 heures, Créer un fichier de swap en utilisant /dev/zero et collatz: Séries de Collatz.Comparez cette méthode avec l'utilisation d'une liste ET pour fournir un argument par défaut à la ligne de commande.

  • ${parametre=defaut}
    Si le paramètre n'est pas initialisé, alors initialisation à defaut.Les deux formes sont pratiquement équivalentes. Le caractère : fait une différence seulement lorsque $parametre a été déclaré et est nul, (21) comme ci-dessus.

  • ${parametre+valeur_alt}
    Si le paramètre est déclaré, utilisez
  • valeur_alt
  • , sinon utilisez la chaîne de caractères nulle.Les deux formes sont pratiquement équivalentes. Le caractère : fait la différence seulement lorsque parametre a été déclaré nul, voir plus bas.

  • ${parametre?msg_err}
    Si le paramètre est affecté, utilisez-le, sinon affichez msg_err.Les deux formes sont pratiquement équivalentes. Le caractère : fait la différence seulement lorsque parametre a été déclaré nul, comme ci-dessus.

Utiliser la substitution et les messages d'erreur
#!/bin/bash # Vérifier certaines des variables d'environnements du système. # Si, par exemple, $USER, le nom de la personne sur la console n'est pas #+ initialisé, la machine ne vous reconnaitra pas. : ${HOSTNAME?} ${USER?} ${HOME?} ${MAIL?} echo echo "Le nom de la machine est $HOSTNAME." echo "Vous êtes $USER." echo "Votre répertoire personnel est $HOME." echo "Votre courrier est situé dans $MAIL." echo echo "Si vous lisez ce message, les variables d'environnement " echo "critiques ont été initialisées." echo echo # ------------------------------------------------------ # La construction ${variablename?} peut aussi vérifier les #+ variables initialisées dans un script. CetteVariable=Valeur-de-CetteVariable # Notez que, du coup, les variables chaîne de caractères pourraient être #+ initialisées avec les caractères contenus dans leurs noms. : ${CetteVariable?} echo "La valeur de CetteVariable est $CetteVariable". echo echo : ${ZZXy23AB?"ZZXy23AB n'a pas été initialisée."} # Si ZZXy23AB n'a pas été initialisée, alors le script se termine avec un #+ message d'erreur. # Vous pouvez spécifier le message d'erreur. # : ${ZZXy23AB?"ZZXy23AB n'a pas été initialisée."} # Même résultat avec: variable_stupide=${ZZXy23AB?} # variable_stupide=${ZZXy23AB?"ZXy23AB n'a pas été initialisée."} # # echo ${ZZXy23AB?} >/dev/null echo "Vous ne verrez pas ce message parce que le script s'est terminée avant." ICI=0 exit $ICI # Ne sortira *pas* ici.
Substitution de paramètres et messages d'usage
#!/bin/bash # usage-message.sh : ${1?"Usage: $0 ARGUMENT"} # Le script sort ici si le paramètre en ligne de commande est absent, #+ avec le message d'erreur suivant. # usage-message.sh: 1: Usage: usage-message.sh ARGUMENT echo "Ces deux lignes ne s'affichent que si le paramètre en ligne de commande est donné." echo "paramètre en ligne de commande = \"$1\"" exit 0 # Sortira ici seulement si le paramètre en ligne de commande est présent. # Vérifiez le code de sortie, à la fois avec et sans le paramètre en ligne de #+ commandes. # Si le paramètre en ligne de commande est présent, alors "$?" vaut 0. # Sinon, "$?" vaut 1.

Substitution de paramètres et/ou expansion

Les expressions suivantes sont le complément des opérations sur les suites de caractères comme match dans expr (voir Utiliser expr). Ces derniers sont utilisés principalement pour analyser les chemins de fichiers.

  • ${#var}
  • Longueur de la suite de caractères
  • (ou nombre de caractères dans $var). Pour un tableau, ${#tableau} est la longueur du premier élément dans le tableau. Remarque: Exceptions: ${#*} et ${#@} donnent le nombre de paramètres de position. Pour un tableau, ${#tableau[*]} et ${#tableau[@]} donnent le nombre d'éléments dans le tableau.
  • Longueur d'une variable
    #!/bin/bash # length.sh E_SANS_ARGS=65 if [ $# -eq 0 ] # Doit avoir des arguments en ligne de commande. then echo "Appeler ce script avec un ou plusieurs argument(s) en ligne de commande." exit $E_SANS_ARGS fi var01=abcdEFGH28ij echo "var01 = ${var01}" echo "Longueur de var01 = ${#var01}" echo "Nombre d'arguments en ligne de commande passés au script = ${#@}" echo "Nombre d'arguments en ligne de commande passés au script = ${#*}" exit 0


  • ${var#Modele}
    Supprimez à partir de $var la plus courte/longue partie de $Modele qui correspond au début de $var. Un exemple d'usage à partir de days-between: Calculer le nombre de jours entre deux dates: Un autre exemple d'usage:

  • ${var%Modele}
    Supprimez à partir de $var la partie la plus courte/longue de $Modele qui correspond à la fin de $var.

La version 2 de Bash ajoute des options supplémentaires.

Correspondance de modèle dans la substitution de paramètres
#!/bin/bash # Reconnaissance de modèles en utilisant les opérateurs de substituion # ## % %% var1=abcd12345abc6789 modele1=a*c # * (wild card) matches everything between a - c. echo echo "var1 = $var1" # abcd12345abc6789 echo "var1 = ${var1}" # abcd12345abc6789 (autre forme) echo "Nombre de caractères dans ${var1} = ${#var1}" echo "modele1 = $modele1" # a*c (tout entre 'a' et 'c') echo echo '${var1#$modele1} =' "${var1#$modele1}" # d12345abc6789 # Correspondance la plus petite, supprime les trois permiers caractères abcd12345abc6789 # ^^^^^ |-| echo '${var1##$modele1} =' "${var1##$modele1}" # 6789 # Correspondance la plus grande possible, supprime les 12 premiers caractères abcd12345abc6789 # ^^^^^ |----------| echo; echo modele2=b*9 # tout entre 'b' et '9' echo "var1 = $var1" # Toujours abcd12345abc6789 echo "modele2 = $modele2" echo echo '${var1%modele2} =' "${var1%$modele2}" # abcd12345a # Correspondance la plus petite, supprime les six derniers caractères abcd12345abc6789 # ^^^^ |----| echo '${var1%%modele2} =' "${var1%%$modele2}" # a # Correspondance la plus grande, supprime les 12 derniers caractères abcd12345abc6789 # ^^^^ |-------------| # Souvenez-vous, # et ## fonctionnent à partir de la gauche de la fin de la #+ chaîne # % et %% fonctionnent à partir de la droite. echo exit 0
Renommer des extensions de fichiers:
#!/bin/bash # rfe # --- # Renommer les extensions de fichier (Renaming File Extensions). # # rfe ancienne_extension nouvelle_extension # # Exemple: # Pour renommer tous les fichiers *.gif d'un répertoire en *.jpg, # rfe gif jpg ARGS=2 E_MAUVAISARGS=65 if [ $# -ne "$ARGS" ] then echo "Usage: `basename $0` ancien_suffixe nouveau_suffixe" exit $E_MAUVAISARGS fi for fichier in *.$1 # Traverse la liste des fichiers dont le nom termine avec le premier argument. do mv $fichier ${fichier%$1}$2 # Supprime la partie du fichier contenant le premier argument #+ puis ajoute le deuxième argument. done exit 0

  • Ces constructions proviennent de ksh.

  • ${var:pos}
    La variable var étendue, commençant à la position pos.

  • ${var:pos:len}
    Augmentation d'un maximum de len caractères de la variable var, à partir de la position pos. Voir password: Générer des mots de passe aléatoires de 8 caractères pour un exemple d'utilisation particulièrement intéressante de cet opérateur.

  • ${var/Modele/Remplacement}
    Première occurrence de Modele, à l'intérieur de var remplacé par Remplacement.Si Remplacement est omis, alors la première occurrence de Modele n'est remplacé par rien, c'est-à-dire qu'il est supprimé.

  • ${var//Modele/Remplacement}
  • Remplacement globalToutes les occurrences de Modele, à l'intérieur de var remplacées par Remplacement.
  • Comme ci-dessus, si Remplacement est omis, alors toutes les occurrences de Modele ne sont remplacées par rien, c'est-à-dire supprimées.
  • Utiliser la recherche de modèles pour analyser des chaînes de caractères diverses
    #!/bin/bash var1=abcd-1234-defg echo "var1 = $var1" t=${var1#*-*} echo "var1 (avec tout, jusqu'au et incluant le premier - supprimé) = $t" # t=${var1#*-} fonctionne de la même façon, #+ car # correspond à la plus petite chaîne de caractères, #+ et * correspond à tout ce qui précède, incluant la chaîne vide. # (Merci, S. C. pour l'avoir indiqué.) t=${var1##*-*} echo "Si var1 contient un \"-\", renvoie une chaîne vide... var1 = $t" t=${var1%*-*} echo "var1 (avec tout à partir de la fin - supprimé) = $t" echo # ------------------------------------------- nom_chemin=/home/bozo/idees/pensees.pour.aujourdhui # ------------------------------------------- echo "nom_chemin = $nom_chemin" t=${nom_chemin##/*/} echo "nom_chemin, sans les préfixes = $t" # Même effet que t=`basename $nom_chemin` dans ce cas particulier. # t=${nom_chemin%/}; t=${t##*/} est une solution plus générale, #+ mais elle échoue quelques fois. # Si $nom_chemin finit avec un retour chariot, alors `basename $nom_chemin` #+ ne fonctionnera pas mais l'expression ci-dessus le fera. # (Merci, S.C.) t=${nom_chemin%/*.*} # Même effet que t=`dirname $nom_chemin` echo "nom_chemin, sans les suffixes = $t" # Ceci va échouer dans certains cas, comme "../", "/foo////", # "foo/", "/". # Supprimer les suffixes, spécialement quand le nom de base n'en a pas, mais #+ que le nom du répertoire en a un, complique aussi le problème. # (Merci, S.C.) echo t=${nom_chemin:11} echo "$nom_chemin, avec les 11 premiers caractères supprimés = $t" t=${nom_chemin:11:5} echo "$nom_chemin, avec les 11 premiers caractères supprimés, longueur 5 = $t" echo t=${nom_chemin/bozo/clown} echo "$nom_chemin avec \"bozo\" remplacé par \"clown\" = $t" t=${nom_chemin/today/} echo "$nom_chemin avec \"today\" supprimé = $t" t=${nom_chemin//o/O} echo "$nom_chemin avec tous les o en majuscule = $t" t=${nom_chemin//o/} echo "$nom_chemin avec tous les o supprimés = $t" exit 0


  • ${var/#Modele/Remplacement}
    Si le préfixe de var correspond à Modele, alors Remplacement remplace Modele.

  • ${var/%Modele/Remplacement}
    Si le suffixe de var correspond à Modele, alors Remplacement remplace Modele.
  • Modèles correspondant au préfixe ou au suffixe d'une chaîne de caractères
    #!/bin/bash # Remplacement de modèle sur le préfixe / suffixe d'une chaîne de caractères. v0=abc1234zip1234abc # Variable originale. echo "v0 = $v0" # abc1234zip1234abc echo # Correspond au préfixe (début) d'une chaîne de caractères. v1=${v0/#abc/ABCDEF} # abc1234zip1234abc # |-| echo "v1 = $v1" # ABCDE1234zip1234abc # |---| # Correspond au suffixe (fin) d'une chaîne de caractères. v2=${v0/%abc/ABCDEF} # abc1234zip123abc # |-| echo "v2 = $v2" # abc1234zip1234ABCDEF # |----| echo # ---------------------------------------------------- # Doit correspondre au début / fin d'une chaîne de caractères. #+ sinon aucun remplacement ne se fera. # ---------------------------------------------------- v3=${v0/#123/000} # Correspond, mais pas au début. echo "v3 = $v3" # abc1234zip1234abc # PAS DE REMPLACEMENT. v4=${v0/%123/000} # Correspond, mais pas à la fin. echo "v4 = $v4" # abc1234zip1234abc # PAS DE REMPLACEMENT. exit 0


  • ${!varprefixe*}
    Correspond à toutes les variables déjà déclarées commençant par varprefixe.


Typer des variables: declare ou typeset

Les commandes internes declare et typeset (ils sont des synonymes exacts) permettent de restreindre les propriétés des variables. C'est une forme très faible de déclaration de variables disponible dans certains langages de programmation. La commande declare est spécifique à la version 2, ou supérieure, de Bash. La commande typeset fonctionne aussi dans les scripts ksh.

  • -r lecture seule
    (
  • declare -r var1
  • fonctionne de la même façon que
  • readonly var1
  • )Ceci est l'équivalent du qualificateur C const. Une tentative de modification de la valeur d'une variable en lecture seule échoue avec un message d'erreur.

  • -i entier
    Notez que certaines opérations arithmétiques sont permises pour des variables déclarées entières sans avoir besoin de expr ou de let.

  • -a tableau (array)
    La variable index sera traité comme un tableau.

  • -f fonction
    Une ligne
  • declare -f
  • sans argument dans un script donnera une liste de toutes les fonctions définies auparavant dans ce script.Un
  • declare -f nom_fonction
  • dans un script liste simplement la fonction nommée.

  • -x export
    Ceci déclare la disponibilité d'une variable pour une exportation en dehors de l'environnement du script lui-même.

  • var=$value
    La commande declare permet d'assigner une valeur à une variable dans la même déclaration que celle de ses ses propriétés.

Utiliser declare pour typer des variables
#!/bin/bash fonc1 () { echo Ceci est une fonction. } declare -f # Liste la fonction ci-dessus. echo declare -i var1 # var1 est un entier. var1=2367 echo "var1 déclaré comme $var1" var1=var1+1 # La déclaration d'un entier élimine le besoin d'utiliser let. echo "var1 incrémenté par 1 vaut $var1." # Essai de modification de la variable déclarée comme entier echo "Essai de modification de var1 en une valeur à virgule flottante, 2367.1." var1=2367.1 # Résultat: un message d'erreur, et une variable non modifiée. echo "var1 vaut toujours $var1" echo declare -r var2=13.36 # 'declare' permet de configurer une variable #+ proprement et de lui affecter une valeur. echo "var2 déclaré comme $var2" # Essai de modification d'une valeur en lecture #+ seule. var2=13.37 # Génère un message d'erreur, et sort du script. echo "var2 vaut toujours $var2" # Cette ligne ne s'exécutera pas. exit 0 # Le script ne terminera pas ici.

Références indirectes aux variables

Supposez que la valeur d'une variable soit le nom d'une seconde variable. Est-il possible de retrouver la valeur de cette deuxième variable à partir de la première? Par exemple, si a=lettre_de_l_alphabet et lettre_de_l_alphabet=z, est-ce qu'une référence à a pourrait renvoyer z? En fait, c'est possible et cela s'appelle une référence indirecte. On utilise la notation inhabituelle eval var1=\$$var2.

Références indirectes
#!/bin/bash # Référencement de variable indirecte. a=lettre_de_l_alphabet lettre_de_l_alphabet=z echo # Référence directe. echo "a = $a" # Référence indirecte. eval a=\$$a echo "Maintenant a = $a" echo # Maintenant, essayons de changer la référence du deuxième. t=tableau_cellule_3 tableau_cellule_3=24 echo "\"tableau_cellule_3\" = $tableau_cellule_3" echo -n "\"t\" déréférencé = "; eval echo \$$t # Dans ce cas simple, # eval t=\$$t; echo "\"t\" = $t" # fonctionne aussi (pourquoi?). echo t=tableau_cellule_3 NOUVELLE_VALEUR=387 tableau_cellule_3=$NOUVELLE_VALEUR echo "Modification de la valeur de \"tableau_cellule_3\" en $NOUVELLE_VALEUR." echo "\"tableau_cellule_3\" vaut maintenant $tableau_cellule_3" echo -n "\"t\" déréférencé maintenant "; eval echo \$$t # "eval" prend deux arguments "echo" et "\$$t" (valeur égale à $tableau_cellule_3) echo # (Merci, S.C., pour la clarification sur le comportement ci-dessus.) # Une autre méthode est la notation ${!t}, discutée dans la section #+ "Bash, version 2". # Voir aussi l'exemple "ex78.sh". exit 0
Passer une référence indirecte à awk
#!/bin/bash # Une autre version du script "column totaler" # qui ajoute une colonne spécifiée (de nombres) dans le fichier cible. # Ceci utilise les références indirectes. ARGS=2 E_MAUVAISARGS=65 if [ $# -ne "$ARGS" ] # Vérifie le bon nombre d'arguments sur la ligne de # commande. then echo "Usage: `basename $0` nomfichier numéro_colonne" exit $E_MAUVAISARGS fi nomfichier=$1 numero_colonne=$2 #===== Identique au script original, jusqu'à ce point =====# # Un script multi-ligne est appelé par awk ' ..... ' # Début du script awk. # ------------------------------------------------ awk " { total += \$${numero_colonne} # référence indirecte } END { print total } " "$nomfichier" # ------------------------------------------------ # Fin du script awk. # La référence de variable indirecte évite les problèmes de # référence d'une variable shell à l'intérieur d'un script embarqué. # Merci, Stephane Chazelas. exit 0

Attention : Cette méthode de référence indirecte est un peu délicate. Si la variable de second ordre change de valeur, alors la variable de premier ordre doit être correctement déréférencée (comme sur l'exemple ci-dessous). Heureusement, la notation ${!variable} introduite avec la version 2 de Bash (voir Références de variables indirectes - la nouvelle façon) rend les références indirectes plus intuitives.


$RANDOM: générer un nombre aléatoire

$RANDOM est une fonction interne Bash (pas une constante) renvoyant un entier pseudo aléatoire dans l'intervalle 0 - 32767. $RANDOM ne devrait pas être utilisé pour générer une clé de cryptage.

Générer des nombres aléatoires
#!/bin/bash # $RANDOM renvoie un entier différent à chaque invocation. # Echelle: 0 - 32767 (entier signé sur 16 bits). NBMAX=10 index=1 echo echo "$NBMAX nombres aléatoires:" echo "-----------------" while [ "$index" -le $NBMAX ] # Génère 10 ($NBMAX) entiers aléatoires. do nombre=$RANDOM echo $nombre let "index += 1" # Incremente index. done echo "-----------------" # Si vous avez besoin d'un entier aléatoire dans une certaine échelle, utilisez #+ l'opérateur 'modulo'. # Il renvoie le reste d'une division. ECHELLE=500 echo nombre=$RANDOM let "nombre %= $ECHELLE" echo "Nombre aléatoire inférieur à $ECHELLE --- $nombre" echo # Si vous avez besoin d'un entier aléatoire supérieur à une borne, alors # faites un test pour annuler tous les nombres en dessous de cette borne. PLANCHER=200 nombre=0 #initialise while [ "$nombre" -le $PLANCHER ] do nombre=$RANDOM done echo "Nombre aléatoire supérieur à $PLANCHER --- $nombre" echo # Vous pouvez combiner les deux techniques pour récupérer un nombre aléatoire # compris entre deux limites. nombre=0 #initialise while [ "$nombre" -le $PLANCHER ] do nombre=$RANDOM let "nombre %= $ECHELLE" # Met $nombre sous $ECHELLE. done echo "Nombre aléatoire compris entre $PLANCHER et $ECHELLE --- $nombre" echo # Génère un choix binaire, c'est-à-dire "vrai" ou "faux". BINAIRE=2 nombre=$RANDOM T=1 let "nombre %= $BINAIRE" # let "nombre >>= 14" donne une meilleure distribution aléatoire # (les décalages droits enlèvent tout sauf le dernier nombre binaire). if [ "$nombre" -eq $T ] then echo "VRAI" else echo "FAUX" fi echo # Peut générer le lancement de deux dés. SPOTS=7 # Modulo 7 donne une échelle de 0 à 6. DICE=2 ZERO=0 die1=0 die2=0 # Jette chaque dé séparément, et donne ainsi une chance correcte. while [ "$die1" -eq $ZERO ] # Un zéro ne peut pas arriver. do let "die1 = $RANDOM % $SPOTS" # Le premier. done while [ "$die2" -eq $ZERO ] do let "die2 = $RANDOM % $SPOTS" # Le deuxième. done let "throw = $die1 + $die2" echo "Jeté des dés = $throw" echo exit 0
Piocher une carte au hasard dans un tas
#!/bin/bash # pick-card.sh # Ceci est un exemple pour choisir au hasard un élément d'un tableau. # Prenez une carte, n'importe quelle carte. Suites="Carreau Pique Coeur Trefle" Denominations="2 3 4 5 6 7 8 9 10 Valet Dame Roi As" suite=($Suites) # Lire dans une variable de type tableau. denomination=($Denominations) num_suites=${#suite[*]} # Compter le nombre d'éléments. num_denominations=${#denomination[*]} echo -n "${denomination[$((RANDOM%num_denominations))]} of " echo ${suite[$((RANDOM%num_suites))]} # $bozo sh pick-cards.sh # Valet de trèfle # Merci, "jipe", pour m'avoir indiqué cette utilisation de $RANDOM. exit 0

Remarque : Jipe nous a indiqué un autre ensemble de techniques pour générer des nombres aléatoires à l'intérieur d'un intervalle donné.

Remarque :

A quel point $RANDOM est-il aléatoire? la meilleur façon de le tester est d'écrire un script qui enregistre la suite des nombres "aléatoires" générés par $RANDOM. Faisons tourner $RANDOM plusieurs fois...

Laisser tourner RANDOM plusieurs fois
#!/bin/bash # A quel point RANDOM est aléatoire? RANDOM=$$ # Réinitialise le générateur de nombres aléatoires en utilisant #+ le PID du script. PIPS=6 # Un dé a 6 faces. COMPTEURMAX=600# Augmentez ceci, si vous n'avez rien de mieux à faire. compteur=0 # Compteur. zero=0 # Doit initialiser les comptes à zéro. un=0 # car une variable non initialisée est null, et ne vaut pas zéro. deux=0 trois=0 quatre=0 cinq=0 six=0 Affiche_resultat () { echo echo "un = $un" echo "deux = $deux" echo "trois = $trois" echo "quatre = $quatre" echo "cinq = $cinq" echo "six = $six" echo } mise_a_jour_compteur() { case "$1" in 0) let "un += 1";; # Comme le dé n'a pas de "zéro", ceci correspond à 1. 1) let "deux += 1";; # Et ceci à 2, etc. 2) let "trois += 1";; 3) let "quatre += 1";; 4) let "cinq += 1";; 5) let "six += 1";; esac } echo while [ "$compteur" -lt "$COMPTEURMAX" ] do let "die1 = RANDOM % $PIPS" mise_a_jour_compteur $die1 let "compteur += 1" done Affiche_resultat # Les scores devraient être distribués de façon égale, en supposant que RANDOM #+ soit correctement aléatoire. # Avec $COMPTEURMAX à 600, tout devrait tourner autour de 100, plus ou moins #+ 20. # # Gardez en tête que RANDOM est un générateur pseudo-aléatoire, # et pas un particulièrement bon. # Exercice (facile): # --------------- # Réécrire ce script pour lancer une pièce 1000 fois. # Les choix sont "PILE" ou "FACE". exit 0

Comme nous avons vu sur le dernier exemple, il est préférable de "réinitialiser" le générateur RANDOM à chaque fois qu'il est invoqué. Utiliser le même germe pour RANDOM ne fera que répéter la même série de nombres. (Ceci reflète le comportement de la fonction C random().)

Réinitialiser RANDOM
#!/bin/bash # seeding-random.sh: Utiliser la variable RANDOM. NBMAX=25 # Combien de nombres à générer. nombres_aleatoires () { compteur=0 while [ "$compteur" -lt "$NBMAX" ] do nombre=$RANDOM echo -n "$nombre " let "compteur += 1" done } echo; echo RANDOM=1 # Initialiser RANDOM met en place le générateur de nombres #+ aléatoires. nombres_aleatoires echo; echo RANDOM=1 # Même élément pour RANDOM... nombres_aleatoires # ...reproduit la même série de nombres. # # Quand est-il utile de dupliquer une série de nombres #+ "aléatoires"? echo; echo RANDOM=2 # Nouvel essai, mais avec un 'germe' différent... nombres_aleatoires # donne une autre série... echo; echo # RANDOM=$$ initialise RANDOM à partir du PID du script. # Il est aussi possible d'initialiser RANDOM à partir des commandes 'time' et #+ 'date'. # Un peu plus d'amusement... SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }') # Sortie pseudo-aléatoire récupérée de /dev/urandom (fichier périphérique #+ pseudo-aléatoire), #+ puis convertit la ligne en nombres (octal) affichables avec "od". #+ Finalement "awk" récupère un seul nombre pour SEED. RANDOM=$SEED nombres_aleatoires echo; echo exit 0

Remarque : Le fichier périphérique /dev/urandom apporte un moyen de générer des nombres pseudo aléatoires bien plus "aléatoires" que la variable $RANDOM.

dd if=/dev/urandom of=fichier_cible bs=1 count=XX

crée un fichier de nombres pseudo aléatoires bien distribués. Néanmoins, assigner ces nombres à une variable dans un script nécessite un petit travail supplémentaire, tel qu'un filtrage par l'intermédiaire de od (comme dans l'exemple ci-dessus) ou tel que l'utilisation de dd (voir Effacer les fichiers de façon sure).

Remarque :

Remarque : Il existe aussi d'autres moyens de générer des nombres pseudo aléatoires dans un script. Awk propose une façon agréable de le faire.

Nombres pseudo aléatoires, en utilisant awk
#!/bin/bash # random2.sh: Renvoie un nombre pseudo-aléatoire compris dans la suite 0 - 1. # Utilise la fonction rand() d'awk. SCRIPTAWK=' { srand(); print rand() } ' # Commande(s) / paramètres passés à awk # Notez que srand() regénère le générateur de nombre aléatoire de awk. echo -n "Nombre aléatoire entre 0 et 1 = " echo | awk "$SCRIPTAWK" exit 0 # Exercices: # --------- # 1) En utilisant une construction boucle, affichez 10 nombres aléatoires # différents. # (Astuce: vous devez réinitialiser la fonction "srand()" avec une donnée # différente à chaque tour de la boucle. Qu'arrive-t'il si vous échouez à le # faire?) # 2) En utilisant un multiplicateur entier comme facteur d'échelle, générez des # nombres aléatoires compris entre 10 et 100. # 3) De même que l'exercice #2, ci-dessus, mais en générant des nombres # aléatoires entiers cette fois.

La construction en double parenthèse

De façon similaire à la commande let, la construction ((...)) permet une évaluation arithmétique. Dans sa forme la plus simple,

a=$(( 5 + 3 ))

exécutera le calcul "5 + 3" , soit 8, et attribuera sa valeur à la variable "a" . Néanmoins, cette construction en double parenthèse est aussi un mécanisme permettant la manipulation de variables à la manière du C dans Bash.

Manipulation, ç la façon du C, de variables
#!/bin/bash # Manipuler une variable, style C, en utilisant la construction ((...)). echo (( a = 23 )) # Initialiser une valeur, style C, avec des espaces des deux # côtés du signe "=". echo "a (initial value) = $a" (( a++ )) # Post-incrémente 'a', style C. echo "a (after a++) = $a" (( a-- )) # Post-décrémente 'a', style C. echo "a (after a--) = $a" (( ++a )) # Pre-incrémente 'a', style C. echo "a (after ++a) = $a" (( --a )) # Pre-décrémente 'a', style C. echo "a (after --a) = $a" echo (( t = a<45?7:11 )) # opérateur à trois opérandes, style C. echo "If a < 45, then t = 7, else t = 11." echo "t = $t " # Oui! echo # ------------------ # Alerte Easter Egg! # ------------------ # Chet Ramey a apparemment laissé un ensemble de constructions C non #+ documentées dans Bash (déjà adapté de ksh). # Dans les documents Bash, Ramey appelle ((...)) un shell arithmétique, #+ mais cela va bien au-delà. # Désolé, Chet, le secret est maintenant découvert. # Voir aussi les boucles "for" et "while" utilisant la construction ((...)). # Elles fonctionnent seulement avec Bash, version 2.04 ou ultérieure. exit 0

Voir aussi Une boucle for à la C.


3.1. Boucles et branchements

Les opérations sur des blocs de code sont la clé pour des scripts shell structurés, organisés. Les constructions de boucles et de branchement fournissent les outils pour accomplir ceci.


Boucles

Une boucle est un bloc de code qui répète une liste de commandes aussi longtemps que la condition de contrôle de la boucle est vraie.

  • for (in)
    C'est la construction de boucle de base. Elle diffère de façon significative de sa contre partie en C.
    for argin[liste]
    do
    commande(s)...
    done
    Remarque:A chaque passage dans la boucle, arg prend successivement la valeur de toutes les variables de la liste.L'argument liste peut contenir des caractères joker.Si do est sur la même ligne que for, il est impératif d'avoir un point virgule après la liste.
    for argin[liste];do

  • Des boucles for simples
    #!/bin/bash # Liste les planètes. for planete in Mercure Vénus Terre Mars Jupiter Saturne Uranus Neptune Pluton do echo $planete done echo # La 'liste' entière entourée par des guillemets crée une variable simple. for planete in "Mercure Vénus Terre Mars Jupiter Saturne Uranus Neptune Pluton" do echo $planete done exit 0
  • Remarque:Chaque élément de la
  • [liste]
  • peut contenir de multiples paramètres. C'est utile pour travailler sur des paramètres en groupe. Dans de tels cas, utilisez la commande set (voir Utiliser set avec les paramètres de position) pour forcer l'analyse de chaque élément de la [liste] et l'affectation de chaque composant aux paramètres positionels.
  • Boucle for avec deux paramètres dans chaque élément de la [liste]
    #!/bin/bash # Planètes revisitées. # Associe le nom de chaque planète à sa distance du soleil. for planete in "Mercure 36" "Vénus 67" "Terre 93" "Mars 142" "Jupiter 483" do set -- $planete # Analyse la variable "planete" et initialise les paramètres #+ de position. # Le "--" empêche de mauvaises surprises si $planete est nul ou commence avec #+ un tiret. # Il peut être utile de sauvegarder les paramètres de position originaux, car #+ ils seront écrasés. # Une façon de le faire est d'utiliser un tableau, # parametres_originaux=("$@") echo "$1 $2.000.000 miles du soleil" #-------deux tabulations---concatènent les zéros dans le paramètre $2 done # (Merci, S.C., pour les clarifications supplémentaires.) exit 0
  • Une variable peut fournir la
  • [liste]
  • dans une boucle for.
  • Fileinfo: opérer sur une liste de fichiers contenue dans une variable
    #!/bin/bash # fileinfo.sh FICHIERS="/usr/sbin/privatepw /usr/sbin/pwck /usr/sbin/go500gw /usr/bin/fakefile /sbin/mkreiserfs /sbin/ypbind" # Liste de fichiers qui vous intéressent. # Envoyez-les dans un fichier quelconque, /usr/bin/fauxfichier. echo for fichier in $FILES do if [ ! -e "$fichier" ] # Vérifie si le fichier existe. then echo "$fichier n'existe pas."; echo continue # Au suivant. fi ls -l $fichier | awk '{ print $9 " taille: " $5 }' # Affiche 2 champs. whatis `basename $fichier` # Informations sur le fichier. echo done exit 0
  • La
  • [liste]
  • dans une boucle for peut contenir un remplacement des noms de fichier, c'est-à-dire utiliser des jokers pour l'expansion de noms de fichiers.
  • Agir sur des fichiers à l'aide d'une boucle for
    #!/bin/bash # list-glob.sh: Générer une [liste] dans une boucle for en utilisant "globbing". echo for fichier in * do ls -l "$fichier" # Liste tous les fichiers de $PWD (répertoire courant). # Rappelez-vous que le caractère joker "*" correspond à chaque nom de fichier, # néanmoins, lors du "globbing", il ne récupère pas les fichier commençant # par un point. # Si le modèle ne correspond à aucun fichier, il s'étend à lui-même. # Pour empêcher ceci, utilisez l'option nullglob # (shopt -s nullglob). # Merci, S.C. done echo; echo for fichier in [jx]* do rm -f $fichier # Supprime seulement les fichiers commençant par un "j" ou # un "x" dans $PWD. echo "Suppression du fichier \"$fichier\"". done echo exit 0
  • Omettre la partie
  • in [liste]
  • d'une boucle for fait en sorte que la boucle opère sur $@, la liste des arguments donnés sur la ligne de commande du script. Une illustration particulièrement intelligente de ceci est primes: Générer des nombres aléatoires en utilisant l'opérateur modulo.
  • in [liste] manquant dans une boucle for
    #!/bin/bash # Appeler à la fois avec et sans arguments, et voir ce que cela donne. for a do echo -n "$a " done # La 'liste' est manquante, donc la boucle opère sur '$@' #+ (la liste d'arguments sur la ligne de commande, incluant les espaces blancs). echo exit 0
  • Il est possible d'utiliser la substitution de commandes pour générer la
  • [liste]
  • d'une boucle for. Voir aussi Utiliser seq pour générer l'incrément d'une boucle, Afficher les liens symboliques dans un répertoire et Conversion de base.
  • Générer la [liste] dans une boucle for avec la substitution de commandes
    #!/bin/bash # Une boucle for avec une [liste] générée par une substitution de commande. NOMBRES="9 7 3 8 37.53" for nombre in `echo $NOMBRE` # for nombre in 9 7 3 8 37.53 do echo -n "$nombre " done echo exit 0
  • Voici un exemple un peu plus complexe de l'utilisation de la substitution de commandes pour créer la [liste].
  • Un remplaçant de grep pour les fichiers binaires
    #!/bin/bash # bin-grep.sh: Trouve les chaînes de caractères correspondantes dans un fichier #+ binaire. # Un remplacement de "grep" pour les fichiers binaires. # Similaire par son effet à "grep -a" E_BADARGS=65 E_NOFILE=66 if [ $# -ne 2 ] then echo "Usage: `basename $0` chaine nomfichier" exit $E_BADARGS fi if [ ! -f "$2" ] then echo "Le fichier \"$2\" n'existe pas." exit $E_NOFILE fi for word in $( strings "$2" | grep "$1" ) # La commande "strings" liste les chaînes de caractères dans les fichiers #+ binaires. # Sortie envoyée via un tube dans "grep", qui cherche la chaîne désirée. do echo $word done # Comme S.C. l'a indiqué, la boucle for ci-dessus pourrait être remplacée avec #+ la chaîne # strings "$2" | grep "$1" | tr -s "$IFS" '[\n*]' # Essayez quelque chose comme "./bin-grep.sh mem /bin/ls" pour comprendre ce #+ script. exit 0
  • Un peu la même chose.
  • Afficher tous les utilisateurs du système
    #!/bin/bash # userlist.sh FICHIER_MOTS_DE_PASSE=/etc/passwd n=1 # Nombre d'utilisateurs for nom in $(awk 'BEGIN{FS=":"}{print $1}' < "$FICHIER_MOTS_DE_PASSE" ) # Champ séparateur = : ^^^^^^ # Affiche le premier champ ^^^^^^^^ # Obtient l'entrée à partir du fichier ^^^^^^^^^^^^^^^^^^^^^^ do echo "UTILISATEUR #$n = $nom" let "n += 1" done # UTILISATEUR #1 = root # UTILISATEUR #2 = bin # UTILISATEUR #3 = daemon # ... # UTILISATEUR #30 = bozo exit 0
  • Un dernier exemple d'une [liste] résultant d'une substitution de commande.
  • Rechercher les auteurs de tous les binaires d'un répertoire
    #!/bin/bash # findstring.sh: # Cherche une chaîne de caractères particulière dans des binaires d'un #+ répertoire particulier. repertoire=/usr/bin/ chainef="Free Software Foundation" # Voir quels fichiers viennent de la FSF. for fichier in $( find $repertoire -type f -name '*' | sort ) do strings -f $fichier | grep "$chainef" | sed -e "s%$repertoire%%" # Dans l'expression "sed", il est nécessaire de substituer le délimiteur #+ standard "/" parce que "/" se trouve être un caractère filtré. Ne pas le #+ faire provoque un message d'erreur (essayez). done exit 0 # Exercice (facile): # --------------- # Convertir ce script pour prendre en paramètres de ligne de commande les #+ variables $repertoire et $chainef.
  • La sortie d'une boucle for peut être envoyée via un tube à une ou plusieurs commandes.
  • Afficher les liens symboliques dans un répertoire
    #!/bin/bash # symlinks.sh: Liste les liens symboliques dans un répertoire. repertoire=${1-`pwd`} # Par défaut, le répertoire courant, si aucun autre n'a été spécifié. # Equivalent au bloc de code ci-dessous. # ----------------------------------------------------------------- # ARGS=1 # Attend un argument en ligne de commande. # # if [ $# -ne "$ARGS" ] # Si ce n'est pas 1 argument... # then # repertoire=`pwd` # répertoire courant # else # repertoire=$1 # fi # ----------------------------------------------------------------- echo "Liens symboliques dans le répertoire \"$repertoire\"" for fichier in "$( find $repertoire -type l )" # -type l = liens symboliques do echo "$fichier" done | sort # Sinon la liste de fichiers n'est pas trié. # Comme Dominik 'Aeneas' Schnitzer l'indique, ne pas mettre entre guillemets #+ $( find $repertoire -type l ) #+ fera échouer le script sur les noms de fichier comprenant des espaces. # Même ceci ne prendra que le premier champ de chaque argument. exit 0
  • Le stdout d'une boucle peut être redirigée vers un fichier, comme cette légère modification du précédent exemple le montre.
  • Liens symboliques dans un répertoire, sauvés dans un fichier
    #!/bin/bash # symlinks.sh: Liste les liens symboliques dans un répertoire. FICHIER_DE_SORTIE=liste.liens_symboliques # fichier de sauvegarde repertoire=${1-`pwd`} # Par défaut, le répertoire courant si aucun autre n'a été spécifié. echo "liens symboliques dans le répertoire \"$repertoire\"" > "$FICHIER_DE_SORTIE" echo "----------------------------------------------------" >> "$FICHIER_DE_SORTIE" for fichier in "$( find $repertoire -type l )" # -type l = liens symboliques do echo "$fichier" done | sort >> "$FICHIER_DE_SORTIE" # stdout de la boucle # ^^^^^^^^^^^^^^^^^^ redirigé vers le fichier de sauvegarde. exit 0
  • Il existe une autre syntaxe pour une boucle for ressemblant fortement à celle du C. Elle nécessite des parenthèses doubles.
  • Une boucle for à la C
    #!/bin/bash # Deux façons de compter jusqu'à 10. echo # Syntaxe standard. for a in 1 2 3 4 5 6 7 8 9 10 do echo -n "$a " done echo; echo # +==========================================+ # Maintenant, faisons de même en utilisant une syntaxe C. LIMITE=10 for ((a=1; a <= LIMITE ; a++)) # Double parenthèses, et "LIMITE" sans "$". do echo -n "$a " done # Une construction empruntée à 'ksh93'. echo; echo # +=========================================================================+ # Utilisons l'opérateur "virgule" C pour incrémenter deux variables en même #+ temps. for ((a=1, b=1; a <= LIMITE ; a++, b++)) # La virgule chaîne les opérations. do echo -n "$a-$b " done echo; echo exit 0
  • Voir aussi Application complexe des tableaux Exploration d'une étrange série mathématique, Simuler un tableau à deux dimensions, puis son test et collatz: Séries de Collatz.---Maintenant, une boucle for utilisée dans un contexte de la "vie quotidienne" .
  • Utiliser efax en mode batch
    #!/bin/bash ARGUMENTS_ATTENDUS=2 E_MAUVAISARGS=65 if [ $# -ne $ARGUMENTS_ATTENDUS ] # Vérifie le bon nombre d'arguments en ligne de commande. then echo "Usage: `basename $0` téléphone# fichier-texte" exit $E_MAUVAISARGS fi if [ ! -f "$2" ] then echo "Le fichier $2 n'est pas un fichier texte" exit $E_MAUVAISARGS fi fax make $2 # Crée des fichiers formatés pour le fax à partir de #+ fichiers texte. for fichier in $(ls $2.0*) # Concatène les fichiers convertis. # Utilise le caractère joker dans la liste des variables. do fic="$fic $fichier" done efax -d /dev/ttyS3 -o1 -t "T$1" $fic # Fait le boulot. # Comme S.C. l'a indiqué, la boucle for peut être supprimée avec # efax -d /dev/ttyS3 -o1 -t "T$1" $2.0* # mais ce n'est pas aussi instructif [grin]. exit 0


  • while
    Cette construction teste une condition au début de la boucle et continue à boucler tant que la condition est vraie (renvoie un 0 code de sortie). Par opposition à une boucle for, une boucle while trouve son utilité dans des situations où le nombre de répétitions n'est pas connu dès le départ.
    while[condition]
    do
    commande...
    done
    Comme c'est le cas avec les boucles for/in, placez le do sur la même ligne que le test de la condition nécessite un point virgule.
    while[condition];do
    Notez que certaines boucles while spécialisées, comme par exemple une construction getopts, dévie quelque peu du modèle standard donné ici.
  • Simple boucle while
    #!/bin/bash var0=0 LIMITE=10 while [ "$var0" -lt "$LIMITE" ] do echo -n "$var0 " # -n supprime le retour chariot. var0=`expr $var0 + 1` # var0=$(($var0+1)) fonctionne aussi. done echo exit 0
  • Une autre boucle while
    #!/bin/bash echo while [ "$var1" != "fin" ] # while teste "$var1" != "fin" do # fonctionne aussi. echo "Variable d'entrée #1 (quitte avec fin) " read var1 # pas de 'read $var1' (pourquoi?). echo "variable #1 = $var1" # A besoin des guillemets à cause du "#". # Si l'entrée est 'fin', l'affiche ici. # Ne teste pas la condition de fin avant de revenir en haut de la boucle. echo done exit 0
  • Une boucle while peut avoir de multiples conditions. Seule la condition finale détermine quand la boucle se termine. Ceci nécessite une syntaxe de boucle légèrement différente, malgré tout.
  • Boucle while avec de multiples conditions
    #!/bin/bash var1=unset precedent=$var1 while echo "Variable précédente = $precedent" echo precedent=$var1 [ "$var1" != fin ] # Garde trace de ce que $var1 valait précédemment. # Quatre conditions sur "while", mais seule la dernière contrôle la #+ boucle. # Le *dernier* code de sortie est celui qui compte. do echo "Variable d'entrée #1 (quitte avec fin) " read var1 echo "variable #1 = $var1" done # Essayez de comprendre comment cela fonctionne. # il y a un peu d'astuces. exit 0
  • Comme pour une boucle for, une boucle while peut employer une syntaxe identique à C en utilisant la construction avec des parenthèses doubles (voir aussi Manipulation, ç la façon du C, de variables).
  • Syntaxe à la C pour une boucle while
    #!/bin/bash # wh-loopc.sh: Compter jusqu'à 10 dans une boucle "while". LIMITE=10 a=1 while [ "$a" -le $LIMITE ] do echo -n "$a " let "a+=1" done # Pas de surprise, jusqu'ici. echo; echo # +=================================================================+ # Maintenant, de nouveau mais avec une syntaxe C. ((a = 1)) # a=1 # Les doubles parenthèses permettent l'utilisation des espaces pour initialiser #+ une variable, comme en C. while (( a <= LIMITE )) # Doubles parenthèses, et pas de "$" devant la variable. do echo -n "$a " ((a += 1)) # let "a+=1" # Oui, en effet. # Les doubles parenthèses permettent d'incrémenter une variable avec une #+ syntaxe style C. done echo # Maintenant, les programmeurs C se sentent chez eux avec Bash. exit 0
  • Remarque:Une boucle while peut avoir son stdin redirigé vers un fichier par un à la fin.

  • until
    Cette construction teste une condition au début de la boucle et continue à boucler tant que la condition est fausse (l'opposé de la boucle while).
    until[condition-est-vraie]
    do
    commande...
    done
    Notez qu'une boucle until teste la condition de fin au début de la boucle, contrairement aux constructions similaires dans certains langages de programmation. Comme c'est la cas avec les boucles for/in, placez do sur la même ligne que le test de la condition nécessite un point virgule.
    until[condition-est-vraie];do
  • Boucle until
    #!/bin/bash until [ "$var1" = fin ] # Condition du test ici, en haut de la boucle. do echo "Variable d'entrée #1 " echo "(fin pour sortir)" read var1 echo "variable #1 = $var1" done exit 0



Boucles imbriquées

Une boucle imbriquée est une boucle dans une boucle, une boucle à l'intérieur du corps d'une autre boucle. Ce qui se passe est que le premier tour de la boucle externe déclenche la boucle interne, qui s'exécute jusqu'au bout. Puis le deuxième tout de la boucle externe déclenche la boucle interne une nouvelle fois. Ceci se répète jusqu'à ce que la boucle externe termine. Bien sûr, un break à l'intérieur de la boucle interne ou externe peut interrompre ce processus.

Boucles imbriquées
#!/bin/bash # Boucles "for" imbriquées. externe=1 # Initialisation du compteur de la boucle externe. # Début de la boucle externe. for a in 1 2 3 4 5 do echo "Tour $externe dans la boucle externe." echo "------------------------------" interne=1 # Initialisation du compteur de la boucle interne. # Début de la boucle interne. for b in 1 2 3 4 5 do echo "Tour $interne dans la boucle interne." let "interne+=1" # Incrémentation du compteur de la boucle interne. done # Fin de la boucle interne. let "externe+=1" # Incrémentation du compteur de la boucle externe. echo # Espace à chaque sortie de la boucle externe. done # Fin de la boucle externe. exit 0

Voir Un viel ami: Le tri Bubble Sort pour une illustration de boucles "while" imbriquées, et Application complexe des tableaux : Crible d' Eratosthene pour voir une boucle "while" imbriquée dans une boucle "until" .


Contrôle de boucles

  • break
    Les commandes de contrôle de boucle break et continue (22) correspondent exactement à leur contre partie dans d'autres langages de programmation. La commande break termine la boucle (en sort), alors que continue fait un saut à la prochaine itération de la boucle, oubliant les commandes restantes dans ce cycle particulier de la boucle.
  • Effets de break et continue dans une boucle
    #!/bin/bash LIMITE=19 # Limite haute. echo echo "Affiche les nombres de 1 à 20 (mais pas 3 et 11)." a=0 while [ $a -le "$LIMITE" ] do a=$(($a+1)) if [ "$a" -eq 3 ] || [ "$a" -eq 11 ] # Exclut 3 et 11 then continue # Continue avec un nouvelle itération de la boucle. fi echo -n "$a " done # Exercice: # Pourquoi la boucle affiche-t'elle jusqu'au 20? echo; echo echo "Affiche les nombres de 1 à 20, mais quelque chose se passe après 2." ################################################################## # Même boucle, mais en substituant 'continue' par 'break'. a=0 while [ "$a" -le "$LIMITE" ] do a=$(($a+1)) if [ "$a" -gt 2 ] then break # Ne continue pas le reste de la boucle. fi echo -n "$a " done echo; echo; echo exit 0
  • La commande break peut de façon optionnelle prendre un paramètre. Un simple break termine seulement la boucle interne où elle est incluse mais un break N sortira de N niveaux de boucle.
  • Sortir de plusieurs niveaux de boucle
    #!/bin/bash # break-levels.sh: Sortir des boucles. # "break N" sort de N niveaux de boucles. for boucleexterne in 1 2 3 4 5 do echo -n "Groupe $boucleexterne: " for boucleinterne in 1 2 3 4 5 do echo -n "$boucleinterne " if [ "$boucleinterne" -eq 3 ] then break # Essayez break 2 pour voir ce qui se passe. # (Sort des boucles internes et externes.) fi done echo done echo exit 0
  • La commande continue, similaire à break, prend un paramètre de façon optionnelle. Un simple continue court-circuite l'itération courante et commence la prochaine itération de la boucle dans laquelle elle se trouve. Un continue N termine toutes les itérations à partir de son niveau de boucle et continue avec l'itération de la boucle N niveaux au-dessus.
  • Continuer à un plus haut niveau de boucle
    #!/bin/bash # La commande "continue N", continue jusqu'au niveau de boucle N. for exterieur in I II III IV V # Boucle extérieure do echo; echo -n "Groupe $exterieur: " for interieur in 1 2 3 4 5 6 7 8 9 10 # Boucle intérieure do if [ "$interieur" -eq 7 ] then continue 2 # Continue la boucle au deuxième niveau, c'est-à-dire la #+ boucle extérieure. # Remplacez la ligne ci-dessus avec un simple "continue" # pour voir le comportement normal de la boucle. fi echo -n "$interieur " # 8 9 10 ne s'afficheront jamais. done done echo; echo # Exercice: # Parvenir à un emploi utile pour "continue N" dans un script. exit 0
  • Utiliser continue N dans une tâche courante
    # Albert Reiner gives an example of how to use "continue N": # --------------------------------------------------------- # Suppose I have a large number of jobs that need to be run, with #+ any data that is to be treated in files of a given name pattern in a #+ directory. There are several machines that access this directory, and #+ I want to distribute the work over these different boxen. Then I #+ usually nohup something like the following on every box: while true do for n in .iso.* do [ "$n" = ".iso.opts" ] && continue beta=${n#.iso.} [ -r .Iso.$beta ] && continue [ -r .lock.$beta ] && sleep 10 && continue lockfile -r0 .lock.$beta || continue echo -n "$beta: " `date` run-isotherm $beta date ls -alF .Iso.$beta [ -r .Iso.$beta ] && rm -f .lock.$beta continue 2 done break done # The details, in particular the sleep N, are particular to my #+ application, but the general pattern is: while true do for job in {pattern} do {job already done or running} && continue {mark job as running, do job, mark job as done} continue 2 done break # Or something like `sleep 600' to avoid termination. done # This way the script will stop only when there are no more jobs to do #+ (including jobs that were added during runtime). Through the use #+ of appropriate lockfiles it can be run on several machines #+ concurrently without duplication of calculations [which run a couple #+ of hours in my case, so I really want to avoid this]. Also, as search #+ always starts again from the beginning, one can encode priorities in #+ the file names. Of course, one could also do this without `continue 2', #+ but then one would have to actually check whether or not some job #+ was done (so that we should immediately look for the next job) or not #+ (in which case we terminate or sleep for a long time before checking #+ for a new job).
  • Attention:La construction continue N est difficile à comprendre et complexe à utiliser dans tous les contextes. Il est probablement raisonnable de l'éviter.


Tests et branchements

Les constructions case et select ne sont pas techniquement des boucles puisqu' elles n'exécutent pas un bloc de code de façon itérative. Néanmoins, comme les boucles, elles orientent le flot d'exécution du programme suivant certaines conditions au début ou à la fin du bloc.

  • case (in) / esac
    La construction case est l'équivalent shell de switch en C/C++. Elle permet le branchement vers un bloc parmi un certain nombre de blocs de code, suivant des tests de condition. Elle agit comme une espèce de raccourcis pour de multiples instructions if/then/else et est un outil approprié pour la création de menus.
    case"$variable"in

    "$condition1" )
    commande...
    ;;

    "$condition2" )
    commande...
    ;;

    esac
    Remarque:Protéger les variables n'est pas obligatoire car la séparation de mots n'est pas effective. Chaque ligne de test se termine avec une parenthèse droite ). Chaque bloc de conditions termine avec un double points virgule ;;. Le bloc case entier se termine avec un esac (case épelé à l'envers).
  • Utiliser case
    #!/bin/bash echo; echo "Appuyez sur une touche, puis faites ENTER." read Touche case "$Touche" in [a-z] ) echo "Lettre minuscule";; [A-Z] ) echo "Lettre majuscule";; [0-9] ) echo "Nombre";; * ) echo "Ponctuation, espace blanc ou autre";; esac # Permet un ensemble de caractères dans des [crochets]. # Exercice: # -------- # Ce script accepte un simple appui sur une touche, puis se termine. # Modifiez le script pour qu'il accepte une saisie continue, # rapportez chaque appui sur une touche, et terminez lors de l'appui sur "X". # Astuce: mettre tout dans une boucle "while". exit 0
  • Créer des menus en utilisant case
    #!/bin/bash # Base de données d'adresse. clear # Efface l'écran. echo " Liste de Contacts" echo " -----------------" echo "Choisissez une des personnes suivantes:" echo echo "[E]vans, Roland" echo "[J]ones, Mildred" echo "[S]mith, Julie" echo "[Z]ane, Morris" echo read personne case "$person" in # Notez que la variable est entre guillemets. "E" | "e" ) # Accepte les entrées en majuscule ou minuscule. echo echo "Roland Evans" echo "4321 Floppy Dr." echo "Hardscrabble, CO 80753" echo "(303) 734-9874" echo "(303) 734-9892 fax" echo "revans@zzy.net" echo "Business partner & old friend" ;; # Notez le double point-virgule pour terminer chaque option. "J" | "j" ) echo echo "Mildred Jones" echo "249 E. 7th St., Apt. 19" echo "New York, NY 10009" echo "(212) 533-2814" echo "(212) 533-9972 fax" echo "milliej@loisaida.com" echo "Girlfriend" echo "Birthday: Feb. 11" ;; # Ajoutez de l'info pour Smith & Zane plus tard. * ) # Option par défaut. # Entrée vide (en appuyant uniquement sur la touche RETURN) vient ici aussi. echo echo "Pas encore dans la base de données." ;; esac echo # Exercice: # -------- # Modifier le script pour qu'il accepte une saisie continue, #+ au lieu de s'arrêter après avoir affiché une seule adresse. exit 0
  • Une utilisation exceptionnellement intelligente de case concerne le test des paramètres de ligne de commande.
  • Utiliser la substitution de commandes pour générer la variable case
    #!/bin/bash # Utilisation de la substitution de commandes pour générer une variable "case". case $( arch ) in # "arch" renvoie l'architecture de la machine. i386 ) echo "Machine 80386";; i486 ) echo "Machine 80486";; i586 ) echo "Machine Pentium";; i686 ) echo "Machine Pentium2+";; * ) echo "Autre type de machine";; esac exit 0
  • Une construction case peut filtrer les chaînes sur des paramètres de remplacement.
  • Simple correspondance de chaîne
    #!/bin/bash # match-string.sh: simple correspondance de chaînes de caractères chaines_correspondent () { CORRESPOND=0 CORRESPOND_PAS=90 PARAMS=2 # La fonction requiert deux arguments. MAUVAIS_PARAMS=91 [ $# -eq $PARAMS ] || return $MAUVAIS_PARAMS case "$1" in "$2") return $CORRESPOND;; * ) return $CORRESPOND_PAS;; esac } a=un b=deux c=trois d=deux chaines_correspondent $a # mauvais nombre de paramètres echo $? # 91 chaines_correspondent $a $b # pas de correspondance echo $? # 90 chaines_correspondent $b $d # correspondance echo $? # 0 exit 0
  • Vérification d'une entrée alphabétique
    #!/bin/bash # isalpha.sh: Utiliser une structure "case" pour filtrer une chaîne de #+ caractères. SUCCES=0 ECHEC=-1 est_alpha () # Teste si le *premier caractère* de la chaîne est alphabétique. { if [ -z "$1" ] # Pas d'argument passé? then return $ECHEC fi case "$1" in [a-zA-Z]*) return $SUCCES;; # Commence avec une lettre? * ) return $ECHEC;; esac } # Comparer ceci avec la fonction "isalpha ()" en C. est_alpha2 () # Teste si la *chaîne entière* est alphabétique. { [ $# -eq 1 ] || return $ECHEC case $1 in *[!a-zA-Z]*|"") return $ECHEC;; *) return $SUCCES;; esac } est_numerique () # Teste si la *chaîne entière* est numérique. { # En d'autres mots, teste si la variable est de type entier. [ $# -eq 1 ] || return $ECHEC case $1 in *[!0-9]*|"") return $ECHEC;; *) return $SUCCES;; esac } verif_var () # Interface à est_alpha (). { if est_alpha "$@" then echo "\"$*\" commence avec un caractère alpha." if est_alpha2 "$@" then # Aucune raison de tester si le premier caractère est non alpha. echo "\"$*\" contient seulement des caractères alpha." else echo "\"$*\" contient au moins un caractère non alpha." fi else echo "\"$*\" commence avec un caractère non alpha." # Aussi "non alpha" si aucun argument n'est passé. fi echo } verif_numerique () # Interface à est_numerique (). { if est_numerique "$@" then echo "\"$*\" contient seulement des chiffres [0 - 9]." else echo "\"$*\" a au moins un caractère qui n'est pas un chiffre." fi echo } a=23skidoo b=H3llo c=-What? d=What? e=`echo $b` # Substitution de commandes. f=AbcDef g=27234 h=27a34 i=27.34 verif_var $a verif_var $b verif_var $c verif_var $d verif_var $e verif_var $f verif_var # Pas d'argument passé, donc qu'arrive-t'il? # verif_numerique $g verif_numerique $h verif_numerique $i exit 0 # Script amélioré par S.C. # Exercice: # -------- # Ecrire une fonction 'est_flottant ()' qui teste les nombres en virgules #+ flottantes. # Astuce: La fonction duplique 'est_numerique ()', #+ mais ajoute un test pour le point décimal nécessaire.


  • select
    La construction select, adoptée du Korn Shell, est encore un autre outil pour construire les menus.
    select variable[in liste]
    do
    commande...
    break
    done
    Ceci demande à l'utilisateur d'entrer un des choix présentés dans la variable liste. Notez que select utilise l'invite PS3 (#? ) par défaut mais que ceci peut être changé.
  • Créer des menus en utilisant select
    #!/bin/bash PS3='Choisissez votre légume favori: ' # Affiche l'invite. echo select legume in "haricot" "carotte" "patate" "ognion" "rutabaga" do echo echo "Votre légume favori est $legume." echo break # si il n'y avait pas de 'break' ici, il continuerait à tourner sans #+ fin. done exit 0
  • Si une
  • liste in
  • est omise, alors select utilise la liste des arguments en ligne de commandes ($@) passée au script ou à la fonction dans lequel la construction select est intégrée.Comparez ceci au comportement de la construction
    for variable[in liste]
    avec
  • in liste
  • omis.
  • Créer des menus en utilisant select dans une fonction
    #!/bin/bash PS3='Choisissez votre légume favori: ' echo choix_entre() { select legume # [in list] omise, donc 'select' utilise les arguments passés à la fonction. do echo echo "Votre légume favori est $vegetable." echo break done } choix_entre haricot riz carotte radis tomate épinard # $1 $2 $3 $4 $5 $6 # passé à la fonction choix_entre() exit 0
  • Voir aussi Simple application de base de données, utilisant les références de variables indirectes.


3.2. Commandes internes et intégrées

Une commande intégrée est une commande contenue dans la boîte à outils de Bash, elle est donc litéralement intégrée. C'est soit pour des raisons de performance -- les commandes intégrées s'exécutent plus rapidement que les commandes externes, qui nécessitent habituellement de dupliquer le processus -- soit parce qu'une commande intégrée spécifique a besoin d'un accès direct aux variables internes du shell.

Quand une commande ou le shell lui-même débute un processus fils pour réaliser une tâche, cela s'appelle un fork. Ce nouveau processus est le "fils" , et le processus qui l'a lancé est le "père" . Pendant que le processus fils fait son travail, le processus père est toujours en cours d'exécution.

Généralement, une commande intégrée Bash ne lance pas un sous-processus lorsqu'il s'exécute à partir d'un script. Une commande système externe ou un filtre dans un script va généralement lancer un sous-processus.

Une commande intégrée peut être le synonyme d'une commande système du même nom, mais Bash la réimplémente en interne. Par exemple, la commande Bash echo n'est pas la même que /bin/echo, bien que leurs comportements soient pratiquement identiques.

Un mot clé est un mot, une expression ou un opérateur réservé. Les mots clés ont une signification particulière pour le shell et sont en fait les blocs permettant la construction de la syntaxe du shell. Comme exemple, "for " , "while " , "do" et "! " sont des mots clés. Identiques à une commande intégrée, un mot clé est codé en dur dans Bash, mais, contrairement à une commande intégrée, un mot clé n'est pas en lui-même une commande, mais fait partie d'un ensemble plus large de commandes. (23)

  • echo
    envoie (vers stdout) une expression ou une variable (voir Affectation de variable et substitution ). Un echo nécessite l'option -e pour afficher des séquences d'échappement. Voir Caractères d'échappement.Normalement, chaque commande echo envoie un retour à la ligne, mais l'option -n désactive ce comportement. Remarque:Un echo peut être utilisé pour envoyer des informations à un ensemble de commandes via un tube. Remarque: Remarque:Un echo, en combinaison avec une substitution de commande peut configurer une variable. Remarque:
  • a=`echo "HELLO" | tr A-Z a-z`
  • Remarque:Voir aussi lowercase: Change tous les noms de fichier du répertoire courant en minuscule., incorrectname élimine dans le répertoire courant les fichiers dont le nom contient des caractères incorrects et des espaces blancs., Paiement mensuel sur une hypothèque et Conversion de base.Sachez que echo `commande` supprime tous les retours chariot que la sortie de commande génère.La variable $IFS (séparateur interne de champ) contient habituellement \n (retour chariot) comme un des éléments de ses espaces blanc. Du coup, Bash divise la sortie de commande suivant les retours chariot et les prend comme argument pour echo. Ensuite, echo affiche ces arguments, séparés par des espaces.
  • bash$ ls -l /usr/share/apps/kjezz/sounds -rw-r--r-- 1 root root 1407 Nov 7 2000 reflect.au -rw-r--r-- 1 root root 362 Nov 7 2000 seconds.au bash$ echo `ls -l /usr/share/apps/kjezz/sounds` total 40 -rw-r--r-- 1 root root 716 Nov 7 2000 reflect.au -rw-r--r-- 1 root root 362 Nov 7 2000 seconds.au
  • Remarque:Cette commande est une commande intégrée au shell, et n'est pas identique à /bin/echo, bien que son comportement soit similaire. Remarque:
  • bash$ type -a echo echo is a shell builtin echo is /bin/echo


  • printf
    La commande printf, un print formaté, est un echo amélioré. C'est une variante limitée de la fonction printf() en langage C, et sa syntaxe est quelque peu différente.
    printf format-string...parameter...
    Il s'agit de la version intégrée à Bash de la commande /bin/printf ou /usr/bin/printf. Voir la page de manuel pour printf (la commande système) pour un éclairage détaillé. Attention:Les anciennes versions de Bash peuvent ne pas supporter printf.
  • printf en action
    #!/bin/bash # printf demo PI=3.14159265358979 ConstanteDecimale=31373 Message1="Greetings," Message2="Earthling." echo printf "Pi avec deux décimales = %1.2f" $PI echo printf "Pi avec neuf décimales = %1.9f" $PI # Il arrondit même correctement. printf "\n" # Affiche un retour chariot. # équivalent à 'echo'. printf "Constante = \t%d\n" $ConstanteDecimale # Insert une tabulation (\t) printf "%s %s \n" $Message1 $Message2 echo # ==========================================# # Simulation de la fonction C, 'sprintf'. # Chager une variable avec une chaîne de caractères formatée. echo Pi12=$(printf "%1.12f" $PI) echo "Pi avec 12 décimales = $Pi12" Msg=`printf "%s %s \n" $Message1 $Message2` echo $Msg; echo $Msg # La fonction 'sprintf' est maintenant accessible en tant que module chargeable #+ de Bash, mais ce n'est pas portable. exit 0
  • Formatter les messages d'erreur est une application utile de printf

  • read
    "Lit" la valeur d'une variable à partir de stdin, c'est-à-dire récupère interactivement les entrées à partir du clavier. L'option -a permet à read d'obtenir des valeurs tableau (voir Quelques propriétés spéciales des tableaux).
  • Affectation d'une variable, en utilisant read
    #!/bin/bash echo -n "Entrez la valeur de la variable 'var1': " # L'option -n pour echo supprime le retour chariot. read var1 # Notez qu'il n'y a pas de '$' devant var1, car elle est en train d'être #+ initialisée. echo "var1 = $var1" echo # Une simple instruction 'read' peut initialiser plusieurs variables. echo -n "Entrez les valeurs des variables 'var2' et 'var3' (séparées par des espaces ou des tabulations): " read var2 var3 echo "var2 = $var2 var3 = $var3" # Si vous entrez seulement une valeur, le(s) autre(s) variable(s) resteront #+ non initialisées (null). exit 0
  • Un read sans variable associée utilise en entrée la valeur de la variable par dédiée $REPLY.
  • Qu'arrive-t'il quand read n'a pas de variable
    #!/bin/bash echo # -------------------------- # # Premier bloc de code. echo -n "Entrez une valeur: " read var echo "\"var\" = "$var"" # Tout se passe comme convenu. # -------------------------- # echo echo -n "Entrez une nouvelle valeur: " read # Aucune variable n'est donnée à 'read', donc... #+ L'entrée de 'read' est affectée à la variable par défaut, $REPLY. var="$REPLY" echo "\"var\" = "$var"" # Ceci est équivalent au premier bloc de code. echo exit 0
  • Normalement, entrer un
  • \
  • supprime le retour chariot lors de la saisie suite à un read. Avec l'option -r, un caractère
  • \
  • saisi sera interprété literalement.
  • Entrée à plusieurs lignes par read
    #!/bin/bash echo echo "Entrez une chaîne de caractères terminée par un \\, puis appuyez sur ENTER." echo "Ensuite, entrez une deuxième chaîne de caractères, et encore une fois appuyez sur ENTER." read var1 # Le "\" supprime le retour chariot, lors de la lecture de "var1". # première ligne \ # deuxième ligne echo "var1 = $var1" # var1 = première ligne deuxième ligne # Pour chaque ligne terminée par un "\", #+ vous obtenez une invite sur la ligne suivante pour continuer votre entrée #+ dans var1. echo; echo echo "Entrez une autre chaîne de caractères terminée par un \\ , puis appuyez sur ENTER." read -r var2 # L'option -r fait que le "\" est lu littéralement. # première ligne \ echo "var2 = $var2" # var2 = première ligne \ # L'entrée de données se termine avec le premier ENTER. echo exit 0
  • La commande read a quelques options intéressantes permettant d'afficher une invite et même de lire des frappes clavier sans appuyer sur ENTER.L'option -n pour read permet aussi la détection des flèches de direction et certaines des autres touches inhabituelles.
  • Détecter les flèches de direction
    #!/bin/bash # arrow-detect.sh: Détecte les flèches du clavier et quelques autres touches. # Merci, Sandro Magi, pour m'avoir montré comment faire. # -------------------------------------------- # Codes générés par l'appui sur les touches. flechehaut='\[A' flechebas='\[B' flechedroite='\[C' flechegauche='\[D' insert='\[2' delete='\[3' # -------------------------------------------- SUCCES=0 AUTRE=65 echo -n "Appuyer sur une touche... " # Il est possible qu'il faille appuyer aussi sur ENTER si une touche non gérée #+ ici est utilisée. read -n3 touche # Lit 3 caractères. echo -n "$touche" | grep "$flechehaut" # Vérifie si un code est détecté. if [ "$?" -eq $SUCCES ] then echo "Appui sur la touche flèche haut." exit $SUCCES fi echo -n "$touche" | grep "$flechebas" if [ "$?" -eq $SUCCES ] then echo "Appui sur la touche flèche bas." exit $SUCCES fi echo -n "$touche" | grep "$flechedroite" if [ "$?" -eq $SUCCES ] then echo "Appui sur la touche flèche droite." exit $SUCCES fi echo -n "$touche" | grep "$flechegauche" if [ "$?" -eq $SUCCES ] then echo "Appui sur la touche flèche gauche." exit $SUCCES fi echo -n "$touche" | grep "$insert" if [ "$?" -eq $SUCCES ] then echo "Appui sur la touche \"Insert\"." exit $SUCCES fi echo -n "$touche" | grep "$delete" if [ "$?" -eq $SUCCES ] then echo "Appui sur la touche \"Delete\"." exit $SUCCES fi echo "Autre touche." exit $AUTRE # Exercices: # --------- # 1) Simplifier ce script en ré-écrivant de multiples tests "if" en une #+ construction 'case'. # 2) Ajouter la détection des touches "Home", "End", "PgUp" et "PgDn".
  • L'option -t pour read permet des réponses dépendantes du temps (voir read avec délai).La commande read peut aussi "lire" l'entrée à partir d'un fichier redirigé vers stdin. Si le fichier contient plus d'une ligne, seule la première ligne est affectée à la variable. Si read a plus d'un paramètres, alors chacune des variables se voit assignée une suite de mots séparés par des espaces blancs. Attention!
  • Utiliser read avec la redirection de fichier
    #!/bin/bash read var1 fichier-donnees echo "var1 = $var1" # var1 initialisée avec la première ligne du fichier d'entrées "fichier-donnees" read var2 var3 fichier-donnees echo "var2 = $var2 var3 = $var3" # Notez le comportement non intuitif de "read" ici. # 1) Revient au début du fichier d'entrée. # 2) Chaque variable est maintenant initialisée avec une chaîne correspondante, # séparée par des espaces blancs, plutôt qu'avec une ligne complète de texte. # 3) La variable finale obtient le reste de la ligne. # 4) S'il existe plus de variables à initialiser que de chaînes terminées par un # un espace blanc sur la première ligne du fichier, alors les variables # supplémentaires restent vides. echo "------------------------------------------------" # Comment résoudre le problème ci-dessus avec une boucle: while read ligne do echo "$ligne" done fichier-donnees # Merci à Heiner Steven de nous l'avoir proposé. echo "------------------------------------------------" # Utilisez $IFS (variable comprenant le séparateur interne de fichier, soit #+ Internal File Separator) pour diviser une ligne d'entrée pour "read", si vous #+ ne voulez pas des espaces blancs par défaut. echo "Liste de tous les utilisateurs:" OIFS=$IFS; IFS=: # /etc/passwd utilise ":" comme séparateur de champ. while read nom motpasse uid gid nomcomplet ignore do echo "$nom ($nomcomplet)" done /etc/passwd # Redirection d'entrées/sorties. IFS=$OIFS # Restaure l'$IFS original. # Cette astuce vient aussi de Heiner Steven. # Initialiser la variable $IFS à l'intérieur même de la boucle élimine le #+ besoin d'enregistrer l'$IFS originale dans une variable temporaire. # Merci à Dim Segebart de nous l'avoir indiqué. echo "------------------------------------------------" echo "Liste de tous les utilisateurs:" while IFS=: read nom motpasse uid gid nomcomplet ignore do echo "$nom ($nomcomplet)" done /etc/passwd # Redirection d'entrées/sorties. echo echo "\$IFS vaut toujours $IFS" exit 0
  • Remarque:Envoyer la sortie d'un tube vers une commande read, en utilisant echo pour configurer les échouera. Remarque:Néanmoins, envoyer la sortie du tube d'un cat semble fonctionner.

  • cd
    La commande familière de changement de répertoire, cd, trouve son intérêt dans les scripts où l'exécution d'une commande requiert d'être dans un répertoire spécifié. [à partir de l'exemple précédemment cité d'Alan Cox]L'option -P (physique) pour cd fait qu'il ignore les liens symboliques. cd - change le répertoire par $OLDPWD, l'ancien répertoire.

  • pwd
    Print Working Directory (NdT: Affiche le répertoire courant). Cela donne le répertoire courant de l'utilisateur (ou du script) (voir Modifier le répertoire courant). L'effet est identique à la lecture de la varibale intégrée $PWD.

  • pushd
    Cette ensemble de commandes est un mécanisme pour enregistrer les répertoires de travail, un moyen pour revenir en arrière ou aller en avant suivant les répertoires d'une manière ordonnée. Une pile LIFO est utilisée pour conserver la trace des noms de répertoires. Des options permettent diverses manipulations sur la pile de répertoires.pushd nom-rep enregistre le chemin de nom-rep dans la pile de répertoires et change le répertoire courant par rep-dir popd supprime (enlève du haut) le chemin du dernier répertoire et, en même temps, change de répertoire courant par celui qui vient d'être récupéré dans la pile.dirs liste le contenu de la pile de répertoires (comparez ceci avec la variable $DIRSTACK). Une commande pushd ou popd fonctionnant va automatiquement appeler dirs.Les scripts requérant différents changements du répertoire courant sans coder en dur les changements de nom de répertoire peuvent faire un bon usage de ces commandes. Notez que la variable tableau implicite $DIRSTACK, accessible depuis un script, tient le contenu de la pile des répertoires.
  • Modifier le répertoire courant
    #!/bin/bash rep1=/usr/local rep2=/var/spool pushd $rep1 # Fera un 'dirs' automatiquement (liste la pile des répertoires sur stdout). echo "Maintenant dans le répertoire `pwd`." # Utilise les guillemets inverses # pour 'pwd'. # Maintenant, faisons certaines choses dans le répertoire 'rep1'. pushd $rep2 echo "Maintenant dans le répertoire `pwd`." # Maintenant, faisons certaines choses dans le répertoire 'rep2'. echo "L'entrée supérieure du tableau DIRSTACK est $DIRSTACK." popd echo "Maintenant revenu dans le répertoire `pwd`." # Maintenant, faisons certaines choses de plus dans le répertoire 'rep1'. popd echo "Maintenant revenu dans le répertoire original `pwd`." exit 0


  • let
    La commande let permet les opérations arithmétiques sur des variables. Dans la majorité des cas, il fonctionne comme une version simplifiée de expr.
  • Laisser let faire un peu d'arithmétique.
    #!/bin/bash echo let a=11 # Identique à 'a=11' let a=a+5 # Equivalent à let "a = a + 5" # (double guillemets et espaces pour le rendre plus lisible) echo "11 + 5 = $a" let "a <<= 3" # Equivalent à let "a = a << 3" echo "\"\$a\" (=16) décalé de 3 places = $a" let "a /= 4" # Equivalent à let "a = a / 4" echo "128 / 4 = $a" let "a -= 5" # Equivalent à let "a = a - 5" echo "32 - 5 = $a" let "a = a * 10" # Equivalent à let "a = a * 10" echo "27 * 10 = $a" let "a %= 8" # Equivalent à let "a = a % 8" echo "270 modulo 8 = $a (270 / 8 = 33, reste $a)" echo exit 0


  • eval
  • eval arg1 [arg2] ... [argN]
  • Transforme en commande les arguments de la liste (utile pour générer du code dans un script).
  • Montrer l'effet d'eval
    #!/bin/bash y=`eval ls -l` # Similaire à y=`ls -l` echo $y # mais les retours chariot sont supprimés parce que la variable # n'est pas entre guillemets. echo echo "$y" # Les retours chariot sont préservés lorsque la variable se # trouve entre guillemets. echo; echo y=`eval df` # Similaaire à y=`df` echo $y # mais les retours chariot ont été supprimés. # Quand LF n'est pas préservé, cela peut être plus simple d'analyser la sortie, #+ en utilisant des outils comme "awk". exit 0
  • Forcer une déconnexion
    #!/bin/bash y=`eval ps ax | sed -n '/ppp/p' | awk '{ print $1 }'` # Trouve le numéro de processus de 'ppp'. kill -9 $y # Le tue. # Les lignes ci-dessus peuvent être remplacées par # kill -9 `ps ax | awk '/ppp/ { print $1 }' chmod 666 /dev/ttyS3 # Faire un SIGKILL sur ppp modifie les droits sur le port série. # Les restaure à leur état initial. rm /var/lock/LCK..ttyS3 # Supprime le fichier de verrouillage du port série. exit 0
  • Une version de rot13
    #!/bin/bash # Une version de "rot13" utilisant 'eval'. # Comparez à l'exemple "rot13.sh". setvar_rot_13() # "rot13" scrambling { local nomvar=$1 valeurvar=$2 eval $nomvar='$(echo "$valeurvar" | tr a-z n-za-m)' } setvar_rot_13 var "foobar" # Lancez "foobar" avec rot13. echo $var # sbbone echo $var | tr a-z n-za-m # foobar # Revenu à la variable originale. # Exemple de Stephane Chazelas. exit 0
  • Rory Winston a apporté sa contribution en donnant un autre exemple de l'utilité de la commande eval.
  • Utiliser eval pour forcer une substitution de variable dans un script Perl
    In the Perl script "test.pl": ... my $WEBROOT = WEBROOT_PATH; ... To force variable substitution try: $export WEBROOT_PATH=/usr/local/webroot $sed 's/WEBROOT_PATH/$WEBROOT_PATH/' test.pl out But this just gives: my $WEBROOT = $WEBROOT_PATH; However: $export WEBROOT_PATH=/usr/local/webroot $eval sed 's/WEBROOT_PATH/$WEBROOT_PATH/' test.pl out # ==== That works fine, and gives the expected substitution: my $WEBROOT = /usr/local/webroot
  • Attention:La commande eval est risquée, et devrait normalement être évitée quand il existe une alternative raisonnable. Un
  • eval $COMMANDES
  • exécute le contenu de COMMANDES, qui pourrait contenir des surprises désagréables comme rm -rf *. Lancer eval sur un code inconnu écrit par des personnes inconnues vous fait prendre des risques importants.

  • set
    La commande set modifie la valeur de variables internes au script. Une utilisation est de basculer des options qui aident à déterminer le comportement du script. Une autre application est de réinitialiser les paramètres de position qu'un script voit en résultat d'une commande (
  • set `commande`
  • ). Le script peut ensuite analyser les champs de la sortie de la commande.
  • Utiliser set avec les paramètres de position
    #!/bin/bash # script "set-test" # Appeler ce script avec trois paramètres en ligne de commande, # par exemple, "./set-test un deux trois". echo echo "Paramètres de position avant set \`uname -a\` :" echo "Argument #1 = $1" echo "Argument #2 = $2" echo "Argument #3 = $3" set `uname -a` # Configure les paramètres de position par rapport à la sortie # de la commande `uname -a` echo $_ # inconnu # Drapeaux initialisés dans le script. echo "Paramètres de position après set \`uname -a\` :" # $1, $2, $3, etc. reinitialisés suivant le résultat de `uname -a` echo "Champ #1 de 'uname -a' = $1" echo "Champ #2 de 'uname -a' = $2" echo "Champ #3 de 'uname -a' = $3" echo --- echo $_ # --- echo exit 0
  • Invoquer set sans aucune option ou argument liste simplement toutes les variables d'environnement ainsi que d'autres variables qui ont été initialisées.
  • bash$ set AUTHORCOPY=/home/bozo/posts BASH=/bin/bash BASH_VERSION=$'2.05.8(1)-release' ... XAUTHORITY=/home/bozo/.Xauthority _=/etc/bashrc variable22=abc variable23=xzy
  • Utiliser set avec l'option -- affecte explicitement le contenu d'une variable aux paramètres de position. Quand aucune variable ne suit --, cela déconfigure les paramètres de positions.
  • Réaffecter les paramètres de position
    #!/bin/bash variable="un deux trois quatre cinq" set -- $variable # Initialise les paramètres de position suivant le contenu de "$variable". premier_param=$1 deuxieme_param=$2 shift; shift # Shift fait passer les deux premiers paramètres de position. params_restant="$*" echo echo "premier paramètre = $premier_param" # un echo "deuxième paramètre = $deuxieme_param" # deux echo "paramètres restants = $params_restant" # trois quatre cinq echo; echo # De nouveau. set -- $variable premier_param=$1 deuxieme_param=$2 echo "premier paramètre = $premier_param" # un echo "deuxième paramètre = $deuxieme_param" # deux # ====================================================== set -- # Désinitialise les paramètres de position si aucune variable n'est spécifiée. premier_param=$1 deuxieme_param=$2 echo "premier paramètre = $premier_param" # (valeur null) echo "deuxième paramètre = $deuxieme_param" # (valeur null) exit 0
  • Voir aussi Boucle for avec deux paramètres dans chaque élément de la [liste] et Utiliser getopt pour analyser les paramètres de la ligne de commande.

  • unset
    La commande unset supprime une variable shell, en y affectant réellement la valeur null. Notez que cette commande n'affecte pas les paramètres de position.
  • bash$ unset PATH bash$ echo $PATH bash$
  • Déconfigurer une variable
    #!/bin/bash # unset.sh: Dés-initialiser une variable. variable=hello # Initialisée. echo "variable = $variable" unset variable # Dés-initialisée. # Même effet que variable= echo "(unset) variable = $variable" # $variable est null. exit 0


  • export
    La commande export rend disponibles des variables aux processus fils du script ou shell en cours d'exécution. Malheureusement, il n'existe pas de moyen pour exporter des variables au processus père. Une utilisation importante de la commande export est dans les fichiers de démarrage, pour initialiser et rendre accessible les variables d'environnement aux processus utilisateur suivants.
  • Utiliser export pour passer une variable à un script awk embarqué
    #!/bin/bash # Encore une autre version du script "column totaler" (col-totaler.sh) # qui ajoute une colonne spécifiée (de nombres) dans le fichier cible. # Il utilise l'environnement pour passer une variable de script à 'awk'. ARGS=2 E_MAUVAISARGS=65 if [ $# -ne "$ARGS" ] # Vérifie le bon nombre d'arguments de la ligne de # commande. then echo "Usage: `basename $0` nomfichier numéro_colonne" exit $E_MAUVAISARGS fi nomfichier=$1 numero_colonne=$2 #===== Identique au script original, jusqu'à ce point =====# export numero_colonne # Exporte le numéro de colonne dans l'environnement, de façon à ce qu'il soit #+ disponible plus tard. # Début du script awk. # ------------------------------------------------ awk '{ total += $ENVIRON["numero_colonne"] } END { print total }' $nomfichier # ------------------------------------------------ # Fin du script awk. # Merci, Stephane Chazelas. exit 0
  • Astuce:Il est possible d'initialiser et d'exporter des variables lors de la même opération, en faisant export var1=xxx. Astuce:Néanmoins, comme l'a indiqué Greg Keraunen, dans certaines situations, ceci peut avoir un effet différent que d'initialiser une variable, puis de l'exporter. Astuce:
  • bash$ export var=(a b); echo ${var[0]} (a b) bash$ var=(a b); export var; echo ${var[0]} a


  • declare
    Les commandes declare et typeset spécifient et/ou restreignent les propriétés des variables.

  • readonly
    Identique à declare -r, configure une variable en lecture-seule, ou, du coup, la transforme en constante. Essayer de modifier la variable échoue avec un message d'erreur. C'est l'équivalent sheel du type const pour le langage C.

  • getopts
    Ce puissant outil analyse les arguments en ligne de commande passés au script. C'est l'équivalent Bash de la commande externe getopt et de la fonction getopt familière aux programmeurs C. Il permet de passer et de concaténer de nombreuses options. (24) et les arguments associés à un script (par exemple
  • scriptname -abc -e /usr/local
  • ).La construction getopts utilise deux variables implicites. $OPTIND est le pointeur de l'argument (OPTion INDex) et $OPTARG (OPTion ARGument) l'argument (optionnel) attaché à une option. Deux points suivant le nom de l'option lors de la déclaration marque cette option comme ayant un argument associé.Une construction getopts vient habituellement dans une boucle while, qui analyse les options et les arguments un à la fois, puis décrémente la variable implicite $OPTIND pour passer à la suivante. Remarque:Les arguments passés à la ligne de commande vers le script doivent être précédés par un tiret (-) ou par un plus (+). Le préfixe - ou + permet à getopts de reconnaitre les arguments en ligne de commande comme des options. En fait, getopts ne passera pas les arguments sans les préfixes - ou +, et terminera l'analyse des options au premier argument rencontré qui ne les aura pas. Le modèle getopts diffère légèrement de la boucle while standard, dans le fait qu'il manque les crochets de condition. La construction getopts remplace la commande getopt qui est obsolète et moins puissante.
  • Utiliser getopts pour lire les options/arguments passés à un script
    #!/bin/bash # 'getopts' analyse les arguments en ligne de commande du script. # Les arguments sont analysés comme des "options" (flags) et leur arguments #+ associés. # Essayez d'appeller ce script avec # 'nomscript -mn' # 'nomscript -oq qOption' (qOption peut être une chaîne de caractère arbitraire.) # 'nomscript -qXXX -r' # # 'nomscript -qr' - Résultat inattendu, prend "r" comme argument à l'option # "q" # 'nomscript -q -r' - Résultat inattendu, identique à ci-dessus # Si une option attend un argument ("flag:"), alors il récupèrera tout ce qui #+ se trouve ensuite sur la ligne de commandes. SANS_ARGS=0 E_ERREUROPTION=65 if [ $# -eq "$SANS_ARGS" ] # Script appelé sans argument? then echo "Usage: `basename $0` options (-mnopqrs)" exit $E_ERREUROPTION # Sort et explique l'usage, si aucun argument(s) # n'est donné. fi # Usage: nomscript -options # Note: tiret (-) nécessaire while getopts ":mnopq:rs" Option do case $Option in m ) echo "Scénario #1: option -m-";; n | o ) echo "Scénario #2: option -$Option-";; p ) echo "Scénario #3: option -p-";; q ) echo "Scénario #4: option -q-, avec argument \"$OPTARG\"";; # Notez que l'option 'q' doit avoir un argument associé, # sinon il aura la valeur par défaut. r | s ) echo "Scénario #5: option -$Option-"'';; * ) echo "Option non implémentée.";; # DEFAULT esac done shift $(($OPTIND - 1)) # Décrémente le pointeur d'argument de façon à ce qu'il pointe vers le prochain. exit 0


  • source
    Cette commande, lorsqu'elle est appelée à partir de la ligne de commande, exécute un script. A l'intérieur d'un script, un
  • source nom-fichier
  • charge le fichier nom-fichier. C'est le script équivalent à la directive C/C++
  • #include
  • . Elle est utile dans certaines situations où de nombreux scripts utilisent un fichier commun de données ou une bibliothèque de fonctions.
  • Inclure un fichier de données
    #!/bin/bash . fichier-donnees # charge un fichier de données. # Même effet que "source fichier-donnees", mais plus portable. # Le fichier "fichier-donnees" doit être présent dans le répertoire courant, #+ car il est référencé par rapport à son 'basename'. # Maintenant, référençons quelques données à partir de ce fichier. echo "variable1 (de fichier-donnees) = $variable1" echo "variable3 (de fichier-donnees) = $variable3" let "sum = $variable2 + $variable4" echo "Somme de variable2 + variable4 (de fichier-donnees) = $sum" echo "message1 (de fichier-donnees) est \"$message1\"" # Note: guillemets échappés print_message Ceci est la fonction message-print de fichier-donnees. exit 0
  • Il est même possible pour un script de se sourcer lui-même, bien qu'il ne semble pas que cela ait la moindre application pratique.
  • Un script (inutile) qui se charge lui-même
    #!/bin/bash # self-source.sh: un script qui s'exécute lui-même "récursivement." # De "Stupid Script Tricks", Volume II. NBTOURSMAX=100 # Nombre maximal de tours d'exécution. echo -n "$nb_tour " # Lors du premier tour, ceci va juste afficher deux espaces car $nb_tour n'est #+ toujours pas initialisé. let "nb_tour += 1" # Suppose que la variable non initialisée $nb_tour peut être incrémentée la #+ première fois. # Ceci fonctionne avec Bash et pdksh, mais cela repose sur un comportement #+ non portable (et certainement dangereux). # Il serait mieux d'initialiser $nb_tour à 0 si il n'est pas initialisé. while [ "$nb_tour" -le $NBTOURSMAX ] do . $0 # Le script "s'inclut" lui-même, plutôt que de s'appeler. # ./$0 (qui serait une vraie récursion) ne fonctionne pas ici. done # Ce qui arrive ici n'est pas réellement une récursion, car le script #+ s'étend effectivement lui-même (cela génère une nouvelle section de code) #+ à chaque tour de la boucle 'while' lors du 'source' en ligne 20. # # Bien sûr, le script interprète chaque nouvelle ligne incluse "#!" comme un #+ commentaire, et non pas comme le début d'un nouveau script. echo exit 0 # L'effet très net est le comptage de 1 à 100. # Très impressionnant. # Exercice: # -------- # Ecrire un script qui utilise cette astuce pour faire quelque chose d'utile.


  • exit
    Termine un script sans condition. La commande exit peut prendre de façon optionnelle un argument de type entier, qui est renvoyé au script en tant qu'état de sortie du script. C'est une bonne pratique de terminer tous les scripts, sauf les plus simples, avec un
  • exit 0
  • , indiquant un succès. Remarque:Si un script se termine avec un exit sans argument, l'état de sortie est le statut de exit lors de son dernier lancement dans le script, sans compter le exit.

  • exec
    Cette commande shell intégrée remplace le processus courant avec une commande spécifiée. Normalement, lorsque le shell rencontre une commande, il lance un processus fils pour exécuter la commande. En utilisant la commande intégrée exec, le shell n'exécute aucun processus fils et la commande bénéficiant du exec remplace purement et simplement le shell. Lorsqu'elle est utilisée dans un script, cela force une sortie (exit) à partir du script lorsque la commande bénéficiant du exec se termine. Pour cette raison, si un exec apparait dans un script, cela sera probablement la dernière commande.
  • Effets d'exec
    #!/bin/bash exec echo "Je sors \"$0\"." # Sortie du script ici. # ---------------------------------- # Les lignes suivantes ne s'exécutent jamais. echo "Cet echo ne sera jamais exécuté." exit 99 # Ce script ne sortira jamais par ici. # Vérifier le code de sortie après l'exécution du #+ du script avec un 'echo $?'. # Cela ne sera *pas* 99.
  • Un script lançant exec sur lui-même
    #!/bin/bash # self-exec.sh echo echo "Cette ligne apparaît UNE FOIS dans le script, cependant elle continue à s'afficher." echo "Le PID de cette instance du script est toujours $$." # Démontre qu'un sous-shell n'est pas un processus fils. echo "==================== Tapez Ctl-C pour sortir ====================" sleep 1 exec $0 # Lance une autre instance du même script remplaçant le précédent. echo "Cette ligne ne s'affichera jamais!" # Pourquoi pas? exit 0
  • Un exec sert aussi à réaffecter les descripteurs de fichiers.
  • exec fichier-zzz
  • remplace stdin par le fichier fichier-zzz (voir Rediriger stdin en utilisant exec). Remarque:L'option -exec pour find n'est pas du tout la même chose que la commande shell intégrée exec.

  • shopt
    Cette commande permet de changer les options du shell au vol (voir Alias à l'intérieur d'un script et unalias: Configurer et supprimer un alias). Elle apparait souvent dans les fichiers de démarrage de Bash, mais a aussi son utilité dans des scripts. Il est nécessaire de disposer de la version 2, ou ultérieurs, de Bash.

  • true
    Une commande qui renvoie un succès (zéro) comme état de sortie, mais ne fait rien d'autre.

  • false
    Une commande qui renvoit un état de sortie correspondant à un échec, mais ne fait rien d'autre.

  • type [cmd]
    Identique à la commande externe which, type cmd donne le chemin complet vers "cmd" . Contrairement à which, type est une commande intégrée à Bash. L'option -a est très utile pour que type identifie des mots clés et des commandes internes, et localise aussi les commandes système de nom identiques.
  • bash$ type '[' [ is a shell builtin bash$ type -a '[' [ is a shell builtin [ is /usr/bin/[


  • hash [cmds]
    Enregistre le chemin des commandes spécifiées (dans une table de hachage du shell), donc le shell ou le script n'aura pas besoin de chercher le $PATH sur les appels futurs à ces commandes. Quand hash est appelé sans arguments, il liste simplement les commandes qui ont été hachées. L'option -r réinitialise la table de hachage.

  • help
    help COMMANDE cherche un petit résumé sur l'utilisation de la commande COMMANDE intégrée au shell. C'est l'équivalent de whatis, pour les commandes intégrées.
  • bash$ help exit exit: exit [n] Exit the shell with a status of N. If N is omitted, the exit status is that of the last command executed.



Commandes de contrôle des jobs

Certaines des commandes de contrôle de jobs prennent en argument un "identifiant de job (job identifier)" Voir la table à la fin de ce chapitre.

  • jobs
    Liste les jobs lançés en tâche de fond, en indiquant le numéro de job. Pas aussi utile que ps. Remarque:Il est trop facile de confondre les jobs et les processus. Certaines commandes intégrées, telles que kill, disown et wait acceptent soit un numéro de job soit un numéro de processus comme argument. Les commandes fg, bg et jobs acceptent seulement un numéro de job. Remarque:
  • bash$ sleep 100 & [1] 1384 bash $ jobs [1]+ Running sleep 100 &
  • Remarque: "1" est le numéro de job (les jobs sont maintenus par le shell courant), et "1384" est le numéro de processus (les processus sont maintenus par le système). Pour tuer ce job/processus, faites soit un kill %1 soit un kill 1384. Remarque:Merci, S.C.

  • disown
    Supprime le(s) job(s) de la table du shell des jobs actifs.

  • fg
    La commande fg fait basculer un job, qui tournait en tâche de fond, en avant-plan. La commande bg relance un job suspendu en tâche de fond. Si aucun numéro de job n'est spécifié, alors la commande fg ou bg agit sur le job en cours d'exécution.

  • wait
    Arrête l'exécution du script jusqu'à ce que tous les jobs en tâche de fond aient terminé, ou jusqu'à ce que le numéro de job ou l'idendifiant de processus spécifié en option termine. Retourne l'état de sortie de la commande attendue.Vous pouvez utiliser la commande wait pour empêcher un script de se terminer avant qu'un job en arrière-plan ne finisse son exécution (ceci créerait un processus orphelin).
  • Attendre la fin d'un processus avant de continuer
    #!/bin/bash ROOT_UID=0 # Seuls les utilisateurs ayant $UID 0 ont les privilèges de root # root. E_NONROOT=65 E_SANSPARAM=66 if [ "$UID" -ne "$ROOT_UID" ] then echo "Vous devez être root pour exécuter ce script." # "Passe ton chemin gamin, il est temps d'aller au lit." exit $E_NONROOT fi if [ -z "$1" ] then echo "Usage: `basename $0` chaine-find" exit $E_SANSPARAM fi echo "Mise à jour de la base 'locate'..." echo "Ceci peut prendre du temps." updatedb /usr & # Doit être lancé en tant que root. wait # Ne pas lancez le reste du script jusqu'à ce que 'updatedb' finisse. # La base de données doit être mise à jour avant de chercher quelque chose. locate $1 # Sans la commande wait, avec le pire scénario, le script sortirait alors que # 'updatedb' serait toujours en cours d'exécution, le laissant orphelin. exit 0
  • Optionnellement, wait peut prendre un identifiant de job en tant qu'argument, par exemple, wait%1 ou wait $PPID. Voir la table des identifiants de job. Astuce:A l'intérieur d'un script, lancer une commande en arrière-plan avec un "et commercial" () peut faire que le script se bloque jusqu'à un appui sur la touche ENTER. Ceci semble arriver avec les commandes qui écrivent sur stdout. Cela peut être un gros problème.
  • bash$ ./test.sh Terminé. [bozo@localhost test-scripts]$ total 1 -rwxr-xr-x 1 bozo bozo 34 Oct 11 15:09 test.sh _
  • Astuce:Placer un wait après la commande de tâche de fond semble remédier à ceci.
  • bash$ ./test.sh Terminé. [bozo@localhost test-scripts]$ total 1 -rwxr-xr-x 1 bozo bozo 34 Oct 11 15:09 test.sh
  • Rediriger la sortie de la commande d'un fichier ou même de /dev/null permet aussi d'éviter ce problème.

  • suspend
    Ceci a un effet similaire à ControleZ, mais cela suspend le shell (le processus père du shell devrait le relancer à un moment approprié).

  • logout
    Sort d'un login shell, quelque fois en spécifiant un état de sortie.

  • times
    Donne des statistiques sur le temps système utilisé pour l'exécution des commandes, de la façon suivante:
  • 0m0.020s 0m0.020s
  • Cette fonctionnalité est d'une valeur très limitée, car il est peu commun de profiler et de tester la rapidité des scripts shells.

  • kill
    Force la fin d'un processus en lui envoyant le signal de terminaison approprié (voir pidof aide à la suppression d'un processus).
  • Un script qui se tue lui-même
    #!/bin/bash # self-destruct.sh kill $$ # Le script tue son propre processus ici. # Rappelez-vous que "$$" est le PID du script. echo "Cette ligne ne s'affichera pas." # A la place, le shell envoie un message "Terminated" sur stdout. exit 0 # Après que le script se soit terminé prématurément, #+ quel code de sortie retourne-t'il? # # sh self-destruct.sh # echo $? # 143 # # 143 = 128 + 15 # signal TERM
  • Remarque:
  • kill -l
  • liste tous les signaux. Un
  • kill -9
  • est une "mort certaine" , qui terminera un processus qui obstinément refuse de mourir avec un simple kill. Quelque fois, un
  • kill -15
  • fonctionne. Un "processus zombie" , c'est-à-dire un processus dont le père est mort, ne peut être tué (vous ne pouvez pas tuer quelque chose qui est déjà mort), mais init nettoiera habituellement cela plus ou moins tôt.

  • command
    La directive command COMMANDE désactive les alias et les fonctions pour la commande "COMMANDE" . Remarque:C'est une des trois directives qui effectuent le traitement de commandes de script. Les autres sont des commandes intégrées et activées.

  • builtin
    Appeler builtin COMMANDE_INTEGREE lance la commande "COMMANDE_INTEGREE" en tant que commande intégrée du shell, désactivant temporairement à la fois les fonctions et les commandes externes du système disposant du même nom.

  • enable
    Ceci active ou désactive une commande intégrée du shell. Comme exemple, enable -n kill désactive la commande intégrée kill, de façon à ce que, quand Bash rencontre kill, il appelle /bin/kill.L'option -a d'enable liste toutes les commandes intégrées du shell, indiquant si elles sont ou non activées. L'option -f nomfichier permet à enable de charger une commande intégrée en tant que module de bibliothèque partagée (DLL) à partir d'un fichier objet correctment compilé. (25).

  • autoload
    Ceci est un port pour Bash du chargeur automatique de ksh. Avec autoload en place, une fonction avec une déclaration "autoload" chargera à partir d'un fichier externe à sa première invocation. (26) Ceci sauve des ressources système.Notez qu'autoload ne fait pas partie de l'installation de base de Bash. Il a besoin d'être chargé avec enable -f (voir ci-dessus).

Notation Signification
Numéro de job [N]
Appel (ligne de commande) de jobs commençant par la chaîne de caractères
Appel (ligne de commande) of jobs contenant en soi la chaîne de caractères
Job "" (dernier job arrêté en avant-plan ou lancé en tâche de fond)
Job "" (dernier job arrêté en avant-plan ou lancé en tâche de fond)
Dernier job
Dernier processus en tâche de fond

3.3. Filtres externes, programmes et commandes

Les commandes UNIX standards rendent les scripts shell plus polyvalents. La puissance des scripts provient du mélange de commandes systèmes et de directives shell avec des structures de programmation simples.


Les commandes basiques

  • ls
    La commande élémentaire de "listage" du contenu d'un répertoire. Il est très facile d'en sous-estimer la puissance. Par exemple, en utilisant -R, option de récursivité, ls affiche une structure de répertoire sous la forme d'une arborescence. D'autres options dignes d'intêret sont -S, qui trie selon la taille du fichier, -t, qui trie selon la date de modification des fichiers, et -i, qui affiche les inodes des fichiers (voir Effacer un fichier par son numéro d'inode).
  • Utilisation de ls pour créer une liste de fichiers à graver sur un CDR
    #!/bin/bash # burn-cd.sh # Script d'automatisation de gravure de CD. SPEED=2 # Peut être plus élevée si votre graveur en est capable. IMAGEFILE=cdimage.iso CONTENTSFILE=contents DEFAULTDIR=/opt # C'est le répertoire contenant les fichiers à graver. # Soyez sûr qu'il existe bien. # Utilise le package "cdrecord" de Joerg Schilling. # (http://www.fokus.gmd.de/nthp/employees/schilling/cdrecord.html) # Si ce script est lancé par un utilisateur normal, alors il faut faire un suid sur cdrecord #+ (chmod u+s /usr/bin/cdrecord, en tant que root). if [ -z "$1" ] then IMAGE_DIRECTORY=$DEFAULTDIR # Le répertoire par défaut, si non défini par la ligne de commande. else IMAGE_DIRECTORY=$1 fi # Créer un fichier "sommaire". ls -lRF $IMAGE_DIRECTORY > $IMAGE_DIRECTORY/$CONTENTSFILE # L'option "l" donne un "long" listing de fichier. # L'option "R" rend le listing récursif. # L'option "F" marque le type des fichiers (les répertoires se voient ajouter un / final). echo "Sommaire en cours de création." # Créer un fichier image avant de le graver sur CD. mkisofs -r -o $IMAGFILE $IMAGE_DIRECTORY echo "Image ISO9660 ($IMAGEFILE) en cours de création." # Grave le CD. cdrecord -v -isosize speed=$SPEED dev=0,0 $IMAGEFILE echo "Gravure du CD." echo "Veuillez patientez." exit 0


  • cat
    cat, un acronyme de concatenate (NdT: concaténer en français), affiche le contenu d'un fichier sur stdout. Lorsqu'il est combiné avec une redirection (> ou ), il est couramment utilisé pour concaténer des fichiers. L'option -n de cat insère, avant chaque début de ligne, un numéro de ligne dans le(s) fichier(s) cible(s). L'option -b sert à numéroter uniquement les lignes qui ne sont pas blanches. L'option -v montre les caractères non imprimables en utilisant la notation ^. L'option -s n'affiche qu'une seule ligne blanche lors de multiples lignes blanches consécutives.Voir aussi nl: Un script d'autonumérotation. et rot13: rot13, cryptage ultra-faible.. tac, le contraire de cat, affiche le contenu d'un fichier en commençant par sa fin.

  • rev
    Inverse chaque ligne d'un fichier et affiche le résultat vers stdout. Le résultat est différent d'une utilisation de tac, dans le sens où rev conserve l'ordre des lignes, mais "retourne" chacune d'elle.
  • bash$ cat fichier1.txt Coucou, je suis la ligne 1. Coucou, je suis la ligne 2. bash$ tac fichier1.txt Coucou, je suis la ligne 2. Coucou, je suis la ligne 1. bash$ rev fichier1.txt .1 engil al sius ej ,uocuoC .2 engil al sius ej ,uocuoC


  • cp
    Il s'agit de la commande de copie de fichier.
  • cp fichier1 fichier2
  • copie fichier1 dans fichier2 et écrase fichier2 s'il existait auparavant (voir copyrep, copier les fichiers du répertoire courant vers un autre en utilisant xargs). Astuce:Les options -a, pour option d'archive (pour copier une arborescence entière de répertoire), -r et -R pour option de récursivité sont particulièrement utiles.

  • mv
    Il s'agit de la commande de déplacement (move). Elle est équivalente à une combinaison des commandes cp et rm. Elle peut être utilisée pour déplacer plusieurs fichiers vers un répertoire ou même pour renommer un répertoire. Pour des exemples d'utilisation dans un script, voir Renommer des extensions de fichiers: et rn: Un utilitaire simple pour renommer des fichiers. Remarque:Lors de l'utilisation de mv dans un script, on doit ajouter l'option -f (forcer) pour empêcher toute interaction avec l'utilisateur. Remarque:Quand un répertoire est déplacé vers un répertoire déjà existant, il devient un sous-répertoire du répertoire existant. Remarque:
  • bash$ mv rep_source rep_cible bash$ ls -lF rep_cible total 1 drwxrwxr-x 2 bozo bozo 1024 nov 21 23:30 rep_source/


  • rm
    Efface, supprime ( "remove" en anglais) un ou plusieurs fichiers. L'option -f force même la suppression de fichiers en lecture seule et est utile pour ignorer toute interaction de l'utilisateur durant son exécution dans un script.Lorsqu'elle est exécutée avec l'option de récursivité (NdT: en anglais, "recursive flag" ) -r, cette commande efface les fichiers de tous les sous-répertoires de l'arborescence.

  • rmdir
    Efface un répertoire ( "remove directory" en anglais). Il est nécessaire que le répertoire soit vide de tout fichier, ce qui inclut les fichiers invisibles (NdT: en anglais, les "dotfiles" ), (27) pour que cette commande s'exécute correctemment.

  • mkdir
    Crée un répertoire (NdT: "make directory" en anglais).
  • mkdir -p projet/programmes/Decembre
  • crée le répertoire indiqué. L'option -p s'occupe, au besoin, de la création des répertoires parents automatiquement.

  • chmod
    Change les attributs d'un fichier existant (voir Forcer une déconnexion).

  • chattr
    Change les attributs de fichier (NdT: "change file attributes" en anglais). Elle agit de la même manière que chmod mais avec une syntaxe d'invocation différente et ne fonctionne que sur les systèmes de fichiers ext2.

  • ln
    Crée des liens vers des fichiers déjà existants. Cette commande est utilisée la plupart du temps avec l'option -s, pour lien symbolique ou "soft" . Cela permet de référencer le fichier lié par plus d'un nom et est une alternative supérieure au système d'alias (voir wh, recherche d'un nom de domaine avec whois).
  • ln -s ancien_fichier nouveau_fichier
  • lie le fichier ancien_fichier au lien nouvellement créé, nouveau_fichier.

  • man
    Ces commandes accèdent aux pages de manuel et d'information relatives aux commandes systèmes et autres utilitaires installés sur la machine. Les pages info, si disponibles, contiennent habituellement des descriptions bien plus détaillées que celles des pages man.


Commandes complexes

  • find
    -exec COMMANDE \;Effectue COMMANDE sur chaque fichier trouvé par find. La séquence de commandes se termine par un \; (le ";" est échappé pour être certain que le shell le passe de façon litéralle à find). Si COMMANDE contient {}, alors find substitue le chemin complet du fichier en cours à "{}" .
  • bash$ find ~/ -name '*.txt' /home/bozo/.kde/share/apps/karm/karmdata.txt /home/bozo/misc/irmeyc.txt /home/bozo/test-scripts/1.txt
  • Remarque:L'option -exec de find ne doit pas être confondue avec la commande intégrée du shellexec.
  • incorrectname élimine dans le répertoire courant les fichiers dont le nom contient des caractères incorrects et des espaces blancs.
    #!/bin/bash # Efface les fichiers du répertoire courant contenant des mauvais caractères. for nomfichier in * do mauvaisnom=`echo "$nomfichier" | sed -n /[\+\{\;\"\\\=\?~\(\)\<\>\&\*\|\$]/p` # Les fichiers contenant ces méchants: + { ; " \ = ? ~ ( ) < > & * | $ rm $mauvaisnom 2>/dev/null # Les erreurs sont effacées. done # Maintenant, faire attention aux noms de fichiers contenant des espaces blancs. find . -name "* *" -exec rm -f {} \; # Le chemin du fichier trouvé par "find" remplace "{}". # Le '\' nous assure que le ';' est interprété littéralement, en tant que fin #+ de commande. exit 0 #--------------------------------------------------------------------- # Les commandes ci-dessous ne seront pas exécutées à cause de la commande # "exit" au dessus. # Voici une alternative au script ci-dessus: find . -name '*[+{;"\\=?~()&*|$ ]*' -exec rm -f '{}' \; exit 0 # (Merci, S.C.)
  • Effacer un fichier par son numéro d'inode
    #!/bin/bash # idelete.sh: Effacer un fichier grâce à son inode. # C'est très utile quand un nom de fichier commence avec un caractère illégal, #+ comme un ? ou -. NBARGS=1 # L'argument du nom de fichier doit être passé au script. E_MAUVAISARGS=70 E_FICHIER_INEXISTANT=71 E_CHANGE_D_ESPRIT=72 if [ $# -ne "$NBARGS" ] then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi if [ ! -e "$1" ] then echo "Le fichier \""$1"\" n'existe pas." exit $E_FICHIER_INEXISTANT fi inum=`ls -i | grep "$1" | awk '{print $1}'` # inum = inode (NdT; index node) numéro de fichier # Chaque fichier possède un inode, qui contient ses informations d'adresses #+ physiques. echo; echo -n "Effacer vraiment \"$1\" (o/n)? " # L'option '-v' de 'rm' pose la même question. read reponse case "$reponse" in [nN]) echo "Vous avez changé d'avis" exit $E_CHANGE_D_ESPRIT ;; *) echo "Effacement en cours du \"$1\".";; esac find . -inum $inum -exec rm {} \; echo "Fichier "\"$1"\" effacé!" exit 0
  • Voir Utiliser cpio pour déplacer un répertoire complet, Sauvegarde de tous les fichiers modifiés dans les dernières 24 heures et Rechercher les auteurs de tous les binaires d'un répertoire pour des exemples de scripts utilisant find. La page de manuel de cette commande, complexe et puissante, apporte des détails supplémentaires.

  • xargs
    Un filtre qui sert à passer des paramètres à une commande, et aussi un outil pour réunir les commandes elles-mêmes. Il découpe un flux de données en des morceaux suffisament petits pour laisser les filtres et les commandes opérer. Considérez-le comme une puissante alternative aux guillemets inversés. Dans les situations où les guillemets inversés échouent avec une erreur "too many arguments" (trop d'arguments), substituer xargs règle souvent les problèmes. Habituellement, xargs lit depuis stdin ou depuis un tube mais il accèpte aussi de lire dans la sortie d'un fichier.La commande par défaut d'xargs est echo. Cela signifie que tout flux entrant transmis via un tube vers xargs peut voir ses sauts de ligne et caractères d'espacements supprimés.
  • bash$ ls -l total 0 -rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 fichier1 -rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 fichier2 bash$ ls -l | xargs total 0 -rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 fichier1 -rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 fichier2
  • ls | xargs -p -l gzip
  • Compresse avec gzip tous les fichiers du répertoire courant, un par un, et demande confirmation avant chaque opération. Astuce:Une option d'xargs intéressante est -n NN , qui limite à NN le nombre d'arguments passés. Astuce:
  • ls | xargs -n 8 echo
  • affiche le contenu du répertoire courant sur 8 colonnes. Astuce:Une autre option utile est -0, combinée avec find -print0 ou grep -lZ. Ceci permet de manipuler les arguments contenant des espaces ou des quotes. Astuce:
  • find / -type f -print0 | xargs -0 grep -liwZ GUI | xargs -0 rm -f
  • Astuce:
  • grep -rliwZ GUI / | xargs -0 rm -f
  • Astuce:N'importe laquelle des commande ci-dessus effacera tout fichier contenant "GUI" . (Merci, S.C.)
  • Fichier de traces utilisant xargs pour surveiller les logs système
    #!/bin/bash # Génère un fichier de traces dans le répertoire courant à partir de la fin de # /var/log/messages. # Note: /var/log/messages doit être lisible par tout le monde si le script # est appelé par un utilisateur simple. # #root chmod 644 /var/log/messages LIGNES=5 ( date; uname -a ) >>fichiertraces # Time and machine name echo --------------------------------------------------------------------- >>fichiertraces tail -$LIGNES /var/log/messages | xargs | fmt -s >>fichiertraces echo >>fichiertraces echo >>fichiertraces exit 0
  • copyrep, copier les fichiers du répertoire courant vers un autre en utilisant xargs
    #!/bin/bash # Copie verbeusement tous les fichiers du répertoire courant # dans le répertoire spécifié sur la ligne de commande if [ -z "$1" ] # Quitte si aucun paramètre n'est fourni. then echo "Usage: `basename $0` rep-destination" exit 65 fi ls . | xargs -i -t cp ./{} $1 # C'est la même chose que # cp * $1 # sauf si les fichiers ont des espaces blancs dans leur nom exit 0


  • expr
    Evaluateur d'expression : Concatène et évalue les arguments suivant l'opération souhaitée (les arguments doivent être séparés par des espaces). Les opérations peuvent être arithmétiques, comparatives, chaîne de caractères ou logiques.expr 3 + 5
    renvoie 8

    expr 5 % 3
    renvoie 2

    expr 5 \* 3
    renvoie 15L'opérateur de multiplication doit être échappé lorsqu'il est utilisé dans une expression arithmétique avec expr.

    y=`expr $y + 1`
    Incrémente une variable, de la même manière que let y=y+1 et y=$(($y+1)). Ceci est un exemple d'expansion arithmétique.

    z=`expr substr $chaine $position $longueur`
    Extrait une sous-chaîne de caractères de $longueur caractères, en partant de $position.

  • Utiliser expr
    #!/bin/bash # Démonstration des possibilités de 'expr' # ======================================== echo # Opérations Arithmétiques # ------------------------ echo "Opérations Arithmétique" echo a=`expr 5 + 3` echo "5 + 3 = $a" a=`expr $a + 1` echo echo "a + 1 = $a" echo "(incrémentation d'une variable)" a=`expr 5 % 3` # modulo echo echo "5 mod 3 = $a" echo echo # Opérations Logiques # ------------------- # Retourne 1 si vrai, 0 si faux, #+ à l'opposé des conventions de Bash echo "Opérations Logiques" echo x=24 y=25 b=`expr $x = $y` # Test d'égalité. echo "b = $b" # 0 ( $x -ne $y ) echo a=3 b=`expr $a \> 10` echo 'b=`expr $a \> 10`, donc...' echo "If a > 10, b = 0 (faux)" echo "b = $b" # 0 ( 3 ! -gt 10 ) echo b=`expr $a \< 10` echo "If a < 10, b = 1 (vrai)" echo "b = $b" # 1 ( 3 -lt 10 ) echo # Notez l'échappement des opérations. b=`expr $a \<= 3` echo "If a <= 3, b = 1 (vrai)" echo "b = $b" # 1 ( 3 -le 3 ) # Il y a aussi l'opérande "\>=" (plus grand que ou égal à). echo echo # Opérateurs de comparaison # ------------------------- echo "Opérateurs de comparaison" echo a=zipper echo "a is $a" if [ `expr $a = snap` ] # Force la re-évaluation de la variable 'a' then echo "a ne vaut pas 'zipper'" fi echo echo # Opérateur de chaine de caractères # --------------------------------- echo "Opérateur de chaînes de caractères" echo a=1234zipper43231 echo "Voici \"$a\"." # length: longueur de la chaine b=`expr length $a` echo "La taille de \"$a\" est $b." # index: position du premier caractère dans une sous-chaine # qui correspond à un caractère dans la chaine. b=`expr index $a 23` echo "La position du premier \"2\" dans \"$a\" est \"$b\"." # substr: extrait une sous-chaîne, en spécifiant la position de départ et la #+ taille b=`expr substr $a 2 6` echo "sous-chaîne de \"$a\", commençant à la position 2,\ et long de 6 caractère est \"$b\"." # L'attitude par défaut de l'opérateur 'match' est de #+ chercher une occurence à partir ***du début*** de la chaîne. # # utilisation des expressions régulières b=`expr match "$a" '[0-9]*'` # Comptage numérique. echo Le nombre de chiffres au début de \"$a\" est $b. b=`expr match "$a" '\([0-9]*\)'` # Notez que les parenthèses échappées # == == + déclenchent une reconnaissance de sous-chaîne. echo "Les chiffres au début de \"$a\" sont \"$b\"." echo exit 0
  • IMPORTANT:L'opérateur : est équivalent à match. Par exemple,
  • b=`expr $a : [0-9]*`
  • est l'équivalent exact de
  • b=`expr match $a [0-9]*`
  • du listing précédent. IMPORTANT:

Cet exemple illustre comment expr utilise les opérateurs groupant appelés parenthèses echappées -- \( ... \) -- en tandem avec une analyse basée sur les expressions régulières pour faire coïncider une sous-chaîne de caractères.

Perl, sed et awk ont des capacités d'analyse de chaînes de caractères très largement supérieures. Une petite sous-routine sed ou awk dans un script (voir ) est aussi une bonne alternative à expr.

Voir pour en savoir plus sur les manipulations des chaînes de caractères.


Commandes de date et d'heure

  • date
    Exécutée telle quelle, date affiche la date et l'heure sur stdout. Cette commande devient intéressante grâce à ses options de présentation et d'analyse.
  • Utiliser date
    #!/bin/bash # S'exercer avec la commande 'date' echo "Le nombre de jours écoulés depuis le début de l'année `date +%j`." # On a besoin d'un '+' au début pour demander un formatage correct. # %j donne le jour de l'année. echo "Le nombre de secondes écoulées depuis 01/01/1970 est `date +%s`." # %s affiche le nombre de secondes écoulées depuis le début de l'époque UNIX, #+ mais quelle est son utilité? prefixe=temp suffixe=`eval date +%s` # L'option "+%s" de 'date' est spécifique à GNU. nomfichier=$prefixe.$suffixe echo $nomfichier # C'est intéressant pour créer des noms de fichiers temporaires "uniques", #+ voire mieux que d'utiliser $$. # Voir la page de manuel de 'date' pour plus d'informations sur ses #+ possibilités de présentation. exit 0
  • L'option -u renvoie la date au format UTC (Temps Universel Coordonné).
  • bash$ date Fri Mar 29 21:07:39 MST 2002 bash$ date -u Sat Mar 30 04:07:42 UTC 2002


  • zdump
    Affiche la date dans le fuseau horaire spécifié.
  • bash$ zdump EST EST Mar Sep 18 22:09:22 2001 EST


  • time
    Renvoie des statistiques très détaillées sur le temps d'exécution d'une commande.
  • time ls -l /
  • va donner quelque chose d'équivalent à ceci :
  • 0.00user 0.01system 0:00.05elapsed 16%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (149major+27minor)pagefaults 0swaps
  • Voir aussi la commande similaire times de la section précédente. Remarque:A partir de la version 2.0 de Bash, time est devenu un mot réservé du shell, avec un comportement très légèrement différent dans un tube.

  • touch
    Utilitaire de mise à jour de la date d'accès/modification d'un fichier à partir de la date système courante ou d'une date spécifiée mais aussi utile pour créer un nouveau fichier. La commande
  • touch zzz
  • créera un nouveau fichier zzz de taille nulle, en admettant bien entendu que zzz n'existait pas auparavant. Marquer de cette façon des fichiers vides est utile pour stocker la date, par exemple pour garder trace des modifications de date sur un projet. Remarque:La commande touch est équivalent à
  • : nouveaufichier
  • ou
  • nouveaufichier
  • (pour des fichiers ordinaires).

  • at
    La commande de contrôle de job at exécute une liste de commandes données à l'heure souhaitée. A première vue, at ressemble à crond. Cependant, at sert surtout à exécuter d'un coup une liste de commandes.
  • at 2pm January 15
  • demande une liste de commandes à exécuter à cette heure précise. Ces commandes devraient être compatibles à un script shell car, en fin de compte, l'utilisateur écrit dans un script shell exécutable une ligne à la fois. L'entrée se termine avec un Ctrl-D.En utilisant le paramètre -f ou la redirection d'entrée (), at lit une liste de commandes depuis un fichier. Ce fichier est un script shell exécutable, bien qu'il devrait être non-interactif. Il est particulièrement malin d'inclure la commande run-parts dans le fichier pour exécuter un différent jeu de scripts.
  • bash$ at 2:30 am Friday at-jobs.liste job 2 at 2000-10-27 02:30


  • batch
    La commande de contrôle de job batch est similaire à at, mais elle exécute une liste de commande quand la charge système tombe en dessous de .8. Comme at, elle peut lire les commandes depuis un fichier avec le paramètre -f.

  • cal
    Affiche un calendrier mensuel correctement formatté vers stdout. cal affichera l'année en cours ou bien un large intervalle d'années passées et futures.

  • sleep
    C'est l'équivalent shell d'une boucle d'attente. Elle attend durant le nombre spécifié de secondes, ne faisant rien. Elle peut être utile pour un timing ou dans les processus tournant en tâche de fond, en attente d'un évenement spécifique aussi souvent (sondage), tel que dans Nettoyage après un Control-C. Remarque:La commande sleep se base par défaut sur les secondes, mais des minutes, des heures ou des jours peuvent aussi être spécifiés.

  • usleep
    Microsleep (le "u" peut être lu de la même manière que la lettre Grecque "mu" , ou micro). Elle fonctionne de manière identique à sleep, décrit juste au dessus, sauf qu'elle "attend" à partir de délai en micro-secondes. On peut l'utiliser pour des timing très fins ou pour interroger un processus en cours à des intervalles très fréquents.Cette commande fait partie du paquetage Red Hat initscripts / rc-scripts. Attention:La commande usleep ne permet pas des timing particulièrement précis et n'est donc pas adaptée pour des boucles aux timings critiques.

  • hwclock
    La commande hwclock accède à, ou ajuste l'horloge de la machine. Quelques options requièrent les privilèges du super-utilisateur (root). Le fichier de démarrage /etc/rc.d/rc.sysinit utilise hwclock pour ajuster l'heure système depuis l'horloge machine durant le démarrage.La commande clock est un synonyme de hwclock.


Commandes d'analyse de texte

  • sort
    Trieuse de fichier, souvent utilisée dans un tube pour trier. Cette commande trie un flux de texte ou un fichier, ascendant ou descendant, ou selon diverses clés ou positions de caractère. Avec l'option -m, elle combine des fichiers pré-triés. La page info recense ses multiples possibilités et options. Voir Rechercher les auteurs de tous les binaires d'un répertoire, Afficher les liens symboliques dans un répertoire et makedict: Créer un dictionnaire.

  • tsort
    Trie topologique, lisant chaque paire de mots séparés par un espace et triant en fonction des motifs (patterns) lus.

  • uniq
    Ce filtre efface les lignes dupliquées depuis un fichier trié. On le voit souvent dans un tube combiné avec un sort. L'option très utile -c préfixe chaque ligne du fichier d'entrée avec son nombre d'occurence.
  • bash$ cat fichiertest Cette ligne apparaît une seule fois. Cette ligne apparaît deux fois. Cette ligne apparaît deux fois. Cette ligne apparaît trois fois. Cette ligne apparaît trois fois. Cette ligne apparaît trois fois. bash$ uniq -c fichiertest 1 Cette ligne apparaît une seule fois. 2 Cette ligne apparaît deux fois. 3 Cette ligne apparaît trois fois. bash$ sort fichiertest | uniq -c | sort -nr 3 Cette ligne apparaît trois fois. 2 Cette ligne apparaît deux fois. 1 Cette ligne apparaît trois fois.
  • La commande
  • sort FICHIER_ENTREE | uniq -c | sort -nr
  • renvoie un listing du nombre d'occurence du fichier FICHIER_ENTREE (l'option -nr de sort produit un tri numérique inversé). Ce modèle de recherche trouve son utilité dans l'analyse de fichiers de traces et de dictionnaires, ainsi que là où la structure lexicale d'un document doit être examinée.
  • Analyse de frequence d'apparition de mot
    #!/bin/bash # wf.sh: Compte la fréquence de répétition des mots d'un fichier texte # Vérifie si un fichier a été fourni en ligne de commande. ARGS=1 E_MAUVAISARGS=65 E_FICHIERINEXISTANT=66 if [ $# -ne "$ARGS" ] then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi if [ ! -f "$1" ] # Est-ce que le fichier existe ? then echo "Le fichier \"$1\" n'existe pas." exit $E_FICHIERINEXISTANT fi ######################################################## # main () sed -e 's/\.//g' -e 's/ /\ /g' "$1" | tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr # ========================= # Fréquence des occurrences # Enlève les virgules et #+ change les espaces entre les mots en retours chariot, #+ puis met les lettres en minuscule et #+ enfin préfixe avec le nombre d'apparition et #+ effectue un tri numérique. ######################################################## # Exercices: # --------- # 1) Ajouter une commande 'sed' pour supprimer les autres ponctuations, comme #+ les virgules. # 2) Modifier aussi pour filtrer les espaces blancs multiples. # 3) Ajouter un second critère de tri, de façon à ce que le nombre d'occurences #+ similaires soit trié alphabétiquement. exit 0
  • bash$ cat fichiertest Cette ligne apparaît une fois. Cette ligne apparaît deux fois. Cette ligne apparaît deux fois. Cette ligne apparaît trois fois. Cette ligne apparaît trois fois. Cette ligne apparaît trois fois. bash$ ./wf.sh fichiertest 6 Cette 6 apparaît 6 ligne 3 fois 3 trois 2 deux 1 une


  • expand
    Souvent utilisé dans un tube, expand transforme les tabulations en espaces.unexpand transforme les espaces en tabulations. Elle inverse les modifications d'expand.

  • cut
    Un outil d'extraction de champs depuis un fichier. Il est similaire à la commande
  • print $N
  • de awk mais en plus limité. Il peut être plus simple d'utiliser cut dans un script plutôt que awk. A noter les options -d (délimitation) et -f (spécification du champ).Utiliser cut pour obtenir une liste des systèmes de fichiers montés: Utiliser cut pour avoir l'OS et la version du noyau: Utiliser cut pour extraire les entêtes des messages depuis un dossier de courriers électroniques:
  • bash$ grep '^Subject:' messages-lus | cut -c10-80 Re: Linux suitable for mission-critical apps? MAKE MILLIONS WORKING AT HOME!!! Spam complaint Re: Spam complaint
  • Utiliser cut pour analyser un fichier :
  • cut -d ' ' -f2,3 fichier
  • est équivalent à
  • awk -F'[ ]' '{ print $2, $3 }' fichier
  • Voir aussi Conversion de base.

  • paste
    Outil pour fusionner ensemble différents fichiers dans un seul fichier multi-colonne. Combiné avec cut, c'est utile pour créer des fichiers de traces.

  • join
    Considérez-le comme un cousin de paste mais à usage spécifique. Ce puissant outil permet de fusionner deux fichiers d'une façon significative, qui crée essentiellemnt une simple version de base de données relationelle.join travaille sur deux fichiers mais copie ensemble seulement les lignes qui possèdent un champ commun (un nombre par exemple) et écrit le résultat vers stdout. Les fichiers joints doivent être triés de la même façon sur le champ cible pour que la correspondance fonctionne correctement.
  • bash$ join 1.donnees 2.donnees Fichier: 1.donnees 2.donnees 100 Chaussures $40.00 200 Bretelles $1.00 300 Cure-dents $2.00
  • Remarque:Les champs marqués apparaîtront seulement une fois dans le résultat.

  • head
    Affiche le début d'un fichier sur stdout (par défaut 10 lignes, mais c'est modifiable). Elle possède de nombreuses options.
  • Quels fichiers sont des scripts ?
    #!/bin/bash # script-detector.sh: Detecte les scripts qui sont dans un répertoire. TESTCHARS=2 # Teste les 2 premiers caractères. SHABANG='#!' # Les scripts commencent toujours avec un "#!" for fichier in * # Scanne tous les fichiers du répertoire courant. do if [[ `head -c$TESTCHARS "$fichier"` = "$SHABANG" ]] # head -c2 #! # L'opton '-c' de "head" n'affiche que le nombre spécifié de #+ caractères, plutôt que de lignes (par défaut). then echo "Le fichier \"$fichier\" est un script." else echo "Le fichier \"$fichier\" n'est *pas* un script." fi done exit 0
  • Générer des nombres aléatoires de 10 chiffres
    #!/bin/bash # rnd.sh: Affiche un nombre aléatoire de 10 chiffres # Script de Stephane Chazelas. head -c4 /dev/urandom | od -N4 -tu4 | sed -ne '1s/.* //p' # =================================================================== # # Analyse # ------- # head: # -c4 prend les 4 premiers octets. # od: # -N4 limite la sortie à 4 octets. # -tu4 sélectionne le format de sortie décimal non-signé. # sed: # -n , combiné avec le drapeau "p" de la commande "s", # n'affiche que les lignes correspondantes. # L'auteur explique ci-après le fonctionnement de 'sed'. # head -c4 /dev/urandom | od -N4 -tu4 | sed -ne '1s/.* //p' # ----------------------------------> | # On dit que ce que l'on va --------> | # envoyer à "sed" ------------------> | # est 0000000 1198195154\n ---------> | # sed commence par lire les caractères: 0000000 1198195154\n. # Ici, il trouve un caractère de saut de ligne, # donc il est prêt pour commencer à s'occuper de la première ligne (0000000 1198195154). # Il regarde son intervalleactions. La première est # intervalle action # 1 s/.* //p # The line number is in the range, so it executes the action: # tries to substitute the longest string ending with a space in the line # ("0000000 ") with nothing (//), and if it succeeds, prints the result # ("p" is a flag to the "s" command here, this is different from the "p" command). # sed is now ready to continue reading its input. (Note that before # continuing, if -n option had not been passed, sed would have printed # the line once again). # Now, sed reads the remainder of the characters, and finds the end of the file. # It is now ready to process its 2nd line (which is also numbered '$' as # it's the last one). # It sees it is not matched by any range, so its job is done. # In few word this sed commmand means: # "On the first line only, remove any character up to the right-most space, # then print it." # A better way to do this would have been: # sed -e 's/.* //;q' # Here, two rangeactions (could have been written # sed -e 's/.* //' -e q): # range action # nothing (matches line) s/.* // # nothing (matches line) q (quit) # Here, sed only reads its first line of input. # It performs both actions, and prints the line (substituted) before quitting # (because of the "q" action) since the "-n" option is not passed. # =================================================================== # # A simpler altenative to the above 1-line script would be: # head -c4 /dev/urandom| od -An -tu4 exit 0
  • Voir aussi Décoder des fichier codés avec uudecode.

  • tail
    Affiche la fin d'un fichier vers stdout (par défaut 10 lignes). Habituellement utilisé pour voir les changements faits à un fichier de traces avec -f qui affiche les lignes ajoutées à un fichier.
  • Utiliser tail pour surveiller le fichier des traces système
    #!/bin/bash fichier=sys.log cat /dev/null > $fichier; echo "Crée / efface fichier." # Crée ce fichier s'il n'existait pas auparavant, #+ et le réduit à une taille nulle s'il existait. # : > fichier et > fichier marchent aussik. tail /var/log/messages > $fichier # /var/log/messages doit avoir la permission de lecture pour que ce programme #+ marche. echo "$fichier contient la fin du fichier log système." exit 0
  • Voir aussi Fichier de traces utilisant xargs pour surveiller les logs système, Décoder des fichier codés avec uudecode et Nettoyage après un Control-C.

  • grep
    Un outil de recherche qui utilise les expressions régulières. A la base, c'était un filtre du vénérable ed éditeur de ligne,
  • G.Re.P
  • : global - regular expression - print.
    grep motif[fichier]...
    Recherche dans le fichier cible un motif, où motif peut être un texte littéral ou une expression régulière.
  • bash$ grep '[rst]ystem.$' osinfo.txt The GPL governs the distribution of the Linux operating system.
  • Si aucun fichier n'est spécifié, grep travaillera en tant que filtre sur stdout, comme dans un tube.
  • bash$ ps ax | grep clock 765 tty1 S 0:00 xclock 901 pts/1 S 0:00 grep clock
  • -i active la recherche insensible à la casse.-w recherche seulement les mots entiers. -l liste seulement les fichiers dans lesquels des correspondances ont été trouvées, mais pas les lignes correspondantes.-r (récursif) cherche dans tous les sous-répertoires.-n montre en plus les numéros de lignes qui correspondent.
  • bash$ grep -n Linux osinfo.txt 2:This is a file containing information about Linux. 6:The GPL governs the distribution of the Linux operating system.
  • -v (ou --invert-match) n'affiche pas les correspondances, seulement celles qui ne correspondent pas. -c (--count) affiche le nombre de correspondances trouvées, plutôt que de les afficher. Lorsqu'il est invoqué avec plus d'un fichier cible donné, grep spécifie quel fichier contient les correspondances.
  • bash$ grep Linux osinfo.txt misc.txt osinfo.txt:This is a file containing information about Linux. osinfo.txt:The GPL governs the distribution of the Linux operating system. misc.txt:The Linux operating system is steadily gaining in popularity.
  • Astuce:Pour forcer grep à montrer le nom du fichier pendant la recherche d'un fichier cible, donnez /dev/null comme deuxième fichier. Astuce:
  • bash$ grep Linux osinfo.txt /dev/null osinfo.txt:This is a file containing information about Linux. osinfo.txt:The GPL governs the distribution of the Linux operating system.
  • S'il y a une correspondance trouvée, grep retourne un code de sortie de 0, ce qui le rend utile comme test conditionnel dans un script, en particulier en combinaison avec l'option -q pour supprimer la sortie. L'Nettoyage après un Control-C démontre comment utiliser grep pour chercher un mot dans un fichier de traces.
  • Emuler grep dans un script
    #!/bin/bash # grp.sh: Une réimplémentation brute de 'grep'. E_MAUVAISARGS=65 if [ -z "$1" ] # Vérification standard des arguments de ligne de commande. then echo "Usage: `basename $0` motif" exit $E_MAUVAISARGS fi echo for fichier in * # Parcourt tous les fichiers dans $PWD. do sortie=$(sed -n /"$1"/p $fichier) # Substitution de commande. if [ ! -z "$sortie" ] # Que se passe t-il si "$sortie" n'est pas quotés ? then echo -n "$fichier: " echo $sortie fi # sed -ne "/$1/s|^|${fichier}: |p" est l'équivalent de dessus. echo done echo exit 0 # Exercices: # --------- # 1) Ajoutez des sauts de lignes à la sortie, # s'il y a plus d'une correspondance dans n'importe quel fichier donné. # 2) Ajoutez des nouvelles possibilités.
  • Remarque:egrep est comme grep -E. Elle utilise un jeu d'expressions régulières légèrement différent et étendu, ce qui peut rendre une recherche plus flexible. Remarque:fgrep est comme grep -F. Elle recherche une chaîne littérale (pas d'expressions régulières), ce qui accélère les choses. Remarque:agrep étend les possibilités de grep à une recherche approximative. La chaîne peut différer d'un nombre spécifié de caractères des résultats de recherches. Cette commande ne fait pas partie des distributions Linux. Astuce:Pour chercher dans des fichiers compressés, utilisez zgrep, zegrep ou zfgrep. Ces commandes marchent aussi avec des fichiers non compressés, bien que plus lentement qu'un simple grep, egrep, fgrep. C'est pratique pour chercher dans divers fichiers, compressés ou non. Astuce:Pour chercher dans des fichiers compressés avec bzip, utilisez bzgrep.

  • look
    La commande look fonctionne comme grep mais fait une recherche basée sur un "dictionnaire" , une liste de mots triés. Par défaut, look cherche une correspondance dans /usr/dict/words mais un autre dictionnaire peut être utilisé.
  • Chercher les mots dans une liste pour tester leur validité
    #!/bin/bash # lookup: Fait une recherche basée sur un dictionnaire sur chaque mot d'un #+ fichier de données. fichier=mots.donnees # Le fichier de données a partir duquel on lit les mots à #+ tester. echo while [ "$mot" != end ] # Le dernier mot du fichier de données. do read mot # Depuis le fichier de données, à cause de la redirection à la #+ fin de la boucle. look $mot > /dev/null # Nous ne voulons pas afficher les lignes dans le #+ dictionnaire. lookup=$? # Code de sortie de 'look'. if [ "$lookup" -eq 0 ] then echo "\"$mot\" est valide." else echo "\"$mot\" est invalide." fi done <"$fichier" # Redirige stdin vers $fichier, donc les "lectures" #+ commencent à partir d'ici. echo exit 0 # ----------------------------------------------------------------------------- # Le code ci-dessous ne s'exécutera pas au cause de la commande exit au dessus # Stephane Chazelas propose aussi ce qui suit, une alternative plus concise : while read mot && [[ $mot != end ]] do if look "$mot" > /dev/null then echo "\"$mot\" est valide." else echo "\"$mot\" est invalide." fi done <"$fichier" exit 0


  • sed
    Langages de script créés spécialement pour analyser les fichiers texte et les sorties de commandes. Peuvent être intégrés seuls ou emsembles dans des tubes et des scripts shell.

  • sed
    "Editeur de flux" non interactif, permettant d'utiliser plusieurs commandes ex dans un mode batch. C'est souvent utile dans des scripts shell.

  • awk
    Extracteur et formatteur de fichiers programmables, bon pour la manipulation ou l'extraction de champ (colonnes) dans des fichiers textes structurés. Sa syntaxe est similaire à C.

  • wc
    wc (word count) donne le nombre de mots d'un fichier ou d'un flux:
  • bash $ wc /usr/doc/sed-3.02/README 20 127 838 /usr/doc/sed-3.02/README [20 lignes 127 mots 838 caractère]
  • wc -w
  • donne seulement le nombre de mots.
  • wc -l
  • donne seulement le nombre de lignes.
  • wc -c
  • donne le nombre de caractères.
  • wc -L
  • donne la taille de la ligne la plus longue.Utiliser wc pour connaître le nombre de fichiers .txt dans le répertoire courant : Utiliser wc pour compter la taille de tous les fichiers dont le nom commence avec les lettres d à h
  • bash$ wc [d-h]* | grep total | awk '{print $3}' 71832
  • Utiliser wc pour compter le nombre de fois que "Linux" apparaît dans ce document.
  • bash$ grep Linux abs-book.sgml | wc -l 50
  • Voir aussi Décoder des fichier codés avec uudecode et Boucle for redirigée. Certaines commandes incluent quelques fonctionnalité de wc comme options.

  • tr
    Filtre de traduction de caractères. Attention:Utilisez les guillemets et/ou les parenthèses, si besoin est. Les guillemets empêchent le shell de réinterpréter les caractères spéciaux dans les séquences de commande de tr. Les parenthèses devraient être mises entre guillemets pour empêcher leur expansion par le shell.
  • tr "A-Z" "*" fichier
  • ou
  • tr A-Z \* fichier
  • remplacent toutes les majuscules de fichier par des astérisques (le résultat est écrit dans stdout). Sur certains systèmes, ça peut ne pas fonctionner. Cependant
  • tr A-Z '[**]'
  • fonctionnera.-d efface un intervalle de caractères. --squeeze-repeats (ou -s) efface tout sauf la première occurence d'une chaîne de caractères. Cette option est utile pour supprimer les espaces blancs superflus.
  • bash$ echo "XXXXX" | tr --squeeze-repeats 'X' X
  • -c "complement" inverse l'ensemble des caractères à détecter. Avec cette option, tr n'agit que sur les caractères ne faisant pas parti de l'ensemble spécifiés.
  • bash$ echo "acfdeb123" | tr -c b-d + +c+d+b++++
  • Notez que tr reconnait les ensembles de caractères POSIX. (28)
  • bash$ echo "abcd2ef1" | tr '[:alpha:]' - ----2--1
  • toupper: Transforme un fichier en majuscule.
    #!/bin/bash # Met unemajuscule un fichier E_MAUVAISARGS=65 if [ -z "$1" ] # Vérification standard des arguments de ligne de commande. then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi tr a-z A-Z <"$1" # Même effet que le programme au dessus, mais utilisant la notation POSIX : # tr '[:lower:]' '[:upper:]' <"$1" # Merci, S.C. exit 0
  • lowercase: Change tous les noms de fichier du répertoire courant en minuscule.
    #! /bin/bash # # Change chaque nom de fichier en minuscules dans le répertoire courant. # # Inspiré par un script de John Dubois, # qui fut traduit en Bash par Chet Ramey, # et considérablement simplifié par Mendel Cooper, auteur de ce document. for nomfichier in * # Scanne tous les fichiers du répertoire. do nomF=`basename $nomfichier` n=`echo $nomF | tr A-Z a-z` # Change le nom en minuscule. if [ "$nomF" != "$n" ] # Renomme seulement les fichiers qui ne sont #+ pas en minuscules. then mv $nomF $n fi done exit 0 # Le code en dessous ne s'exécutera pas à cause de la commande exit au dessus #-----------------------------------------------------------------------------# # Pour le lancer, effacez la ligne de script ci dessus. # Le script suivant ne fonctionnera pas sur les fichiers dont le nom a des #+ espaces ou des caractères de saut de ligne. # Stephane Chazelas suggère donc l'alternative suivante : for nomfichier in * # Pas nécéssaire d'utiliser basename, # car "*" ne retourne aucun fichier contenant "/". do n=`echo "$nomfichier/" | tr '[:upper:]' '[:lower:]'` # Jeu de notation POSIX. # Le slash est ajouté, comme ça les saut de lignes ne sont # pas supprimés par la substition de commande. # Substitution de variable : n=${n%/} # Supprime le slash de fin, rajouté au dessus, du nom de #+ fichier. [[ $nomfichier == $n ]] || mv "$nomfichier" "$n" # Vérifie si le nom de fichier est déjà en minuscules. done exit 0
  • du: Converti les fichiers text DOS vers UNIX.
    #!/bin/bash # du.sh: Convertisseur de fichier texte DOS vers UNIX. E_WRONGARGS=65 if [ -z "$1" ] then echo "Usage: `basename $0` fichier-a-convertir" exit $E_WRONGARGS fi NEWFILENAME=$1.unx CR='\015' # Retour charriot. # En DOS, les lignes se termine par un CR-LF. tr -d $CR $1 $NEWFILENAME # Efface les caractères CR et écrit le résultat dans un nouveau fichier. echo "Le fichier texte DOS original est \"$1\"." echo "Le fichier UNIX converti est \"$NEWFILENAME\"." exit 0
  • rot13: rot13, cryptage ultra-faible.
    #!/bin/bash # rot13.sh: L'algorithme classique rot13, # cryptage qui pourrait berner un gamin de 3 ans. # Usage: ./rot13.sh nomfichier # ou ./rot13.sh nomfichier # ou ./rot13.sh et fournissez une entrée clavier (stdin) cat "$@" | tr 'a-zA-Z' 'n-za-mN-ZA-M' # "a" devient "n", "b" devient "o", etc. # La commande 'cat "$@"' #+ permet la saisie de données depuis soit stdin soit un fichier. exit 0
  • Générer des puzzles Crypto-Citations
    #!/bin/bash # crypto-quote.sh: Crypte les citations # Crypte de célèbre citations par une simple substitution de lettres. # Le résultat est similaire aux puzzles "Crypto Quote" #+ vus sur les pages "Op Ed" du journal Sunday. key=ETAOINSHRDLUBCFGJMQPVWZYXK # "key" n'est qu'un alphabet mélangé. # Changer "key" modifie le cryptage. # L'instruction 'cat "$@"' prend son entrée soit de stdin, soit de fichiers. # Si stdin est utilisé, il faut terminer la saisie par un Ctrl-D. # Sinon, spécifier le nom du fichier en tant que paramètre de la ligne de commande. cat "$@" | tr "a-z" "A-Z" | tr "A-Z" "$key" # | en majuscule | crypte # Fonctionne avec n'importe quel type de casse : majuscule, minuscule ou les #+ deux ensemble. # Les caractères non-alphabétiques restent inchangés. # Essayer ce script avec # "Nothing so needs reforming as other people's habits." # --Mark Twain # # Il en résulte : # "CFPHRCS QF CIIOQ MINFMBRCS EQ FPHIM GIFGUI'Q HETRPQ." # --BEML PZERC # Pour décrypter, utiliser : # cat "$@" | tr "$key" "A-Z" # Ce programme de cryptage bidon peut être cassé par un gosse de 12 ans. exit 0
  • L'utilitaire tr a des variantes historiques. La version BSD n'utilise pas les crochets (
  • tr a-z A-Z
  • ) contrairement à la version SysV (
  • tr '[a-z]' '[A-Z]'
  • ). La version GNU de tr ressemble à celle de BSD, donc mettre entre guillemets un intervalle de lettre est obligatoire.

  • fold
    Un filtre qui coupe les lignes entrées à partir d'une taille spécifiée. C'est spécialement utile avec l'option -s, qui coupe les lignes à chaque espace (voir Affichage d'un fichier formatté. et mailformat: Formater un courrier électronique).

  • fmt
    Un formatteur de fichier tout bête, utilisé en tant que filtre dans un tube pour "couper" les longues lignes d'un texte.
  • Affichage d'un fichier formatté.
    #!/bin/bash LARGEUR=40 # Colonnes de 40 caractères. b=`ls /usr/local/bin` # Récupère la liste des fichiers du répertoire echo $b | fmt -w $LARGEUR # Aurait pu aussi être fait avec # echo $b | fold - -s -w $LARGEUR exit 0
  • Voir aussi Fichier de traces utilisant xargs pour surveiller les logs système. Astuce:Une puissante alternative à fmt est par de Kamil Toman disponible sur http://www.cs.berkeley.edu/~amc/Par/.

  • col
    Cette commande dont le nom est trompeur supprime les retours chariot inversés d'un flux en entrée. Elle tente aussi de remplacer les espaces blances par des tabulations équivalentes. Le rôle principal de col est de filtrer la sortie de certains utilitaires de manipulation de textes, tels que groff et tbl.

  • column
    Formatteur de colonnes. Ce filtre transforme le texte écrit façon "liste" en un "joli" tableau par l'insertion de tabulations aux endroits appropriés.
  • utiliser column pour formatter l'affichage des répertoires
    #!/bin/bash # Il s'agit d'une légère modification du fichier d'exemple dans la page de #+ manuel de "column". (printf "PERMISSIONS LIENS PROPRIETAIRE GROUPE TAILLE MOIS JOUR HH:MM NOM-PROG\n" \ ; ls -l | sed 1d) | column -t # "sed 1d" efface la première ligne écrite, #+ qui devrait être "total N", #+ où "N" est le nombre total de fichiers trouvés par "ls -l". # L'option -t de "column" affiche un tableau bien formaté. exit 0


  • colrm
    Filtre de suppression de colonnes. Ce filtre enlève les colonnes (caractères) d'un fichier et affche le résultat vers stdout.
  • colrm 2 4 fichier
  • efface le deuxième par bloc de 4 caractères de chaque ligne du fichier fichier.Si le fichier contient des tabulations ou des caractères non imprimables, ça peut causer des comportements imprévisibles. Dans de tel cas, considérez l'utilisation de expand et unexpand dans un tube précédant colrm.

  • nl
    Filtre de numérotation de lignes.
  • nl fichier
  • affiche fichier sur stdout en insérant un nombre au début de chaque ligne non vide. Si fichier est omit, alors ce filtre travaillera sur stdin.La sortie de nl est très similaire à
  • cat -n
  • . Cependant, par défaut nl ne liste pas les lignes vides.
  • nl: Un script d'autonumérotation.
    #!/bin/bash # Ce script s'affiche deux fois sur stdout en numérotant les lignes. # 'nl' voit ceci comme la ligne 3 car il ne compte pas les lignes blanches. # 'cat -n' voit la ligne ci-dessus comme étant la ligne 5. nl `basename $0` echo; echo # Maintenant, essayons avec 'cat -n' cat -n `basename $0` # La différence est que 'cat -n' numérote les lignes blanches. # Notez que 'nl -ba' fera de même. exit 0


  • pr
    Filtre d'impression formatté. Ce filtre paginera des fichiers (ou stdout) en sections utilisables pour des impressions papier ou pour voir à l'écran. Diverses options permettent la manipulation des rangées et des colonnes, le regroupement des lignes, la définition des marges, la numérotation des lignes, l'ajout d'entêtes par page et la fusion de fichiers entre autres choses. La commande pr combine beaucoup des fonctionnalités de nl, paste, fold, column et expand.
  • pr -o 5 --width=65 fileZZZ | more
  • renvoie un joli affichage paginé à l'écran de fileZZZ avec des marges définies à 5 et 65.Une option particulèrement utile est -d, forçant le double-espacement (même effet que sed -G).

  • gettext
    Un utilitaire GNU pour localiser et traduire les sorties textes des programmes en langue étrangère. Bien que prévu à l'origine pour les programmes C, gettext trouve aussi son utilité dans des scripts shell. Voir la page info.

  • iconv
    Un utilitaire pour convertir des fichiers en un codage différent (jeu de caractère). Son rôle principal concerne les localisations.

  • recode
    Considérez-le comment une version fantaisie d'iconv, ci-dessus. Ce très souple utilitaire pour la conversion d'un fichier dans un jeu de caractère différent ne fait pas partie d'une installation Linux standard.

  • TeX
    TeX et Postscript sont des langages de balises utilisés pour préparer une impression ou un formattage pour l'affichage vidéo.TeX est le système "typesetting" élaboré de Donald Knuth. C'est souvent pratique d'écrire un script qui va encapsuler toutes les options et arguments passés à l'un de ces langages.Ghostscript (gs) est un interpréteur GPL de Postscript .

  • groff
    Un autre langage de balises est groff. C'est la version avancée GNU de la commande UNIX roff/troff. Les pages de manuel utilisent groff (voir manview: Visualiser des pages man formatées ).tbl, utilitaire de création de tableau est considéré comme faisant partie de groff, dans la mesure où sa fonction est de convertir une balise tableau en commandes groff.Le processeur d'équations eqn fait aussi parti de groff et sa fonction est de convertir une balise d'équation en commandes groff.

  • lex
    lex, analyseur lexical, produit des programmes pour la détection de motifs. Ca a été remplacé depuis par le non propriétaire flex sur les systèmes Linux.L'utilitaire yacc crée un analyseur basé sur un ensemble de spécifications. Elle est depuis remplacée par le non propriétaire bison sur les systèmes Linux.


Commandes pour les fichiers et l'archivage

  • tar
    L'utilitaire standard d'archivage sous UNIX. Originellement, un programme d'archivage sur cassette (Tape ARchiving), il est devenu un package plus généraliste qui peut gérer toutes les façons d'archiver sur tout type de support, allant des lecteurs de bande aux fichiers standards, ou même sur stdout (voir Sauvegarde de tous les fichiers modifiés dans les dernières 24 heures). La version GNU de tar a été améliorée pour accepter différents filtres de compression, tels que tar czvf archive_name.tar.gz *, qui, récursivement, archive et compresse (gzip) tous les fichiers d'un répertoire sauf ceux commençant par un point dans le répertoire courant ($PWD). (29)Quelques options utiles de tar: -c crée (une nouvelle archive) -x extrait (les fichiers d'une archive existante) --delete supprime (les fichiers d'une archive existante) Attention:Cette option ne fonctionnera pas sur les périphériques à bandes magnétiques. -r ajoute (des fichiers à une archive existante) -A ajoute (des fichiers tar à une archive existante) -t liste (le contenu d'une archive existante) -u met à jour une archive -d compare une archive avec un système de fichiers spécifié -z gzip l'archive(compresse ou décompresse, suivant que cette option est combinée avec l'option -c ou -x) -j bzip2 l'archive (NdT: autre format de compression) Attention:Il pourrait être difficile de récupérer des données d'une archive tar corrompue compressée avec gzip. Lors de l'archivage de fichiers importants, faites plusieurs copies.

  • shar
    Utilitaire d'archivage shell. Les fichiers dans une archive shell sont concaténés sans compression, et l'archive qui en résulte est essentiellement un script shell complet, avec l'entête #!/bin/sh, et contenant toutes les commandes nécessaires pour déballer l'archive. Les archives shar sont toujours montrées sur les groupes de nouvelles Internet, mais sinon shar a été assez bien remplacé par tar/gzip. La commande unshar déballe les archives shar.

  • ar
    Utilitaire de création et de manipulation d'archives, principalement utilisé pour des bibliothèques de fichiers binaires.

  • rpm
    Le gestionnaire de paquetages Red Hat (Red Hat Package Manager, ou rpm) apporte une sur-couche pour les archives source ou binaire. Il inclut des commandes pour installer et vérifier l'intégrité des paquetages, en plus d'autres choses. Un simple rpm -i package_name.rpm suffit généralement à installer un paquetage, bien qu'il y ait bien plus d'options disponibles. Astuce:
  • rpm -qa
  • donne une liste complète de tous les paquetages rpm installés sur un système donné. Un
  • rpm -qa nom_paquetage
  • liste seulement le(s) paquetage(s) correspondant à nom_paquetage. Astuce:
  • bash$ rpm -qa redhat-logos-1.1.3-1 glibc-2.2.4-13 cracklib-2.7-12 dosfstools-2.7-1 gdbm-1.8.0-10 ksymoops-2.4.1-1 mktemp-1.5-11 perl-5.6.0-17 reiserfs-utils-3.x.0j-2 ... bash$ rpm -qa docbook-utils docbook-utils-0.6.9-2 bash$ rpm -qa docbook | grep docbook docbook-dtd31-sgml-1.0-10 docbook-style-dsssl-1.64-3 docbook-dtd30-sgml-1.0-10 docbook-dtd40-sgml-1.0-11 docbook-utils-pdf-0.6.9-2 docbook-dtd41-sgml-1.0-10 docbook-utils-0.6.9-2


  • cpio
    Cette commande d'archivage spécifique à la copie (copy input and output, c'est--à-dire copie l'entrée et la sortie) est rarement utilisé, car elle a été supplanté par tar/gzip. Elle a toujours son utilité, comme le déplacement d'un répertoire complet.
  • Utiliser cpio pour déplacer un répertoire complet
    #!/bin/bash # Copier un répertoire complet en utilisant cpio. ARGS=2 E_MAUVAISARGS=65 if [ $# -ne "$ARGS" ] then echo "Usage: `basename $0` source destination" exit $E_MAUVAISARGS fi source=$1 destination=$2 find "$source" -depth | cpio -admvp "$destination" # Lire la page man de cpio pour "décrypter" ces options. exit 0


  • rpm2cpio
    Cette commande crée une archive cpio à partir d'un rpm.
  • Déballer une archive rpm
    #!/bin/bash # de-rpm.sh: Déballe une archive 'rpm' : ${1?"Usage: `basename $0` fichier_cible"} # Doit spécifier le nom de l'archive 'rpm' comme argument. FICHIERTEMP=$$.cpio # Fichier temporaire avec un nom "unique". # $$ est l'identifiant du processus du script. rpm2cpio < $1 > $FICHIERTEMP # Convertir l'archive rpm en archive cpio. cpio --make-directories -F $FICHIERTEMP -i # Déballe l'archive cpio. rm -f $FICHIERTEMP # Supprime l'archive cpio. exit 0 # Exercice: # Ajouter une vérification pour # 1) le "fichier-cible" existe et #+ 2) c'est réellement une archive rpm. # Astuce: analysez la sortie de la commande 'file'.


  • gzip
    L'utilitaire de compression standard GNU/UNIX, remplaçant l'inférieur et propriétaire compress. La commande de décompression correspondante est gunzip, qui est l'équibvalent de gzip -d.Le filtre zcat décompresse un fichier gzip vers stdout, comme possible entrée à une redirection ou un tube. Ceci est, en fait, une commande cat fonctionnant sur des fichiers compressés (incluant les fichiers créés par l'ancien utilitaire compress). La commande zcat est l'équivalent de gzip -dc. Attention:Sur certains systèmes UNIX commerciaux, zcat est un synonyme pour uncompress -c, et ne fonctionnera pas avec les fichiers compressés avec gzip.Voir aussi zmost.

  • bzip2
    Un autre utilitaire de compression, habituellement plus efficace (mais plus lent) que gzip, spécialement sur de gros fichiers. La commande de décompression correspondante est bunzip2. Remarque:Les nouvelles versions de tar ont acquis le support de bzip2.

  • compress
    C'est un utilitaire de compression plus ancien, propriétaire disponible dans les distributions UNIX commerciales. gzip, plus efficace, l'a largement remplacé. Les distributions Linux incluent généralement un compress pour des raisons des compatibilité, bien que gunzip peux déballer des fichiers traités avec compress. Astuce:La commande znew transforme les fichiers compressés en fichiers gzip.

  • sq
    Encore un autre utilitaire de compression, un filtre qui fonctionne seulement sur les listes de mots ASCII triées. Il utilise la syntaxe standard d'appel pour un filtre, sq input-file fichier-sortie. Rapide, mais pas aussi efficace que gzip. Le filtre de décompression correspondant est unsq, appelé comme sq. Astuce:La sortie de sq peut être envoyé via un tube à gzip pour une meilleure compression.

  • zip
    Utilitaire inter-plateforme d'archivage et de compression de fichiers compatible avec DOS pkzip.exe. Les archives "Zip" semblent être un medium plus acceptable pour l'échange sur Internet que les "archives tar" .

  • unarc
    Ces utilitaires Linux permettent de déballer des archives compressées avec les programmes DOS arc.exe, arj.exe et rar.exe.

  • file
    Un utilitaire pour identifier le type des fichiers. La commande
  • file nom-fichier
  • renvoiera une spécification du fichier nom-fichier, telle que ascii text ou data. Il utilise les numéros magiques trouvés dans /usr/share/magic, /etc/magic ou /usr/lib/magic, suivant la distribution Linux/UNIX.L'option -f fait que file tourne en mode batch, pour lire à partir d'un fichier désigné une liste de noms de fichiers à analyser. L'option -z, lorsqu'elle est utilisé sur un fichier compressé, essaie d'analyser le type du fichier décompressé.
  • bash$ file test.tar.gz test.tar.gz: gzip compressed data, deflated, last modified: Sun Sep 16 13:34:51 2001, os: Unix bash file -z test.tar.gz test.tar.gz: GNU tar archive (gzip compressed data, deflated, last modified: Sun Sep 16 13:34:51 2001, os: Unix)
  • Supprimer les commentaires de programmes C
    #!/bin/bash # strip-comment.sh: Supprime les commentaires (/* COMMENT */) d'un progamme C. E_SANSARGS=65 E_ERREURARG=66 E_MAUVAIS_TYPE_FICHIER=67 if [ $# -eq "$E_SANSARGS" ] then echo "Usage: `basename $0` fichier-C" >&2 # Message d'erreur vers stderr. exit $E_ERREURARG fi # Test du type de fichier. type=`eval file $1 | awk '{ print $2, $3, $4, $5 }'` # "file $1" affiche le type du fichier... # puis awk supprime le premier champ, le nom du fichier... # enfin, le résultat remplit la variable "type". type_correct="ASCII C program text" if [ "$type" != "$type_correct" ] then echo echo "Ce script fonctionne uniquement sur les fichiers C." echo exit $E_MAUVAIS_TYPE_FICHIER fi # Script sed assez complexe: #-------- sed ' /^\/\*/d /.*\/\*/d ' $1 #-------- # Facile à comprendre si vous prenez quelques heures pour apprendre les #+ concepts de sed. # Il est possible d'ajouter une ligne supplémentaire au script sed pour gérer #+ le cas où la ligne de code a un commentaire le suivant, sur la même ligne. # Ceci est laissé en exercice (difficile). # De même, le code ci-dessus supprime les lignes avec un "*/" ou "/*", # ce qui n'est pas un effet désirable. exit 0 # ---------------------------------------------------------------- # Le code ci-dessous ne s'exécutera pas à cause du 'exit 0' ci-dessus. # Stephane Chazelas suggère l'alternative suivante: usage() { echo "Usage: `basename $0` fichier-C" >&2 exit 1 } BIZARRE=`echo -n -e '\377'` # ou BIZARRE=$'\377' [[ $# -eq 1 ]] || usage case `file "$1"` in *"C program text"*) sed -e "s%/\*%${BIZARRE}%g;s%\*/%${BIZARRE}%g" "$1" \ | tr '\377\n' '\n\377' \ | sed -ne 'p;n' \ | tr -d '\n' | tr '\377' '\n';; *) usage;; esac # Ceci ne fonctionne pas avec, par exemple: # printf("/*"); # or # /* /* commentaire intégré bogué */ # # Pour gérer tous les cas spécifiques (commentaires dans des chaînes, # commentaires dans des chaînes où se trouve un \", \\" ...) la seule façon est # d'écrire un analyseur C # (lex ou yacc peut-être?). exit 0


  • which
    which commande-xxx donne le chemin complet vers "commande-xxx" . C'est utile pour trouver si une commande ou un utilitaire particulier est installé sur le système.
  • $bash which rm
  • /usr/bin/rm


  • whereis
    Similair à which, ci-dessus, whereis commande-xxx donne le chemin complet vers "commande-xxx" , mais aussi sa page man.
  • $bash whereis rm
  • rm: /bin/rm /usr/share/man/man1/rm.1.bz2


  • whatis
    whatis fichierxxx recherche "ficheirxxx" dans la base de données whatis. C'est utile pour identifier les commandes système et les fichiers de configuration importants. Considérez-le en tant que commande man simplifiée.
  • $bash whatis whatis
  • whatis (1) - search the whatis database for complete words
  • Explorer /usr/X11R6/bin
    #!/bin/bash # Que sont tous ces mystérieux binaires dans /usr/X11R6/bin? REPERTOIRE="/usr/X11R6/bin" # Essayez aussi "/bin", "/usr/bin", "/usr/local/bin", etc. for fichier in $REPERTOIRE/* do whatis `basename $fichier` # affiche des informations sur le binaire. done exit 0 # Vous pouvez souhaiter rediriger la sortie de ce script, de cette façon: # ./what.sh >>whatis.db # ou la visualiser une page à la fois sur stdout, # ./what.sh | less
  • Voir aussi Fileinfo: opérer sur une liste de fichiers contenue dans une variable.

  • vdir
    Affiche une liste détaillée du contenu du répertoire. L'effet est similaire à ls -l.Il fait partie de GNU fileutils.
  • bash$ vdir total 10 -rw-r--r-- 1 bozo bozo 4034 Jul 18 22:04 data1.xrolo -rw-r--r-- 1 bozo bozo 4602 May 25 13:58 data1.xrolo.bak -rw-r--r-- 1 bozo bozo 877 Dec 17 2000 employment.xrolo bash ls -l total 10 -rw-r--r-- 1 bozo bozo 4034 Jul 18 22:04 data1.xrolo -rw-r--r-- 1 bozo bozo 4602 May 25 13:58 data1.xrolo.bak -rw-r--r-- 1 bozo bozo 877 Dec 17 2000 employment.xrolo


  • locate
    La commande locate cherche les fichiers en utilisant une base de données stockée pour ce seul but. La commande slocate est la version sécurisée de locate (qui pourrait être un alias de slocate).
  • $bash locate hickson
  • /usr/lib/xephem/catalogs/hickson.edb


  • readlink
    Révèle le fichier sur lequel pointe un lien symbolique.
  • bash$ readlink /usr/bin/awk ../../bin/gawk


  • strings
    Utiliser la commande strings pour trouver les chaînes de caractères affichables dans un fichier binaire ou de données. Elle listera les séquences de caractères affichables trouvées dans le fichier cible. C'est intéressant pour un examen rapide (et sale) d'un core dump ou pour regarder un fichier image inconnu (
  • strings fichier-image | more
  • pourrait afficher quelque chose comme JFIF, qui identifierait le fichier en tant que graphique jpeg). Dans un script, vous devriez probablement analyser la sortie de strings avec grep ou sed. Voir Un remplaçant de grep pour les fichiers binaires et Rechercher les auteurs de tous les binaires d'un répertoire.
  • Une commande strings améliorée
    #!/bin/bash # wstrings.sh: "word-strings" (commande "strings" améliorée) # # Ce script filtre la sortie de "strings" en la comparant avec une liste de #+ mots communs. # Ceci élimine efficacement tout le charabia et le bruit et n'affiche #+ que les mots reconnus. # ================================================================= # Vérification standard de(s) argument(s) du script ARGS=1 E_MAUVAISARGS=65 E_AUCUNFICHIER=66 if [ $# -ne $ARGS ] then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi if [ -f "$1" ] # Vérifie si le fichier existe. then nom_fichier=$1 else echo "Le fichier \"$1\" n'existe pas." exit $E_AUCUNFICHIER fi # ================================================================= LONGUEUR_CHAINE_MINIMUM=3 # Longueur minimum d'une chaîne. FICHIER_MOTS=/usr/share/dict/linux.words # Dictionnaire. # Vous pouvez spécifier un autre #+ fichier de mots, à condition que #+ son format soit d'un mot par ligne. listemots=`strings "$1" | tr A-Z a-z | tr '[:space:]' Z | \ tr -cs '[:alpha:]' Z | tr -s '\173-\377' Z | tr Z ' '` # Traduit la sortie de la commande 'strings' avec de multiples passes de 'tr'. # "tr A-Z a-z" réalise une conversion en minuscule. # "tr '[:space:]'" change les espaces blancs par des Z. # "tr -cs '[:alpha:]' Z" change les caractères non alphabétiques en Z. #+ et ne conserve qu'un seul Z pour les Z successifs. # "tr -s '\173-\377' Z" convertit tous les caractères après 'z' en Z #+ et ne conserve qu'un seul Z pour les Z successifs #+ ce qui supprime tous les caractères bizarres que la précédente passe aurait #+ oubliés de gérer. # Finalement, "tr Z ' '" convertit tous les Z en espaces blancs, #+ ce qui sera vu comme des mots séparés dans la boucle ci-dessous. # Notez que la technique de remplissage de la sortie de 'tr' vers lui-même, #+ mais avec different arguments et/ou options à chaque passe. for mot in $listemots # Important: # $listemots ne doit pas être entre guillemets ici. # "$listemots" ne fonctionne pas. # Pourquoi? do longueur_chaine=${#mot} # Longueur de la chaîne. if [ "$longueur_chaine" -lt "$LONGUEUR_CHAINE_MINIMUM" ] then # Ne pas tenir compte des petites chaînes. continue fi grep -Fw $mot "$FICHIER_MOTS" # Correspond seulement aux mots complets. done exit 0


  • diff
    diff: utilitaire de comparaison de fichiers flexible. Il compare les fichiers cibles ligne par ligne, séquentiellement. Dans certaines applications, telles que la comparaison de dictionnaires de mots, il peut être utile pour filtrer les fichiers avec sort et uniq avant de les envoyer via un tube à diff.
  • diff fichier-1 fichier-2
  • affiche en sortie les lignes qui différent des deux fichiers, avec des symbôles indiquant à quel fichier appartient la ligne en question.L'option --side-by-side de diff affiche en sortie chaque fichier comparé, ligne pat ligne, dans des colonnes séparées, et avec les lignes ne correspondant pas marquées. Les options -c et -u rendent la sortie de la commande plus facile à interpréter.Il existe de nombreuses interfaces agréables pour diff, comme spiff, wdiff, xdiff et mgdiff. Astuce:La commande diff renvoie un état de sortie de 0 si les fichiers comparés sont identiques et 1 si ils ne le sont pas. Cela permet d'utiliser diff dans une construction de test à l'intérieur d'un script shell (voir ci-dessous).Une utilisation commune de diff est de générer des fichiers de différence à utiliser avec patch. L'option -e permet la génération de tels fichiers, à utiliser avec des scripts ed ou ex.patch: utilitaire de gestion de versions. Suivant un fichier de différences généré par diff, patch peut mettre à jour une version précédente d'un paquetage en une nouvelle version. Il est bien plus convenable de distribuer un fichier "diff" sensiblement plus petit que le corps entier du paquetage revu. Les correctifs ( "patches" ) du noyau sont devenus la méthode préférée pour distribuer les fréquentes mises à jour du noyau Linux.Applicquer un correctif au noyau: Remarque:La commande diff peut aussi comparer récursivement les répertoires (et les fichiers qui s'y trouvent). Remarque:
  • bash$ diff -r ~/notes1 ~/notes2 Only in /home/bozo/notes1: fichier02 Only in /home/bozo/notes1: fichier03 Only in /home/bozo/notes2: fichier04
  • Astuce:Utiliser zdiff pour comparer des fichiers gzip.

  • diff3
    Une version étendue de diff qui compare trois fichiers en une fois. Cette commande renvoie un état de sortie de 0 si l'exécution est réussie, mais malheureusement, cela ne donne aucune information sur le résultat de la comparaison.
  • bash$ diff3 fichier-1 fichier-2 fichier-3 ==== 1:1c Ceci est la ligne 1 de "fichier-1" 2:1c Ceci est la ligne 1 de "fichier-2" 3:1c Ceci est la ligne 1 de "fichier-3"


  • sdiff
    Compare et/ou édite les deux fichiers pour les assembler dans un fichier de sortie. Dû à sa nature interactive, cette commande trouvera peu d'utilité dans un script.

  • cmp
    La commande cmp est une version simplifiée de diff, ci-dessus. Alors que diff reporte les différences entre deux fichiers, cmp montre simplement à quel point ils diffèrent. Remarque:Comme diff, cmp renvoie un état de sortie de 0 si les fichiers comparés sont identiques et de 1 si ils diffèrent. Ceci permet une utilisation dans une construction de test à l'intérieur d'un script shell.
  • Utiliser cmp pour comparer deux fichiers à l'intérieur d'un script.
    #!/bin/bash ARGS=2 # Deux arguments attendus par le script. E_MAUVAISARGS=65 E_ILLISIBLE=66 if [ $# -ne "$ARGS" ] then echo "Usage: `basename $0` fichier1 fichier2" exit $E_MAUVAISARGS fi if [[ ! -r "$1" || ! -r "$2" ]] then echo "Les deux fichiers à comparer doivent exister et être lisibles." exit $E_ILLISIBLE fi cmp $1 $2 &> /dev/null # /dev/null enterre la sortie de la commande "cmp". # cmp -s $1 $2 a le même résultat ("-s" option de silence pour "cmp") # Merci à Anders Gustavsson pour nous l'avoir indiqué. # # Fonctionne aussi avec 'diff', c'est-à-dire diff $1 $2 &> /dev/null if [ $? -eq 0 ] # Teste du code de sortie de la commande "cmp". then echo "Le fichier \"$1\" est identique au fichier \"$2\"." else echo "Le fichier \"$1\" diffère du fichier \"$2\"." fi exit 0
  • Astuce:Utiliser zcmp sur des fichiers gzip.

  • comm
    Utilitaire de comparaison de fichiers versatile. Les fichiers doivent être triés pour qu'il soit utile.comm -options premier-fichier second-fichier
  • comm fichier-1 fichier-2
  • affiche trois colonnes: colonne 1 = lignes uniques à fichier-1 colonne 2 = lignes uniques à fichier-2 colonne 3 = lignes communes aux deux. Les options permettent la sortie d'une ou plusieurs colonnes. -1 supprime la colonne 1 -2 supprime la colonne 2 -3 supprime la colonne 3 -12 supprime les deux colonnes 1 et 2, etc.

  • basename
    Supprime le chemin d'un nom de fichier, en affichant seulement le nom. La construction
  • basename $0
  • permet au script de connaître son nom, c'est-à-dire le nom par lequel il a été invoqué. Ceci peut être utilisé pour les messages d' "usage" si, par exemple, un script est appelé sans ses arguments:

  • dirname
    Supprime le basename d'un nom de fichier, en affichant que le chemin. Remarque:basename et dirname peuvent s'exécuter sur des chaînes de caractères arbitraires. L'argument n'a pas besoin de référer un fichier existant, voire même un fichier (voir days-between: Calculer le nombre de jours entre deux dates).
  • basename et dirname
    #!/bin/bash a=/home/bozo/daily-journal.txt echo "Nom de base de /home/bozo/daily-journal.txt = `basename $a`" echo "Nom du répertoire de /home/bozo/daily-journal.txt = `dirname $a`" echo echo "Mon répertoire personnel est `basename ~/`." # Fonctionne aussi avec ~. echo "Le chemin de mon répertoire personnel est `dirname ~/`." # Fonctionne aussi avec ~. exit 0


  • split
    Utilitaire pour diviser un fichier en plusieurs petites parties. Habituellement utilisé pour diviser un gros fichier en fichiers tenant sur une disquette ou pour préparer un courrier électronique ou pour les télécharger.

  • sum
    Ces utilitaires ont pour but de vérifier une somme de contrôle. Une somme de contrôle est un nombre calculé à partir du contenu d'un fichier, dans le but de vérifier son intégrité. Un script peut se référer à une liste de sommes de contrôle pour des raisons de sécurité, comme pour s'assurer que des fichiers clés du système n'ont pas été modifié ou corrompu. Pour les applications de sécurité, utilisez la commande md5sum en 128 bits (message digest checksum).
  • bash$ cksum /boot/vmlinuz 1670054224 804083 /boot/vmlinuz bash$ md5sum /boot/vmlinuz 0f43eccea8f09e0a0b2b5cf1dcf333ba /boot/vmlinuz
  • Notez que cksum affiche aussi la taille, en octet, du fichier cible.
  • Vérifier l'intégrité d'un fichier
    #!/bin/bash # file-integrity.sh: Vérifie si les fichiers d'un répertoire donné ont été # modifié. E_REP_INEXISTANT=70 E_MAUVAIS_FICHIER_BD=71 fichierdb=File_record.md5 # Fichier pour stocker les enregistrements. init_base_donnees () { echo ""$repertoire"" > "$fichierdb" # Ecrit le nom du répertoire sur la première ligne du fichier. md5sum "$repertoire"/* >> "$fichierdb" # Ajoute les sommes de contrôle md5 et les noms de fichiers. } verifie_base_donnees () { local n=0 local nomfichier local somme_controle # ------------------------------------------------- # # Cette vérification du fichier devrait être #+ inutile mais c'est mieux d'être sain que désolé. if [ ! -r "$fichierdb" ] then echo "Incapable de lire les somme de contrôle du fichier de base de données!" exit $E_MAUVAIS_FICHIER_BD fi # ------------------------------------------------- # while read enregistrement[n] do repertoire_verifie="${enregistrement[0]}" if [ "$repertoire_verifie" != "$repertoire" ] then echo "Les répertoires ne correspondent pas!" # Essayez d'utiliser un fichier d'un autre répertoire. exit $E_REP_INEXISTANT fi if [ "$n" -gt 0 ] # Pas de nom de répertoire. then nomfichier[n]=$( echo ${enregistrement[$n]} | awk '{ print $2 }' ) # md5sum écrit les enregistrements après, #+ effectue un contrôle des sommes, puis du fichier. somme_controle[n]=$( md5sum "${nomfichier[n]}" ) if [ "${enregistrement[n]}" = "${somme_controle[n]}" ] then echo "${nomfichier[n]} non modifié." else echo "${nomfichier[n]} : ERREUR DE SOMME DE CONTROLE!" # Le fichier a été changé depuis la dernière vérification. fi fi let "n+=1" done <"$fichierdb" # Lit les sommes de contrôle à partir du fichier de #+ base de données. } # =================================================== # # main () if [ -z "$1" ] then repertoire="$PWD" # Si non spécifié, else #+ utilise le répertoire courant. repertoire="$1" fi clear # Efface l'écran. # ------------------------------------------------------------------ # if [ ! -r "$fichierdb" ] # Besoin de créer un fichier de base de données? then echo "Configuration de la base de données, \""$repertoire"/"$fichierdb"\"."; echo init_base_donnees fi # ------------------------------------------------------------------ # verifie_base_donnees # Fait le vrai travail. echo # Vous pouvez souhaiter rediriger stdout vers un fichier spécialement si le #+ répertoire vérifié a de nombreux fichiers. # Pour une explication sur la vérificaton d'intégrité. #+ considérez le paquetage #+ http://sourceforge.net/projects/tripwire/. exit 0


  • shred
    Efface de façon sécurisé un fichier en l'écrasant (en écrivant dessus) plusieurs fois avec des octets aléatoires avant de le supprimer. Cette commande a le même effet que Effacer les fichiers de façon sure, mais le fait de façon plus élégante et plus approfondie.Il fait partie des utilitaires GNU fileutils. Attention:Des technologies avancées peuvent toujours retrouvées le contenu d'un fichier, même après l'utilisation de shred.

  • uuencode
    Cet utilitaire code des fichiers binaires en caractères ASCII, leur permettant d'être transmis dans le corps de message email ou d'être envoyé dans un groupe de nouvelles.

  • uudecode
    Ceci inverse le codage, décode des fichiers passés par uuencode et récupère les binaires originaux.
  • Décoder des fichier codés avec uudecode
    #!/bin/bash lignes=35 # Permet 35 lignes pour l'entête (très généreux). for Fichier in * # Teste tous les fichiers du répertoire courant... do recherche1=`head -$lignes $Fichier | grep begin | wc -w` recherche2=`tail -$lignes $Fichier | grep end | wc -w` # Les fichiers uuencodés ont un "begin" près du début et un "end" près de #+ la fin. if [ "$recherche1" -gt 0 ] then if [ "$recherche2" -gt 0 ] then echo "uudecoding - $Fichier -" uudecode $Fichier fi fi done # Notez que lancer ce script sur lui-même le trompe et croie qu'il est un #+ fichier uuencodé, parce qu'il contient les mots "begin" et "end". # Exercice: # Modifier ce script pour vérifier un entête de newsgroup. exit 0
  • Astuce:La commande fold -s est utile (parfois dans un tube) pour décoder de long messages téléchargés à partir des groupes de nouvelles Usenet.

  • mimencode
    Les commandes mimencode et mmencode s'occupent du codage des pièces-jointes des courriers éléctroniques. Bien que les clients mail (MUA tels que pine ou kmail) gèrent normalement ceci automatiquement, ces utilitaires particuliers permettent de manipuler de telles pièces-jointes manuellement à partir de la ligne de commande ou dans un script shell.

  • crypt
    A un moment, il était l'utilitaire de cryptage standard sous UNIX. (30) Des régulations gouvernementales, basées sur la politique, ont interdit l'export de logiciels de cryptage, ce qui a résulté en la disparition de la commande crypt de la majeure partie du monde UNIX, et il est toujours manquant sur la plupart des distributions Linux. Heureusement, des programmeurs ont réalisé un certain nombre d'alternatives, dont celle de l'auteur cruft (voir encryptedpw: Charger un fichier sur un site ftp, en utilisant un mot de passe crypté en local).

  • mktemp
    Crée un fichier temporaire avec un nom "unique" .

  • make
    Utilitaire pour construire et compiler des paquetages binaires. Il peut aussi être utilisé pour tout type d'opérations qui seraient déclenchées par une modification des fichiers source.La commande make vérifie le Makefile, une liste de dépendances de fichiers et les opérations à réaliser.

  • install
    Commande de copie de fichier à but spécifique, similaire à cp, mais est capable de modifier les droits et attributs des fichiers copiés. Cette commande semble faite uniquement pour l'installation de paquetages, et en tant que telle, elle fait souvent son apparition dans les Makefiles (dans la section make install :). Elle pourrait aussi trouver une utilité dans les scripts d'installation.

  • ptx
    La commande ptx [fichier_cible] affiche en sortie un index permuté (liste référencée) du fichier cible. Elle peut être encore filtrée et formattée dans untube, si nécessaire.

  • more
    Programmes envoyant un fichier texte ou un flux sur stdout, un écran à la fois. Ils peuvent être utilisé pour filtrer la sortie d'un script.


Commandes de communications

Certaines des commandes suivantes trouvent leur utilité dans la chasse aux spammers, ainsi que dans les transferts réseaux et les analyses de données.

  • host
    Recherche de l'information à propos d'un hôte suivant son nom ou son adresse IP en utilisant DNS.
  • bash$ host surfacemail.com surfacemail.com. has address 202.92.42.236


  • ipcalc
    Envoie des requêtes pour des adresses IP. Avec l'option -h, ipcalc fait une recherche DNS inversée, trouvant le nom de l'hôte (serveur) à partir de l'adresse IP.
  • bash$ ipcalc -h 202.92.42.236 HOSTNAME=surfacemail.com


  • nslookup
    Lance une "recherche sur un serveur de noms" par l'adresse IP d'un hôte. Ceci est l'équivalent de ipcalc -h ou dig -x. La commande peut être lancé interactivement ou pas, donc elle est utilisable dans un script.La commande nslookup est "obsolète" , mais elle a toujours son utilité.
  • bash$ nslookup -sil 66.97.104.180 nslookup kuhleersparnis.ch Server: 135.116.137.2 Address: 135.116.137.2#53 Non-authoritative answer: Name: kuhleersparnis.ch


  • dig
    Similaire à nslookup, fait une "recherche Internet par un serveur de noms" sur un hôte. Peut être lancé interactivement ou non, donc il est utilisable à partir d'un script.Comparez la sortie de dig -x avec ipcalc -h et nslookup.
  • bash$ dig -x 81.9.6.2 ;; Got answer: ;; -HEADER- opcode: QUERY, status: NXDOMAIN, id: 11649 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0 ;; QUESTION SECTION: ;2.6.9.81.in-addr.arpa. IN PTR ;; AUTHORITY SECTION: 6.9.81.in-addr.arpa. 3600 IN SOA ns.eltel.net. noc.eltel.net. 2002031705 900 600 86400 3600 ;; Query time: 537 msec ;; SERVER: 135.116.137.2#53(135.116.137.2) ;; WHEN: Wed Jun 26 08:35:24 2002 ;; MSG SIZE rcvd: 91


  • traceroute
    Trace la route prise par les paquets envoyés à un hôte distant. Cette commande fonctionne à l'intérieur d'un LAN, WAN ou sur Internet. L'hôte distant peut être indiqué par son adresse IP. La sortie de cette commande peut être filtrée par grep ou sed via un tube.
  • bash$ traceroute 81.9.6.2 traceroute to 81.9.6.2 (81.9.6.2), 30 hops max, 38 byte packets 1 tc43.xjbnnbrb.com (136.30.178.8) 191.303 ms 179.400 ms 179.767 ms 2 or0.xjbnnbrb.com (136.30.178.1) 179.536 ms 179.534 ms 169.685 ms 3 192.168.11.101 (192.168.11.101) 189.471 ms 189.556 ms * ...


  • ping
    Envoie un paquet "ICMP ECHO_REQUEST" aux autres machines, soit sur un réseau local soit sur un réseau distant. C'est un outil de diagnostic pour tester des connections réseaux, et il devrait être utiliser avec précaution.Un ping à succès renvoie un code de sortie de 0. Ceci peut être testé dans un script.
  • bash$ ping localhost PING localhost.localdomain (127.0.0.1) from 127.0.0.1 : 56(84) bytes of data. Warning: time of day goes back, taking countermeasures. 64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=0 ttl=255 time=709 usec 64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=255 time=286 usec --- localhost.localdomain ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max/mdev = 0.286/0.497/0.709/0.212 ms


  • whois
    Réalise une recherche DNS (Domain Name System, système de nom de domaine). L'option -h permet de spécifier sur quel serveur whois envoyer la requête. Voir wh, recherche d'un nom de domaine avec whois.

  • finger
    Retrouve de l'information sur les utilisateurs d'un réseau. Optionnellement, cette commande peut afficher les fichiers ~/.plan, ~/.project et ~/.forward d'un utilisateur, si un des fichiers est présent.
  • bash$ finger Login Name Tty Idle Login Time Office Office Phone bozo Bozo Bozeman tty1 8 Jun 25 16:59 bozo Bozo Bozeman ttyp0 Jun 25 16:59 bozo Bozo Bozeman ttyp1 Jun 25 17:07 bash$ finger bozo Login: bozo Name: Bozo Bozeman Directory: /home/bozo Shell: /bin/bash On since Fri Aug 31 20:13 (MST) on tty1 1 hour 38 minutes idle On since Fri Aug 31 20:13 (MST) on pts/0 12 seconds idle On since Fri Aug 31 20:13 (MST) on pts/1 On since Fri Aug 31 20:31 (MST) on pts/2 1 hour 16 minutes idle No mail. No Plan.
  • En plus de raisons de sécurité, un grand nombre de réseaux désactive finger et son démon associé. (31)

  • vrfy
    Vérifie une adresse Internet de courrier électronique.

  • sx
    L'ensemble de commandes sx et rx sert à transférer des fichiers de et vers un hôte distant en utilisant le protocole xmodem. Ils font généralement partie d'un paquetage de communications, tel que minicom.

  • sz
    L'ensemble de commandes sz et rz sert à transférer des fichiers de et vers un hôte distant en utilisant le protocole zmodem. Zmodem a certains avantages sur xmodem, tels qu'un meilleur taux de transmission et une récupération des transferts interrompus. Comme sx et rx, ils font généralement partie d'un paquetage de communications.

  • ftp
    Utilitaire et protocole pour envoyer / recevoir des fichiers vers / de un hôte distant. Une session ftp peut être automatisée avec un script (voir upload: Envoie un fichier vers le répertoire incoming chez Sunsite, encryptedpw: Charger un fichier sur un site ftp, en utilisant un mot de passe crypté en local et ftpget: Télécharger des fichiers via ftp ).

  • cu
    Call Up (NdT: appelle) un système distant et se connecte comme simple terminal. C'est une sorte de version stupide de telnet.

  • uucp
    Copie Unix vers Unix (UNIX to UNIX copy). C'est un paquetage de communication pour transférer des fichiers entre des serveurs UNIX. Un script shell est un moyen efficace de gérer une séquence de commandes uucp.Depuis le développement d'Internet et du courrier électronique, uucp semble avoir disparu, mais il existe toujours et reste parfaitement utilisable dans des situations où des connexions Internet ne sont pas disponibles ou appropriées.

  • telnet
    Utilitaire et protocole pour se connecter à un hôte distant. Attention:Le protocole telnet contient des failles de sécurité et devrait donc être évité.

  • wget
    L'utilitaire wget recupère de façon non-interactive ou télécharge des fichiers à partir d'un site Web ou d'un site ftp. Il fonctionne bien dans un script.

  • lynx
    Le navigateur web lynx peut être utilisé dans un script (avec l'option -dump) pour récupérer un fichier d'un site web ou ftp de façon non interactive

  • rlogin
    Connexion distante, initie une session sur un hôte distant. Cette commande a des failles de sécurité, donc utilisez à la place ssh.

  • rsh
    Shell distant, exécute des commande(s) sur un hôte distant. Il a aussi des failles de sécurité, donc utilisez à la place ssh.

  • rcp
    Copie distante, copie des fichiers entre deux machines différentes. Utiliser rcp et les outils similaires a des implications en terme de sécurité. Il n'est pas conseillé de l'utiliser dans un script shell. Utilisez plutôt ssh ou un script expect.

  • ssh
    Shell sécurisé, pour se connecter sur un hôte distant et y exécuter des commandes. Ce remplacement sécurisé pour telnet, rlogin, rcp et rsh utilise authentification et cryptage. Voir sa page man pour plus de détails.

  • write
    C'est un utilitaire pour la communication terminal à terminal. Il permet d'envoyer des lignes à partir de votre terminal (console ou xterm) à un autre utilisateur. La commande mesg pourrait, bien sûr, être utilisée pour désactiver l'accès en écriture au terminal.Comme write est interactif, il a peu de chances de prouver son utilité dans un script.

  • mail
    Envoie ou lit des courriers électroniques.Ce client mail en ligne de commande est très simpliste et fonctionne bien comme commande embarquée dans un script.
  • Un script qui s'envoie un courrier
    #!/bin/sh # self-mailer.sh: Script vous envoyant un mail. adr=${1:-`whoami`} # Par défaut, l'utilisateur courant, si non spécifié. # Taper 'self-mailer.sh wiseguy@superdupergenius.com' #+ envoie ce script à cette adresse. # Taper juste 'self-mailer.sh' (sans argument) envoie le script à la personne #+ l'ayant appelé, par exemple bozo@localhost.localdomain. # # Pour plus d'informations sur la construction ${parameter:-default}, #+ voir la section "Substitution de paramètres" du chapitre "Variables #+ Revisitées." # ============================================================================ cat $0 | mail -s "Le script \"`basename $0`\" s'est envoyé lui-même à vous." "$adr" # ============================================================================ # -------------------------------------------- # Bonjour du script qui s'envoie par mail. # Une personne mal intentionnée a lancé ce script, ce qui a fait que ce mail #+ vous a été envoyé. # Apparemment, certaines personnes n'ont rien de mieux à faire de leur temps. # -------------------------------------------- echo "Le `date`, le script \"`basename $0`\" vous a été envoyé par mail sur "$adr"." exit 0


  • mailto
    Similaire à la commande mail, mailto envoie des emails à partir de la ligne de commande ou dans un script. Néanmoins, mailto permet aussi d'envoyer des messages MIME (multimedia).

  • vacation
    Cet utilitaire répond automatiquement aux courriers électroniques que le destinataire est en vacances et temporairement indisponible. Ceci tourne sur le réseau, en conjonction avec sendmail, et n'est pas applicable à un compte POP.


Commandes de contrôle du terminal

  • tput
    Initialise et/ou recherche des informations relatives à un terminal depuis les données terminfo. Certaines options permettent différentes manipulations du terminal. tput clear est l'équivalent de clear, cité plus haut. tput reset est l'équivalent de reset, cité plus haut tput sgr0 réinitialise aussi le terminal mais ne vide pas l'écran.
  • bash$ tput longname xterm terminal emulator (XFree86 4.0 Window System)
  • La commande tput cup X Y bouge le curseur à la position (X,Y) sur le terminal actuel. clear la précède généralement pour effacer l'écran.Notez que stty offre un jeu de commandes plus conséquentes pour le contrôle des terminaux.

  • infocmp
    Cette commande affiche des informations étendues sur le terminal actuel. Il fait référence à la base de données terminfo.
  • bash$ infocmp # Reconstructed via infocmp from file: /usr/share/terminfo/r/rxvt rxvt|rxvt terminal emulator (X Window System), am, bce, eo, km, mir, msgr, xenl, xon, colors#8, cols#80, it#8, lines#24, pairs#64, acsc=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l, clear=\E[H\E[2J, cnorm=\E[?25h, cr=^M, ...


  • reset
    Réinitialise les paramètres du terminal et efface son contenu. Comme avec la commande clear, le curseur réapparaît dans le coin supérieur gauche de l'écran.

  • clear
    La commande clear efface simplement le contenu textuel d'une console ou d'un xterm. Le curseur de l'invite réapparaît dans le coin supérieur gauche du terminal. Cette commande peut être utilisée en ligne de commande ou dans un script. Voir Créer des menus en utilisant case.

  • script
    Cet utilitaire sauve toutes les saisies clavier saisies dans le terminal par l'utilisateur. En fait, cela crée un enregistrement de la session.


Commandes mathématiques

  • factor
    Décompose un entier en nombre premiers.
  • bash$ factor 27417 27417: 3 13 19 37


  • bc
    Bash ne peut traiter les calculs en virgule flottante et n'intègre pas certaines fonctions mathématiques importantes. Heureusement, bc est là pour nous sauver.bc n'est pas simplement une calculatrice souple à précision arbitraire, elle offre aussi beaucoup de facilités disponibles dans un langage de programmation.La syntaxe de bc ressemble vaguement à celle du C.bc est devenu un outil UNIX assez puissant pour être utilisé via un tube et est manipulable depuis des scripts.Ceci est un simple exemple utilisant bc pour calculer la valeur d'une variable. Il utilise lasubstitution de commande.
  • variable=$(echo "OPTIONS; OPERATIONS" | bc)
  • Paiement mensuel sur une hypothèque
    #!/bin/bash # monthlypmt.sh: Calcule le paiement mensuel d'une hypothèque. # C'est une modification du code du package "mcalc" (mortgage calculator, #+ c'est-à-dire calcul d'hypothèque), de Jeff Schmidt et Mendel Cooper #+ (l'auteur de ce document). # http://www.ibiblio.org/pub/Linux/apps/financial/mcalc-1.6.tar.gz [15k] echo echo "Etant donné le montant principal, le taux d'intérêt et la fin de l'hypothèque," echo "calcule le paiement mensuel." bas=1.0 echo echo -n "Entrez le montant principal (sans virgule) " read principal echo -n "Entrez le taux d'intérêt (pourcentage) " # Si 12%, entrez "12" et non pas ".12". read taux_interet echo -n "Entrez le nb_mois (mois) " read nb_mois taux_interet=$(echo "scale=9; $taux_interet/100.0" | bc) # Convertit en décimal # "scale" détermine le nombre de décimales. taux_interet_tmp=$(echo "scale=9; $taux_interet/12 + 1.0" | bc) top=$(echo "scale=9; $principal*$taux_interet_tmp^$nb_mois" | bc) echo; echo "Merci d'être patient. Ceci peut prendre longtemps." let "mois = $nb_mois - 1" # ==================================================================== for ((x=$mois; x > 0; x--)) do bot=$(echo "scale=9; $taux_interet_tmp^$x" | bc) bas=$(echo "scale=9; $bas+$bot" | bc) # bas = $(($bas + $bot")) done # -------------------------------------------------------------------- # Rick Boivie indique une implémentation plus efficace que la boucle #+ ci-dessus, ce qui réduit le temps de calcul de 2/3. # for ((x=1; x <= $mois; x++)) # do # bas=$(echo "scale=9; $bas * $taux_interet_tmp + 1" | bc) # done # Puis, il est revenu avec une alternative encore plus efficace, #+ car elle descend le temps d'exécution de 95%! # bas=`{ # echo "scale=9; bas=$bas; taux_interet_tmp=$taux_interet_tmp" # for ((x=1; x <= $mois; x++)) # do # echo 'bas = bas * taux_interet_tmp + 1' # done # echo 'bas' # } | bc` # Intègre une 'boucle for' dans la substitution de commande. # ==================================================================== # let "paiement = $top/$bas" paiement=$(echo "scale=2; $top/$bas" | bc) # Utilise deux décimales pour les dollars et les cents. echo echo "paiement mensuel = \$$paiement" # Affiche un signe dollar devant le montant. echo exit 0 # Exercices: # 1) Filtrez l'entrée pour permettre la saisie de virgule dans le montant. # 2) Filtrez l'entrée pour permettre la saisie du taux d'intérêt en #+ pourcentage ou en décimale. # 3) Si vous êtres vraiment ambitieux, étendez ce script pour afficher # les tables d'amortissement complètes.
  • Conversion de base
    : ########################################################################## # Script shell: base.sh - affiche un nombre en différentes bases (Bourne Shell) # Auteur : Heiner Steven (heiner.steven@odn.de) # Date : 07-03-95 # Categorie : Desktop # $Id: base.sh,v 1.4 2003/12/08 20:57:22 guillaume Exp $ ########################################################################## # Description # # Modifications # 21-03-95 stv correction d'une erreur arrivant avec 0xb comme entrée (0.2) ########################################################################## # ==> Utilisé dans ce document avec la permission de l'auteur du script. # ==> Commentaires ajoutés par l'auteur du document. NOARGS=65 PN=`basename "$0"` # Nom du programme VER=`echo '$Revision: 1.4 $' | cut -d' ' -f2` # ==> VER=1.2 Usage () { echo "$PN - Affiche un nombre en différentes bases, $VER (stv '95) usage: $PN [nombre ...] Si aucun nombre n'est donné, les nombres sont lus depuis l'entrée standard. Un nombre peut être binaire (base 2) commençant avec 0b (i.e. 0b1100) octal (base 8) commençant avec 0 (i.e. 014) hexadécimal (base 16) commençant avec 0x (i.e. 0xc) décimal autrement (c'est-à-dire 12)" >&2 exit $NOARGS } # ==> Fonction pour afficher le message d'usage. Msg () { for i # ==> [liste] manquante. do echo "$PN: $i" >&2 done } Fatal () { Msg "$@"; exit 66; } AfficheBases () { # Détermine la base du nombre for i # ==> [liste] manquante... do # ==> donc opère avec le(s) argument(s) en ligne de commande. case "$i" in 0b*) ibase=2;; # binaire 0x*|[a-f]*|[A-F]*) ibase=16;; # hexadécimal 0*) ibase=8;; # octal [1-9]*) ibase=10;; # décimal *) Msg "nombre illégal $i - ignoré" continue;; esac # Suppression du préfixe, conversion des nombres hexadécimaux en #+ majuscule (bc a besoin de cela) number=`echo "$i" | sed -e 's:^0[bBxX]::' | tr '[a-f]' '[A-F]'` # ==> Utilise ":" comme séparateur sed, plutôt que "/". # Conversion des nombres en décimal dec=`echo "ibase=$ibase; $number" | bc` # ==> 'bc' est un utilitaire de #+ calcul. case "$dec" in [0-9]*) ;; # nombre ok *) continue;; # erreur: ignore esac # Affiche toutes les conversions sur une ligne. # ==> le 'document en ligne' remplit la liste de commandes de 'bc'. echo `bc <<! obase=16; "hex="; $dec obase=10; "dec="; $dec obase=8; "oct="; $dec obase=2; "bin="; $dec ! ` | sed -e 's: : :g' done } while [ $# -gt 0 ] do case "$1" in --) shift; break;; -h) Usage;; # ==> Message d'aide. -*) Usage;; *) break;; # premier nombre esac # ==> Plus de vérification d'erreur pour des entrées illégales #+ serait utile. shift done if [ $# -gt 0 ] then AfficheBases "$@" else # lit à partir de l'entrée standard #+ stdin while read ligne do PrintBases $ligne done fi
  • Une autre façon d'utiliser bc serait d'utiliser des documents en ligne embarqués dans un bloc de substitution de commandes. Cette méthode est très intéressante lorsque le script passe un grand nombre de commandes à bc
  • Une autre façon d'invoquer bc
    #!/bin/bash # Appelle 'bc' en utilisant la substitution de commandes # en combinaison avec un 'document en ligne'. var1=`bc << EOF 18.33 * 19.78 EOF ` echo $var1 # 362.56 # La notation $( ... ) fonctionne aussi. v1=23.53 v2=17.881 v3=83.501 v4=171.63 var2=$(bc << EOF scale = 4 a = ( $v1 + $v2 ) b = ( $v3 * $v4 ) a * b + 15.35 EOF ) echo $var2 # 593487.8452 var3=$(bc -l << EOF scale = 9 s ( 1.7 ) EOF ) # Renvoie le sinus de 1,7 radians. # L'option "-l" appelle la bibliothèque mathématique de 'bc'. echo $var3 # .991664810 # Maintenant, essayez-la dans une fonction... hyp= # Déclarez une variable globale. hypotenuse () # Calculez l'hypoténuse d'un triangle à angle droit. { hyp=$(bc -l << EOF scale = 9 sqrt ( $1 * $1 + $2 * $2 ) EOF ) # Malheureusement, on ne peut pas renvoyer de valeurs à virgules flottantes à #+ partir d'une fonction Bash. } hypotenuse 3.68 7.31 echo "hypoténuse = $hyp" # 8.184039344 exit 0
  • Calculer PI
    #!/bin/bash # cannon.sh: Approximation de PI en tirant des balles de canon. # C'est une très simple instance de la simulation "Monte Carlo", un modèle #+ mathématique d'un événement réel, en utilisant des nombres pseudo-aléatoires #+ pour émuler la chance. # Considérez un terrain parfaitement carré, de 10000 unités par côté. # Ce terrain comprend un lac parfaitement circulaire en son centre d'un #+ diamètre de 10000 units. # Ce terrain ne comprend donc que de l'eau sauf dans ses quatre coins. # (un carré comprenant un cercle.) # # Tirons des balles de canon à partir d'un vieux canon situé sur un des côtés #+ du terrain. # Tous les tirs créent des impacts quelque part sur le terrain, soit dans le #+ lac soit dans un des coins secs. # Comme le lac prend la majorité de l'espace disponible sur ce terrain, la #+ plupart des tirs va tomber dans l'eau. # Seuls quelques tirs tomberont sur un sol rigide compris dans les coins du #+ terrain. # # Si nous prenons assez de tirs non visés et au hasard, #+ alors le ratio des coups dans l'eau par rapport au nombre total sera #+ approximativement de PI/4. # Exercice: Expliquez pourquoi. # # Théoriquement, plus de tirs sont réalisés, plus cela correspondra. # Néanmoins, un script shell, contrairement à un langage compilé avec un #+ support des calculs à virgule flottante, nécessite quelques compromis. # Ceci tend à rendre la simulation moins précise malheureusement. DIMENSION=10000 # Longueur de chaque côté du terrain. # Initialise aussi le nombre d'entiers générés au hasard. NB_TIRS_MAX=1000 # Tire ce nombre de fois. # 10000 ou plus serait mieux mais prendrait bien plus de temps. PMULTIPLIEUR=4.0 # Facteur d'échelle pour l'approximation de PI. au_hasard () { RECHERCHE=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }') HASARD=$RECHERCHE # Du script d'exemple "seeding-random.sh" let "rnum = $HASARD % $DIMENSION" # Echelle plus petite que 10000. echo $rnum } distance= # Déclaration de la variable globale. hypotenuse () # Calcule de l'hypoténuse d'un triangle à angle droit. { # A partir de l'exemple "alt-bc.sh". distance=$(bc -l << EOF scale = 0 sqrt ( $1 * $1 + $2 * $2 ) EOF ) # Initialiser "scale" à zéro fait que le résultat sera une valeur entière, un #+ compris nécessaire dans ce script. # Ceci diminue l'exactitude de la simulation malheureusement. } # main() { # Initialisation des variables. tirs=0 dans_l_eau=0 sur_terre=0 Pi=0 while [ "$tirs" -lt "$NB_TIRS_MAX" ] # Boucle principale. do xCoord=$(au_hasard) # Obtenir les coordonnées X et Y au # hasard. yCoord=$(au_hasard) hypotenuse $xCoord $yCoord # Hypoténuse du triangle = #+ distance. ((tirs++)) printf "#%4d " $tirs printf "Xc = %4d " $xCoord printf "Yc = %4d " $yCoord printf "Distance = %5d " $distance # Distance à partir du centre #+ du lac if [ "$distance" -le "$DIMENSION" ] then echo -n "Dans l'eau ! " ((dans_l_eau++)) else echo -n "Sur terre ! " ((sur_terre++)) fi Pi=$(echo "scale=9; $PMULTIPLIEUR*$dans_l_eau/$tirs" | bc) # Multipliez le ratio par 4.0. echo -n "PI ~ $Pi" echo done echo echo "Après $tirs tirs, PI ressemble approximativement à $Pi." # Tend à être supérieur. # Probablement dû aux erreurs d'arrondi et au hasard perfectible de $RANDOM. echo # } exit 0 # On peut se demander si un script shell est approprié pour une application #+ aussi complexe et aussi intensive en calcul. # # Il existe au moins deux justifications. # 1) La preuve du concept: pour montrer que cela est possible. # 2) Pour réaliser un prototype et tester les algorithmes avant de le réécrire #+ dans un langage compilé de haut niveau.


  • dc
    L'utilitaire dc (desk calculator) est orienté pile et utilise la "notation polonaise inversée" (RPN). Commebc, Comme bc, il possède les bases d'un langage de programmation.La plupart des gens évitent dc, parce qu'il nécessite de saisir les entrées en RPN, ce qui n'est pas très intuitif. Toutefois, cette commande garde son utilité.
  • Convertir une valeur décimale en hexadecimal
    #!/bin/bash # hexconvert.sh: Convertit un nombre décimal en hexadecimal. BASE=16 # Hexadecimal. if [ -z "$1" ] then echo "Usage: $0 nombre" exit $E_SANSARGS # A besoin d'un argument en ligne de commande. fi # Exercice: ajouter une vérification de la validité de l'argument. hexcvt () { if [ -z "$1" ] then echo 0 return # "Renvoie" 0 si aucun argument n'est passé à la fonction. fi echo ""$1" "$BASE" o p" | dc # "o" demande une sortie en base numérique. # "p" Affiche le haut de la pile. # Voir 'man dc' pour plus d'options. return } hexcvt "$1" exit 0
  • L'étude de la page d'info de la commande dc donne une idée de sa complexité. Il semble cependant qu'une poignée de connaisseurs de dc se délectent de pouvoir exiber leur maîtrise de cet outil puissant mais mystérieux.
  • Factoring
    #!/bin/bash # factr.sh: Factorise un nombre MIN=2 # Ne fonctionnera pas pour des nombres plus petits que celui-ci. E_SANSARGS=65 E_TROPPETIT=66 if [ -z $1 ] then echo "Usage: $0 nombre" exit $E_SANSARGS fi if [ "$1" -lt "$MIN" ] then echo "Le nombre à factoriser doit être supérieur ou égal à $MIN." exit $E_TROPPETIT fi # Exercice: Ajouter une vérification du type (pour rejeter les arguments non #+ entiers). echo "Les facteurs de $1:" # --------------------------------------------------------------------------------- echo "$1[p]s2[lip/dli%0=1dvsr]s12sid2%0=13sidvsr[dli%0=1lrli2+dsi!>.]ds.xd1<2" | dc # --------------------------------------------------------------------------------- # La ligne de code ci-dessus a été écrit par Michel Charpentier charpov@cs.unh.edu. # Utilisé avec sa permission (merci). exit 0


  • awk
    Une autre façon d'utiliser les nombres à virgule flottante est l'utilisation des fonctions internes de la commande awk dans un emballage shell .
  • Calculer l'hypoténuse d'un triangle
    #!/bin/bash # hypotenuse.sh: Renvoie l'"hypoténuse" d'un triangle à angle droit, # (racine carrée de la somme des carrés des côtés) ARGS=2 # Le script a besoin des côtés du triangle. E_MAUVAISARGS=65 # Mauvais nombre d'arguments. if [ $# -ne "$ARGS" ] # Teste le nombre d'arguments du script. then echo "Usage: `basename $0` cote_1 cote_2" exit $E_MAUVAISARGS fi SCRIPTAWK=' { printf( "%3.7f\n", sqrt($1*$1 + $2*$2) ) } ' # commande(s) / paramètres passés à awk echo -n "Hypoténuse de $1 et $2 = " echo $1 $2 | awk "$SCRIPTAWK" exit 0



Commandes diverses

  • jot
    Ces outils génèrent des séquences de nombres entiers avec une incrémentation choisie par l'utilisateur.Le retour à la ligne qui sépare habituellement les entiers peut être modifié avec l'option -s.
  • bash$ seq 5 1 2 3 4 5 bash$ seq -s : 5 1:2:3:4:5
  • jot et seq sont fort pratiques pour les boucles.
  • Utiliser seq pour générer l'incrément d'une boucle
    #!/bin/bash # Utiliser "seq" echo for a in `seq 80` # ou for a in $( seq 80 ) # Identique à for a in 1 2 3 4 5 ... 80 (évite beaucoup de frappe!). # Pourrait aussi utiliser 'jot' (si présent sur le système). do echo -n "$a " done # 1 2 3 4 5 ... 80 # Exemple d'utilisation de la sortie d'une commande pour générer la [liste] # dans une boucle "for". echo; echo COMPTEUR=80 # Oui, 'seq' peut aussi prendre un compteur remplaçable. for a in `seq $COMPTEUR` # ou for a in $( seq $COMPTEUR ) do echo -n "$a " done # 1 2 3 4 5 ... 80 echo; echo DEBUT=75 FIN=80 for a in `seq $DEBUT $FIN` # Donner à "seq" deux arguments permet de commencer le comptage au premier et #+ de le terminer au second. do echo -n "$a " done # 75 76 77 78 79 80 echo; echo DEBUT=45 INTERVALLE=5 FIN=80 for a in `seq $DEBUT $INTERVALLE $FIN` # Donner à "seq" trois arguments permet de commencer le comptage au premier, #+ d'utiliser le deuxième comme intervalle et de le terminer au troisième. do echo -n "$a " done # 45 50 55 60 65 70 75 80 echo; echo exit 0


  • getopt
    La commande getopt analyse les paramètres de la ligne de commande précédés par un tiret. Cette commande externe correspond à une version moins souple de la commande interne getopts .
  • Utiliser getopt pour analyser les paramètres de la ligne de commande
    #!/bin/bash # Essayez ce qui suit lors de l'appel à ce script. # sh ex33a -a # sh ex33a -abc # sh ex33a -a -b -c # sh ex33a -d # sh ex33a -dXYZ # sh ex33a -d XYZ # sh ex33a -abcd # sh ex33a -abcdZ # sh ex33a -z # sh ex33a a # Expliquez les résultats de chacun. E_OPTERR=65 if [ "$#" -eq 0 ] then # Le script a besoin d'au moins un argument en ligne de commande. echo "Usage $0 -[options a,b,c]" exit $E_OPTERR fi set -- `getopt "abcd:" "$@"` # Positionne les paramètres de position par rapport aux arguments en ligne de #+ commandes. # Qu'arrive-t'il si vous utilisez "$*" au lieu de "$@"? while [ ! -z "$1" ] do case "$1" in -a) echo "Option \"a\"";; -b) echo "Option \"b\"";; -c) echo "Option \"c\"";; -d) echo "Option \"d\" $2";; *) break;; esac shift done # Il est mieux d'utiliser la commande intégrée 'getopts' dans un script, #+ plutôt que 'getopt'. # Voir "ex33.sh". exit 0


  • run-parts
    La commande run-parts (32) exécutent tous les scripts d'un répertoire cible triés par ordre ASCII. Evidement, ces scripts nécessitent les droits d'exécution.Le démon crond lance run-parts pour exécuter les scripts du répertoire /etc/cron.*.

  • yes
    Par défaut , la commande yes envoie une suite infinie de lettres y suivies de retours à la ligne sur stdout. Un ctrlc arrête l'éxécution. Une chaîne différente peut être spécifiée en argument (
  • yes chaine_differente
  • affichera continuellement chaine_differente sur stdout). On pourrait se demander l'intérêt de la chose. En pratique, yes peut être utilisé comme un expect minimaliste en étant redirigé vers un programme en attente d'une saisie expect.
  • yes | fsck /dev/hda1
  • confirme toutes les réparations à fsck (méfiance!).
  • yes | rm -r dirname
  • aura le même effet que
  • rm -rf dirname
  • (toujours méfiance!).La plus grande prudence est conseillée lorsque vous redirigez yes vers une commande potentiellement dangereuse pour votre système, comme fsck ou fdisk. Cela pourrait avoir des effets secondaires inattendus.

  • banner
    affiche les paramètres sur stdout comme une grande bannière verticale en utilisant un symbole ASCII ( # par défaut ). On peut rediriger cette sortie vers l'imprimante pour obtenir une copie papier.

  • printenv
    Montre toutes les variables d'environnement réglées pour un utilisateur donné.
  • bash$ printenv | grep HOME HOME=/home/bozo


  • lp
    Les commandes lp et lpr envoient un (des) fichier(s) à la file d'impression. (33) Ces commandes tirent l'origine de leurs noms des imprimantes "ligne par ligne" d'un autre âge.bash$
  • lp file1.txt
  • ou bash
  • lp file1.txt
  • Il est souvent utile d'envoyer le résultat de la commande pr à lp.bash$
  • pr -options file1.txt | lp
  • Les outils de mise en forme comme groff et Ghostscript peuvent directement envoyer leurs sorties à lp.bash$
  • groff -Tascii file.tr | lp
  • bash$
  • gs -options | lp file.ps
  • Les commandes sont lpq pour visualiser la file d'impression et lprm pour retirer des documents de la file d'impression.

  • tee
    [UNIX emprunte ici une idée aux commerces de tuyauterie]C'est un opérateur de redirection avec une petite différence : comme le "T" du plombier, il permet de "renvoyer" vers un fichier la sortie d'une commande ou de plusieurs commandes à l'intérieur d'un tube mais sans affecter le résultat. Ceci est utile pour envoyer le résultat du processus en cours vers un fichier ou un papier, par exemple pour des raisons de déboguages.tee |------> vers le fichier | ===============|=============== commande--->----|-operateur-->---> résultats des commandes =============================== (le fichierfichier.verif contient les contenus concaténés puis triés des fichiers "listefichiers" avant que les doublons ne soient supprimés par uniq.)

  • mkfifo
    Cette commande obscure crée un tube nommé, un tampon temporaire pour transférer les données entre les programmes sur le principe du first-in-first-out (FIFO : premier arrivé, premier sorti) (34) Classiquement, un programme écrit dans le FIFO et un autre y lit. Voir fifo: Faire des sauvegardes journalières, en utilisant des tubes nommés.

  • pathchk
    Ce programme vérifie la validité d'un nom de fichier. Il renvoie un message d'erreur si le nom excède la taille maximale autorisée (255 caractères) ou si un des répertoires du chemin est inaccessible, alors un message d'erreur est affiché.Malheureusement, pathchk ne renvoie pas un code d'erreur interprétable, ce qui le rend assez inutile dans un script. Cherchez du côté des opérateurs de tests sur les fichiers si besoin.

  • dd
    C'est une commande légèrement obscure et l'une des plus craintes des commandes de duplication des données. A l'origine, c'était un outil d'échange de données entre les bandes magnétiques des mini-ordinateurs unix et les mainframes d'IBM. Cette commande est encore utilisée à cet effet. dd copie simplement un fichier (ou stdin/stdout) mais en effectuant une conversion. ASCII/EBCDIC est une conversion possible (35) minuscule/majuscule, permutation des paires d'octets entre l'entré et la sortie, saut et troncature des entêtes et queues du fichier d'entrées, un
  • dd --help
  • affichera la liste des autres conversions possibles de ce puissant programme.Pour montrer a quel point dd est souple, utilisons le pour capturer nos saisies.
  • Capturer une saisie
    #!/bin/bash # Capture des touches clavier sans avoir besoin d'appuyer sur ENTER. touches_appuyees=4 # Nombre de touches à capturer. ancien_parametrage_du_tty=$(stty -g) # Sauve l'ancienne configuration du terminal. echo "Appuyez sur $touches_appuyees touches." stty -icanon -echo # Désactive le mode canonique. # Désactive l'echo local. touches=$(dd bs=1 count=$touches_appuyees 2> /dev/null) # 'dd' utilise stdin si "if" n'est pas spécifié. stty "$ancien_parametrage_du_tty" # Restaure l'ancien paramètrage du terminal. echo "Vous avez appuyé sur les touches \"$keys\"." # Merci, S.C. pour avoir montré la façon. exit 0
  • dd peut effectuer un accès aléatoire sur un flux de données. dd peut copier les données brutes d'un périphérique (comme un lecteur de disquette ou de bande magnétique) vers une image et inversement (copy-cd: Copier un CD de données). On l'utilise couramment pour créer des disques de démarage.
  • dd if=kernel-image of=/dev/fd0H1440
  • De la même manière, dd peut copier le contenu entier d'un disque (même formaté avec un autre OS) vers un fichier image.
  • dd if=/dev/fd0 of=/home/bozo/projects/floppy.img
  • Comme autres exemples d'applications de dd, on peut citer l'initialisation d'un fichier swap temporaire (Créer un fichier de swap en utilisant /dev/zero) ou d'un disque en mémoire (Créer un disque ram). dd peut même effectuer la copie bas-niveau d'une partition complète d'un disque dur même si la pratique n'est pas conseillée.Les gens (qui n'ont probablement rien à faire de mieux de leur temps) pensent constamment à de nouvelles applications intéressantes de dd.
  • Effacer les fichiers de façon sure
    #!/bin/bash # blotout.sh: Efface toutes les traces d'un fichier. # Ce script écrase un fichier cible avec des octets pris au hasard, puis avec #+ des zéros, avant de le supprimer définitivement. # Après cela, même l'examen des secteurs du disque ne permet pas de retrouver #+ les données du fichier d'origine. PASSES=7 # Nombre d'écriture sur le fichier. TAILLEBLOC=1 # Les entrées/sorties avec /dev/urandom requièrent la taille #+ d'un bloc, sinon vous obtiendrez des résultats bizarres. E_MAUVAISARGS=70 E_NON_TROUVE=71 E_CHANGE_D_AVIS=72 if [ -z "$1" ] # Aucun nom de fichier spécifié. then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi fichier=$1 if [ ! -e "$fichier" ] then echo "Le fichier \"$fichier\" est introuvable." exit $E_NON_TROUVE fi echo; echo -n "Etes-vous absolument sûr de vouloir complètement écraser \"$fichier\" (o/n)? " read reponse case "$reponse" in [nN]) echo "Vous avez changé d'idée, hum?" exit $E_CHANGE_D_AVIS ;; *) echo "Ecrasement du fichier \"$fichier\".";; esac longueur_fichier=$(ls -l "$fichier" | awk '{print $5}') # Le 5e champ correspond à la taille du fichier. nb_passe=1 echo while [ "$nb_passe" -le "$PASSES" ] do echo "Passe #$nb_passe" sync # Vider les tampons. dd if=/dev/urandom of=$fichier bs=$TAILLEBLOC count=$longueur_fichier # Remplir avec des octets pris au hasard. sync # Vider de nouveau les tampons. dd if=/dev/zero of=$fichier bs=$TAILLEBLOC count=$longueur_fichier # Remplir avec des zéros. sync # Vider encore une fois les tampons. let "nb_passe += 1" echo done rm -f $fichier # Finalement, la suppression a complètement écrasé le fichier. sync # Vide les tampons une dernière fois. echo "Le fichier \"$fichier\" a été complètement écrasé et supprimé."; echo # C'est une méthode assez sécurisée, mais inefficace et lente pour massacrer #+ un fichier. La commande "shred", faisant partie du paquetage GNU "fileutils", #+ fait la même chose mais de façon plus efficace. # Le fichier ne peut pas être récupéré par les méthodes habituelles. # Néanmoins... #+ cette simple méthode ne pourra certainement *pas* résister à des #+ analyses plus poussées. # Le paquetage de suppression de fichier "wipe" de Tom Vier fait un travail #+ bien plus en profondeur pour massacrer un fichier que ce simple script. # http://www.ibiblio.org/pub/Linux/utils/file/wipe-2.0.0.tar.bz2 # Pour une analyse en détail du thème de la suppression de fichier et de la #+ sécurité, voir le papier de Peter Gutmann, #+ "Secure Deletion of Data From Magnetic and Solid-State Memory". # http://www.cs.auckland.ac.nz/~pgut001/secure_del.html exit 0


  • od
    Le filtre od (pour octal dump) convertit l'entré en octal (base 8) ou dans une autre base. C'est très utile pour voir ou traiter des fichiers binaires ou d'autres sources de données illisibles comme /dev/urandom. Voir Réinitialiser RANDOM et Générer des nombres aléatoires de 10 chiffres.

  • hexdump
    Effectue une image hexadécimale, octale, décimale ou ASCII d'un fichier binaire. hexdump est un équivalent moins complet d' od, traité ci-dessus.

  • objdump
    Affiche un objet ou un exécutable binaire sous sa forme hexadécimale ou en tant que code désassemblé (avec l'option -d).
  • bash$ objdump -d /bin/ls /bin/ls: file format elf32-i386 Disassembly of section .init: 080490bc .init: 80490bc: 55 push %ebp 80490bd: 89 e5 mov %esp,%ebp . . .


  • mcookie
    Cette commande génère un fichier témoin ( "magic cookie" ), un nombre hexadécimal pseudo-aléatoire de 128 bits (32 caractères) qui est habituellement utilisé par les serveurs X comme "signature" pour l'authentification. Elle peut être utilisée dans un script comme une solution sale mais rapide pour générer des nombres aléatoires. Evidemment, un script peut utiliser md5 pour obtenir le même résultat. mcookie est aussi une autre facon de générer un nom de fichier "unique" .
  • Générateur de nom de fichier
    #!/bin/bash # tempfile-name.sh: générateur de fichier temporaire. BASE_STR=`mcookie` # Chaîne magique de 32 caractères. POS=11 # Position arbitraire dans la chaîne magique. LONG=5 # Pour obtenir $LONG caractères consécutifs. prefixe=temp # C'est après tout un fichier "temp"oraire. # Pour que le nom soit encore plus "unique", génère le #+ préfixe du nom du fichier en utilisant la même méthode #+ que le suffixe ci-dessous. suffixe=${BASE_STR:POS:LONG} # Extrait une chaîne de cinq caractères, commençant à la # position 11. nomfichiertemporaire=$prefixe.$suffixe # Construction du nom du fichier. echo "Nom du fichier temporaire = "$nomfichiertemporaire"" # sh tempfile-name.sh # Nom du fichier temporaire = temp.e19ea exit 0


  • units
    Généralement appelé de façon interactive, cet utilitaire peut être utilisé dans un script. Il sert à convertir des mesures en différentes unités.
  • Convertir des mètres en miles
    #!/bin/bash # unit-conversion.sh convertir_unites () # Prend comme arguments les unit?s ? convertir. { cf=$(units "$1" "$2" | sed --silent -e '1p' | awk '{print $2}') # Supprime tout sauf le facteur de conversion. echo "$cf" } Unite1=miles Unite2=meters facteur_conversion=`convertir_unites $Unite1 $Unite2` quantite=3.73 resultat=$(echo $quantite*$facteur_conversion | bc) echo "Il existe $resultat $Unite2 dans $quantite $Unit1." # Que se passe-t'il si vous donnez des unit?s incompatibles, telles que #+ "acres" et "miles"? exit 0


  • m4
    Trésor caché, m4 est un puissant filtre de traitement des macros. (36) Langage pratiquement complet, m4 fut écrit comme pré-processeur pour RatFor avant de s'avérer être un outil autonome très utile. En plus de ses possibilités étendues d'interpolation de macros, m4 intègre les fonctionnalités d'eval, tr et awk.Un très bon article sur m4 et ses utilisations a été écrit pour le numéro d'avril 2002 du Linux Journal.
  • Utiliser m4
    #!/bin/bash # m4.sh: Utiliser le processeur de macros m4 # Chaîne de caractères chaine=abcdA01 echo "len($chaine)" | m4 # 7 echo "substr($chaine,4)" | m4 # A01 echo "regexp($chaine,[0-1][0-1],\Z)" | m4 # 01Z # Arithmétique echo "incr(22)" | m4 # 23 echo "eval(99 / 3)" | m4 # 33 exit 0


  • doexec
    doexec permet de transmettre une liste arbitraire d'arguments à un binaire exécutable. En particulier , le fait de transmettre argv[0] (qui correspond à $0 dans un script) permet à l'exécutable d'être invoqué avec des noms différents et d'agir en fonction de cette invocation. Ceci n'est qu'une autre façon de passer des options à un exécutable.Par exemple , le répertoire /usr/local/bin peut contenir un binaire appelé "aaa" . doexec /usr/local/bin/aaa list affichera la liste de tous les fichiers du répertoire courant qui commencent par un "a" . Appeler le même binaire par doexec /usr/local/bin/aaa delete détruira ces fichiers. Remarque:Les différentes actions d'un executable doivent être définies à l'interieur du code exécutable lui-même. De façon similaire au script suivant :


3.4. Commandes système et d'administration

Les scripts de démarrage et d'arrêt du répertoire /etc/rc.d illustrent l'utilisation (et l'intérêt) de ces commandes. Elles sont généralement appelées par root et utilisées pour la maintenance du système ou pour des réparation en urgence du système de fichiers. Utilisez-les avec précaution car certaines de ces commandes peuvent endommager votre système en cas de mauvaise utilisation.

  • users
    Affiche tous les utilisateurs connectés. Ceci est l'équivalent approximatif de who -q.

  • groups
    Affiche l'utilisateur actuel et les groupes auxquels il appartient. Ceci correspond à la variable interne $GROUPS mais donne les noms des groupes plutôt que leur numéro.bash$ groups bozita cdrom cdwriter audio xgrp bash$ echo $GROUPS 501

  • chown
    La commande chown modifie le propriétaire d'un ou plusieurs fichiers. Cette commande est un méthode utile qu'utilise root pour modifier le propriétaire d'un fichier. Un utilisateur ordinaire peut ne pas pouvoir changer le propriétaire des fichiers, même pas ses propres fichiers. (37)
  • root# chown bozo *.txt
  • La commande chgrp modifie le groupe d'un ou plusieurs fichiers. Vous devez être le propriétaire du fichier ainsi qu'un membre du groupe de destination (ou root) pour réaliser cette opération.

  • useradd
    La commande d'administration useradd ajoute un compte utilisateur au système et crée un répertoire personnel pour cet utilisateur particulier si cela est demandé. La commande correspondante userdel supprime le compte de l'utilisateur du système (38) et supprime les fichiers associés. Remarque:La commande adduser est un synonyme de useradd et est habituellement un lien symbolique vers ce dernier.

  • id
    La commande id affiche les identifiants effectifs de l'utilisateur et du groupe pour l'utilisateur actuel. C'est la contre-partie des variables internes Bash $UID, $EUID et $GROUPS.bash$ id uid=501(bozo) gid=501(bozo) groups=501(bozo),22(cdrom),80(cdwriter),81(audio) bash$ echo $UID 501Voir aussi Suis-je root?.

  • who
    Affiche tous les utilisateurs connectés sur le système.
  • bash$ who bozo tty1 Apr 27 17:45 bozo pts/0 Apr 27 17:46 bozo pts/1 Apr 27 17:47 bozo pts/2 Apr 27 17:49
  • L'option -m donne des informations détaillées sur l'utilisateur actuel. Passer n'importe quels arguments, à condition qu'il en ait deux, à who est l'équivalent de who -m, comme dans in who am i ou who The Man.
  • bash$ who -m localhost.localdomain!bozo pts/2 Apr 27 17:49
  • whoami est similaire à who -m mais affiche seulement le nom de l'utilisateur.
  • bash$ whoami bozo


  • w
    Affiche tous les utilisateurs connectés et le processus leur appartenant. C'est une version étendue de who. La sortie de w peut être envoyée via un tube vers grep pour trouver un utilisateur et/ou un processus spécifique.bash$ w | grep startx bozo tty1 - 4:22pm 6:41 4.47s 0.45s startx

  • logname
    Affiche le nom de connexion de l'utilisateur actuel (disponible dans /var/run/utmp). C'est presque l'équivalent de whoami, ci-dessus.bash$ logname bozo bash$ whoami bozoNéanmoins...bash$ su Password: ...... bash# whoami root bash# logname bozo

  • su
    Lance un programme ou un script en substituant l'utilisateur (substitue l'utilisateur). su rjones lance un shell en tant qu'utilisateur rjones. Une commande su sans arguments utilise root par défaut. Voir fifo: Faire des sauvegardes journalières, en utilisant des tubes nommés.

  • sudo
    Lance une commande en tant que root (ou un autre utilisateur). Ceci peut être utilisé dans un script, permettant ainsi à un utilisateur standard de lancer un script.Le fichier /etc/sudoers contient le nom des utilisateurs ayant le droit d'appeller sudo.

  • passwd
    Initialise ou modifie le mot de passe d'un utilisateur.passwd peut être utilisé dans un script, mais ne devrait pas l'être.

  • ac
    Affiche le temps de connexion des utilisateurs actuellement connectés, à partir des informations lues dans /var/log/wtmp. Il fait partie des utilitaires de mesure GNU.bash$ ac total 68.08

  • last
    Affiche les derniers (last en anglais) utilisateurs connectés, suivant les informations disponibles dans /var/log/wtmp. Cette commande peut aussi afficher les connexions distantes.

  • newgrp
    Modifie l'identifiant du groupe de l'utilisateur sans se déconnecter. Ceci permet l'accès aux fichiers du nouveau groupe. Comme les utilisateurs pourraient être des membres de plusieurs groupes simultanément, cette commande a peu d'utilité.

  • tty
    Affiche le nom du terminal de l'utilisateur actuel. Notez que chaque fenêtre xterm compte comme un terminal séparé.bash$ tty /dev/pts/1

  • stty
    Affiche et/ou modifie les paramètrages du terminal. Cette commande complexe, utilisée dans un script, peut contrôler le comportement du terminal et la façon dont il affiche des caractères. Voir la page info et l'étudier en profondeur.
  • configurer un caractère d'effacement
    #!/bin/bash # erase.sh: Utilisation de "stty" pour initialiser un caractère d'effacement # lors de la lecture de l'entrée standard. echo -n "Quel est ton nom? " read nom # Essayez d'effacer quelques caractères. # Ne fonctionnera pas. echo "Votre nom est $nom." stty erase '#' # Initialisation de la "dièse" (#) comme # caractère d'effacement. echo -n "Quel est ton nom? " read nom # Utilisez # pour effacer le dernier caractère # tapé. echo "Votre nom est $nom." exit 0
  • mot de passe secret: Désactiver l'écho du terminal
    #!/bin/bash echo echo -n "Entrez le mot de passe " read mot_de_passe echo "Le mot de passe est $mot_de_passe" echo -n "Si quelqu'un a regardé par dessus votre épaule, " echo "votre mot de passe pourrait avoir été compromis." echo && echo # Deux retours chariot dans une "liste ET". stty -echo # Supprime l'affichage sur l'écran. echo -n "Entrez de nouveau le mot de passe " read mot_de_passe echo echo "Le mot de passe est $mot_de_passe" echo stty echo # Restaure l'affichage sur l'écran. exit 0
  • Une utilisation originale de stty concerne la détection de l'appui d'une touche (sans appuyer sur ENTER).
  • Détection de l'appui sur une touche
    #!/bin/bash # keypress.sh: Détecte un appui sur une touche ("hot keyboard"). echo ancienne_config_tty=$(stty -g) # Sauvegarde de l'ancienne configuration. stty -icanon Appui_touche=$(head -c1) # ou $(dd bs=1 count=1 2> /dev/null) # sur les systèmes non-GNU echo echo "La touche est \""$Appui_touche"\"." echo stty "$ancienne_config_tty" # Restaure l'ancienne configuration. # Merci, Stephane Chazelas. exit 0
  • Voir aussi Encore une fois, saisie avec délai.Normalement, un terminal fonctionne en mode canonique. Lorsque l'utilisateur appuie sur une touche, le caractère correspondant ne va immédiatement au programme en cours sur le terminal, Un tampon local au terminal enregistre les frappes clavier. Lorsqu'un utilisateur appuie sur la touche ENTER, il envoie toutes les touches frappées au programme en cours. Il existe même un éditeur ligne basique dans le terminal.
  • bash$ stty -a speed 9600 baud; rows 36; columns 96; line = 0; intr = ^C; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = undef; eol2 = undef; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; ... isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
  • En utilisant le mode canonique, il est possible de redéfinir les touches spéciales pour l'éditeur ligne local du terminal.
  • bash$ cat > filexxx whactl-WIctl-Hfoo barctl-Uhello worldENTER ctl-D bash$ cat filexxx hello world bash$ bash$ wc -c < file 13
  • Le processus contrôlant le terminal reçoit seulement 13 caractères (12 alphabétiques, plus le retour chariot), bien que l'utilisateur ait appuyé sur 26 touches. Dans un mode non canonique ( "raw" ), chaque appui sur une touche (y compris les touches spéciales d'édition telles que ctl-H) envoie un caractère immédiatement au processus de contrôle.L'invite Bash désactive à la fois icanon et echo, car il remplace l'éditeur ligne basique du terminal avec son propre éditeur plus élaboré. Par exemple, lorsque vous appuyez sur ctl-A à l'invite Bash, aucun ^A n'est affiché par le terminal mais Bash obtient un caractère \1, l'interprète et déplace le curseur en début de ligne.Stephane Chazelas

  • tset
    Affiche ou initialise les paramètrages du terminal. C'est une version stty comprenant moins de fonctionnalités.
  • bash$ tset -r Terminal type is xterm-xfree86. Kill is control-U (^U). Interrupt is control-C (^C).


  • setserial
    Initialise ou affiche les paramètres du port série. Cette commande doit être lancé par l'utilisateur root et est habituellement utilisée dans un script de configuration du système.

  • getty
    Le processus d'initialisation d'un terminal utilise getty ou agetty pour demander le nom de connexion d'un utilisateur. Ces commandes ne sont pas utilisées dans des scripts shell d'utilisateurs. Leur contre-partie script est stty.

  • mesg
    Active ou désactive les droits d'écriture sur le terminal de l'utilisateur actuel. Désactiver l'accès empêcherait tout utilisateur sur le réseau d'écrire (write en anglais) sur le terminal. Astuce:Il peut être très ennuyant de voir apparaître un message pour une commande de pizza au milieu du fichier texte en cours d'édition. Sur un réseau multi-utilisateur, vous pourriez du coup souhaiter désactiver les droits d'écriture sur votre terminal lorsque vous ne voulez pas être dérangé.

  • wall
    C'est un acronyme pour "write all" , c'est-à-dire écrire un message à tous les utilisateurs sur tous les terminaux connectés sur le réseau. C'est essentiellement un outil pour l'administrateur système, utile par exemple pour prévenir tout le monde que le système sera bientôt arrêté à cause d'un problème (voir broadcast: Envoie un message à chaque personne connectée).
  • bash$ wall Système arrêté pour maintenance dans 5 minutes! Broadcast message from bozo (pts/1) Sun Jul 8 13:53:27 2001... Système arrêté pour maintenance dans 5 minutes!
  • Remarque:Si le droit d'écriture sur un terminal particulier a été désactivé avec mesg, alors wall ne pourra pas lui envoyer un message.

  • dmesg
    Affiche tous les messages de démarrage du système envoyés à stdout. Pratique pour déboguer, pour s'assurer des pilotes de périphériques qui ont été installés et des interruptions système utilisées. La sortie de dmesg pourrait, bien sûr, être analysée avec grep, sed, ou awk à l'intérieur d'un script.
  • bash$ dmesg | grep hda Kernel command line: ro root=/dev/hda2 hda: IBM-DLGA-23080, ATA DISK drive hda: 6015744 sectors (3080 MB) w/96KiB Cache, CHS=746/128/63 hda: hda1 hda2 hda3 < hda5 hda6 hda7 > hda4


  • uname
    Affiche les spécifications du système (OS, version du noyau, etc.) sur stdout. Appelé avec l'option -a, donne plus d'informations sur le système (voir Fichier de traces utilisant xargs pour surveiller les logs système). L'option -s affiche seulement le type de l'OS.bash$ uname -a Linux localhost.localdomain 2.2.15-2.5.0 #1 Sat Feb 5 00:13:43 EST 2000 i686 unknown bash$ uname -s Linux

  • arch
    Affiche l'architecture du système. Equivalent à uname -m. Voir Utiliser la substitution de commandes pour générer la variable case.bash$ arch i686 bash$ uname -m i686

  • lastcomm
    Donne une information sur les dernières commandes, disponibles dans le fichier /var/account/pacct. Le nom de la commande et de l'utilisateur peuvent être spécifiés en options. Elle fait partie des utilitaires de comptage GNU.

  • lastlog
    Affiche la dernière connexion de tous les utilisateurs système. Ceci prend comme référence le fichier /var/log/lastlog.
  • bash$ lastlog root tty1 Fri Dec 7 18:43:21 -0700 2001 bin **Never logged in** daemon **Never logged in** ... bozo tty1 Sat Dec 8 21:14:29 -0700 2001 bash$ lastlog | grep root root tty1 Fri Dec 7 18:43:21 -0700 2001
  • Attention:Cette commande échouera si l'utilisateur l'appellant n'a pas des droits de lecture sur le fichier /var/log/lastlog.

  • lsof
    Affiche les fichiers ouverts. Cette commande affiche une table détaillée de tous les fichiers ouverts et donne de l'information sur leur propriétaire, taille, processus associés et bien plus encore. Bien sûr, lsof pourrait être redirigé avec un tube vers grep et/ou awk pour analyser ce résultat.
  • bash$ lsof COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME init 1 root mem REG 3,5 30748 30303 /sbin/init init 1 root mem REG 3,5 73120 8069 /lib/ld-2.1.3.so init 1 root mem REG 3,5 931668 8075 /lib/libc-2.1.3.so cardmgr 213 root mem REG 3,5 36956 30357 /sbin/cardmgr ...


  • strace
    Outil de diagnostique et de déboguage des appels systèmes et des signaux. La façon la plus simple de l'appeller est strace COMMANDE.
  • bash$ strace df execve("/bin/df", ["df"], [/* 45 vars */]) = 0 uname({sys="Linux", node="bozo.localdomain", ...}) = 0 brk(0) = 0x804f5e4 ...
  • C'est l'équivalent Linux de truss.

  • nmap
    Scanner de port réseau. Cette commande parcourt les ports d'un serveur pour localiser les ports ouverts et les services associés à ces ports. C'est un important outil de sécurité pour verrouiller un réseau contre les tentatives de pirates.

  • free
    Affiche l'utilisation de la mémoire et du cache sous forme de tableau. La sortie de cette commande tend à être analyser avec l'utilisation de grep, awk ou Perl. La commande procinfo affiche toute l'information dont dispose la commande free et bien plus encore.bash$ free total used free shared buffers cached Mem: 30504 28624 1880 15820 1608 16376 -/+ buffers/cache: 10640 19864 Swap: 68540 3128 65412Pour afficher la mémoire RAM inutilisée:bash$ free | grep Mem | awk '{ print $4 }' 1880

  • procinfo
    Extrait et affiche des informations et des statistiques à partir du pseudo système de fichiers /proc. Cela donne une liste très détaillée.bash$ procinfo | grep Bootup Bootup: Wed Mar 21 15:15:50 2001 Load average: 0.04 0.21 0.34 3/47 6829

  • lsdev
    Affiche les périphériques, c'est-à-dire le matériel installé.
  • bash$ lsdev Device DMA IRQ I/O Ports ------------------------------------------------ cascade 4 2 dma 0080-008f dma1 0000-001f dma2 00c0-00df fpu 00f0-00ff ide0 14 01f0-01f7 03f6-03f6 ...


  • du
    Affiche l'utilisation du disque, de façon récursive. Par défaut, il prend en compte le répertoire courant.bash$ du -ach 1.0k ./wi.sh 1.0k ./tst.sh 1.0k ./random.file 6.0k . 6.0k total

  • df
    Affiche l'utilisation des systèmes de fichiers sous forme de tableau.bash$ df Filesystem 1k-blocks Used Available Use% Mounted on /dev/hda5 273262 92607 166547 36% / /dev/hda8 222525 123951 87085 59% /home /dev/hda7 1408796 1075744 261488 80% /usr

  • stat
    Donnes des statistiques détaillées, voire verbeuses, sur un fichier donné (voire un répertoire ou un fichier périphérique) ou sur un ensemble de fichiers.
  • bash$ stat test.cru File: "test.cru" Size: 49970 Allocated Blocks: 100 Filetype: Regular File Mode: (0664/-rw-rw-r--) Uid: ( 501/ bozo) Gid: ( 501/ bozo) Device: 3,8 Inode: 18185 Links: 1 Access: Sat Jun 2 16:40:24 2001 Modify: Sat Jun 2 16:40:24 2001 Change: Sat Jun 2 16:40:24 2001
  • Si le fichier cible n'existe pas, stat renvoie un message d'erreur.
  • bash$ stat fichier-inexistant nonexistent-file: No such file or directory


  • vmstat
    Affiche les statistiques concernant la mémoire virtuel.
  • bash$ vmstat procs memory swap io system cpu r b w swpd free buff cache si so bi bo in cs us sy id 0 0 0 0 11040 2636 38952 0 0 33 7 271 88 8 3 89


  • netstat
    Affiche des informations et des statistiques sur le réseau, telles que les tables de routage et les connexions actives. Cet utilitaire accède à l'information avec /proc/net (). Voir Etat de la connexion.netstat -r est équivalent à route.

  • uptime
    Affiche depuis quand le système est lancé ainsi que quelques autres statistiques.bash$ uptime 10:28pm up 1:57, 3 users, load average: 0.17, 0.34, 0.27

  • hostname
    Affiche le nom d'hôte du système. Cette commande initialise le nom d'hôte dans un script de démarrage /etc/rc.d (/etc/rc.d/rc.sysinit ou similaire). C'est équivalent à uname -n, et une contrepartie de la variable interne $HOSTNAME. bash$ hostname localhost.localdomain bash$ echo $HOSTNAME localhost.localdomain

  • hostid
    Affiche un identifiant numérique (hexadécimal) sur 32 bits pour la machine hôte.
  • bash$ hostid 7f0100
  • Remarque:Cette commande récupère prétendument un numéro de série "unique" pour un système particulier. Certaines procédures d'enregistrement d'un produit utilisent ce numéro pour indiquer une licence utilisateur particulière. Malheureusement, hostid ne fait que renvoyer l'adresse réseau en héxadécimal avec quelques octets transposés. Remarque:L'adresse réseau d'une machine Linux typique ne se trouvant pas sur un réseau est disponible dans /etc/hosts. Remarque:
  • bash$ cat /etc/hosts 127.0.0.1 localhost.localdomain localhost
  • Remarque:Il arrive que la transposition de
  • 127.0.0.1
  • soit
  • 0.127.1.0
  • , ce qui donne en hexadécimal
  • 007f0100
  • , l'équivalent exact de ce que renvoit hostid, ci-dessus. Il existe seulement quelques millions d'autres machines Linux avec ce même hostid.

  • sar
    Appeller sar (system activity report) donne une indication minutée et très détaillée des statistiques système. Cette commande est disponible sur certains systèmes UNIX commerciaux mais ne fait pas partie de la distribution UNIX de base. Il appartient au package des utilitaires sysstat, écrit par Sébastien Godard.bash$ sar Linux 2.4.7-10 (localhost.localdomain) 12/31/2001 10:30:01 AM CPU %user %nice %system %idle 10:40:00 AM all 1.39 0.00 0.77 97.84 10:50:00 AM all 76.83 0.00 1.45 21.72 11:00:00 AM all 1.32 0.00 0.69 97.99 11:10:00 AM all 1.17 0.00 0.30 98.53 11:20:00 AM all 0.51 0.00 0.30 99.19 06:30:00 PM all 100.00 0.00 100.01 0.00 Average: all 1.39 0.00 0.66 97.95

  • readelf
    Affiche des informations et des statistiques sur un binaire elf indiqué. Cela fait partie du package binutils.bash$ readelf -h /bin/bash ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) . . .

  • size
    La commande size [/chemin/vers/binaire] donne les tailles des segments d'un exécutable binaire ou d'un fichier archive. C'est utile principalement pour les programmeurs.
  • bash$ size /bin/bash text data bss dec hex filename 495971 22496 17392 535859 82d33 /bin/bash


  • logger
    Ajoute un message généré par l'utilisateur dans le journal système (/var/log/messages). Vous n'avez pas besoin d'être root pour appeler logger. En embarquant une commande logger dans un script, il est possible d'écrire des informations de déboguage dans /var/log/messages.

  • logrotate
    Cet utilitaire gère les journaux système, en utilisant une rotation, en les compressant, supprimant, et/ou en les envoyant par courrier électronique. Habituellement, crond lance quotidiennement logrotate.Ajouter une entrée appropriée dans /etc/logrotate.conf rend possible la gestion de journaux personnels, ainsi que des journaux système.

  • ps
    Statistiques sur les processus (Process Statistics): affiche les processus en cours d'exécution avec leur propriétaire et identifiant de processus (PID). Celui-ci est habituellement appelé avec les options ax et le résultat peut être envoyé via un tube à grep ou sed pour repérer un processus spécifique (voir Forcer une déconnexion et Trouver le processus associé à un PID).bash$ ps ax | grep sendmail 295 ? S 0:00 sendmail: accepting connections on port 25

  • pstree
    Affiche les processus en cours d'exécution avec le format "tree" (arbre). L'option -p affiche les PID, ainsi que les noms des processus.

  • top
    Affiche les processus les plus consomnateur de puissances avec un rafaichissement permanant. L'option -b affiche en mode texte de façon à ce que la sortie puisse être analysée ou tout simplement récupérée à partir d'un script.
  • bash$ top -b 8:30pm up 3 min, 3 users, load average: 0.49, 0.32, 0.13 45 processes: 44 sleeping, 1 running, 0 zombie, 0 stopped CPU states: 13.6% user, 7.3% system, 0.0% nice, 78.9% idle Mem: 78396K av, 65468K used, 12928K free, 0K shrd, 2352K buff Swap: 157208K av, 0K used, 157208K free 37244K cached PID USER PRI NI SIZE RSS SHARE STAT %CPU %MEM TIME COMMAND 848 bozo 17 0 996 996 800 R 5.6 1.2 0:00 top 1 root 8 0 512 512 444 S 0.0 0.6 0:04 init 2 root 9 0 0 0 0 SW 0.0 0.0 0:00 keventd ...


  • nice
    Lance un job en tâche de fond avec une priorité modifiée. Les priorités vont de 19 (le plus bas) à -20 (le plus haut). Seul root peut configurer les priorités négatives (les plus hautes). Les commandes en relation sont renice, snice et skill.

  • nohup
    Conserve l'exécution d'une commande même si l'utilisateur se déconnecte. La commande s'exécutera en tant que tâche de fond sauf si il est suivi d'un &. Si vous utilisez nohup à l'intérieur d'un script, considérez le fait de le placer avec un wait pour éviter la création d'un processus orphelin ou zombie.

  • pidof
    Identifie l'identifiant du processus (pid) d'un job en cours d'exécution. Comme les commandes de contrôle de job, telles que kill et renice qui agissent sur le pid d'un processus (et non pas son nom), il est parfois nécessaire d'identifier ce pid. La commande pidof est la contrepartie approximative de la variable interne $PPID.
  • bash$ pidof xclock 880
  • pidof aide à la suppression d'un processus
    #!/bin/bash # kill-process.sh SANSPROCESSUS=2 processus=xxxyyyzzz # Utilise un processus inexistant. # Pour les besoins de la démo seulement... # ... je ne veux pas réellement tuer un processus courant avec ce script. # # Si, par exemple, vous voulez utiliser ce script pour vous déconnecter d'Internet, # processus=pppd t=`pidof $processus` # Trouve le pid (process id) de $processus. # Le pid est nécessaire pour 'kill' (vous ne pouvez pas lancer 'kill' sur un nom de #+ programme). if [ -z "$t" ] # Si le processus n'est pas présent, 'pidof' renvoie null. then echo "Le processus $processus n'est pas lancé." echo "Rien n'a été tué." exit $SANSPROCESSUS fi kill $t # Vous pouvez avoir besoin d'un 'kill -9' pour les processus fils. # Une vérification sur l'existence du processus est nécessaire ici. # Peut-être un autre " t=`pidof $processus` ". # Ce script entier pourrait être remplacé par # kill $(pidof -x processus_name) # mais cela ne serait pas aussi instructif. exit 0


  • fuser
    Identifie les processus (par pid) accédant à un fichier donné, à un ensemble de fichiers ou à un répertoire. Pourrait aussi être appelé avec l'option -k, qui tue ces processus. Ceci a des implications intéressantes pour la sécurité du système, spécialement avec des scripts empêchant des utilisateurs non autorisés d'accèder à certains services système.

  • crond
    Plannificateur de programmes d'administration, réalisant des tâches comme le nettoyage et la suppression des journaux système ainsi que la mise à jour de la base de données slocate. C'est la version superutilisateur de at (bien que chaque utilisateur peut avoir son propre fichier crontab modifiable avec la commande crontab). Il s'exécute comme un démon et exécute les entrées plannifiées dans /etc/crontab.

  • init
    La commande init est le parent de tous les processus. Appelé à l'étape finale du démarrage, init détermine le niveau d'exécution du système à partir de /etc/inittab. Appellé par son alias telinit, et par root seulement.

  • telinit
    Lien symbolique vers init, c'est un moyen de changer de niveau d'exécution, habituellement utilisé pour la maintenance système ou des réparations en urgence de systèmes de fichiers. Appelé uniquement par root. Cette commande peut être dangereuse - soyez certain de bien la comprendre avant de l'utiliser!

  • runlevel
    Affiche le niveau d'exécution actuel et ancien, c'est-à-dire si le système a été arrêté (niveau 0), était en mode simple-utilisateur (1), en mode multi-utilisateur (2 ou 3), dans X Windows (5) ou en redémarrage (6). Cette commande accède au fichier /var/run/utmp.

  • halt
    Ensemble de commandes pour arrêter le système, habituellement juste avant un arrêt.

  • ifconfig
    Configuration fine de l'interface réseau. Il est bien plus utilisé au démarrage lors de la configuration des interfaces, ou à l'arrêt lors d'un redémarrage. Voir aussi Nettoyage après un Control-C.

  • route
    Affiche des informations sur la façon de modifier la table de routage du noyau.
  • bash$ route Destination Gateway Genmask Flags MSS Window irtt Iface pm3-67.bozosisp * 255.255.255.255 UH 40 0 0 ppp0 127.0.0.0 * 255.0.0.0 U 40 0 0 lo default pm3-67.bozosisp 0.0.0.0 UG 40 0 0 ppp0


  • chkconfig
    Vérifie la configuration du réseau. Cette commande affiche et gère les services réseau lancés au démarrage dans le répertoire /etc/rc?.d.Originellement un port d'IRIX vers Red Hat Linux, chkconfig pourrait ne pas faire partie de l'installation principale des différentes distributions Linux.
  • bash$ chkconfig --list atd 0:off 1:off 2:off 3:on 4:on 5:on 6:off rwhod 0:off 1:off 2:off 3:off 4:off 5:off 6:off ...


  • tcpdump
    "Reniffleur" de paquets réseau. C'est un outil pour analyser et corriger le trafic sur un réseau par l'affichage des en-têtes de paquets correspondant à des critères précis. Affiche le trafic des paquets ip entre l'hôte bozoville et caduceus:
  • bash$ tcpdump ip host bozoville and caduceus
  • Bien sûr, la sortie de tcpdump est analysable en utilisant certains utilitaires texte préalablement discutées.

  • mount
    Monte un système de fichier, généralement sur un périphérique externe, tel qu'un lecteur de disquette ou de CDROM. Le fichier /etc/fstab comprend tous les systèmes de fichiers, partitions et périphériques disponibles pouvant être montés manuellement ou automatiquement. Le fichier /etc/mtab affiche les systèmes de fichiers et partitions actuellement montés (en incluant les systèmes virtuels tels que /proc). mount -a monte tous les systèmes de fichiers et partitions indiqués dans /etc/fstab, à l'exception de ceux disposant de l'option noauto. Au démarrage, un script de /etc/rc.d (rc.sysinit ou un similaire) appelle cette commande pour monter tout ce qui doit l'être.Cette commande versatile peut même monter un fichier ordinaire sur un périphérique bloc et ce fichier agira comme si il était un système de fichiers. Mount accomplit cela en associant le fichier à un périphérique loopback. Une application de ceci est le montage et l'examen d'une image ISO9660 avant qu'elle ne soit gravée sur un CDR. (39)
  • Vérifier une image
    # En tant que root... mkdir /mnt/cdtest # Préparez un point de montage, s'il n'existe pas déjà. mount -r -t iso9660 -o loop cd-image.iso /mnt/cdtest # Montez l'image. # l'option "-o loop" est équivalent à "losetup /dev/loop0" cd /mnt/cdtest # Maintenant, vérifiez l'image ls -alR # Listez les fichiers dans cette hiérarchie de répertoires. # Et ainsi de suite.


  • umount
    Démonte un système de fichiers actuellement montés. Avant de supprimer physiquement une disquette ou un CDROM monté au prélable, le périphérique doit être démonté (umount), sinon des corruptions du système de fichiers pourraient survenir. Remarque:L'utilitaire automount, s'il est correctement installé, peut monter et démonter des disquettes et des CDROM s'ils sont utilisés ou enlevés. Sur des portables diposant de lecteurs de disquette et CDROM enlevables, ceci peut poser des problèmes.

  • sync
    Force une écriture immédiate de toutes les données mises à jour à partir des tampons vers le disque dur (synchronisation des lecteurs avec les tampons). Bien que cela ne soit pas strictement nécessaire, sync assure à l'administrateur système et à l'utilisateur que les données tout juste modifiées survivront à une soudaine coupure de courant. Aux anciens temps, un
  • sync; sync
  • (deux fois, pour être absolument certain) était une mesure de précaution utile avant un redémarrage du système.Quelque fois, vous pouvez souhaiter forcer un vidage immédiat des tampons, comme lors de la suppression sécurisée d'un fichier (voir Effacer les fichiers de façon sure) ou lorsque les lumières commencent à clignotter.

  • losetup
    Initialise et configure les périphériques loopback.
  • Création d'un système de fichiers dans un fichier
    TAILLE=1000000 # 1 Mo head -c $TAILLE < /dev/zero > fichier # Initialise un fichier de la taille indiquée. losetup /dev/loop0 fichier # Le configure en tant que périphérique loopback. mke2fs /dev/loop0 # Crée un système de fichiers. mount -o loop /dev/loop0 /mnt # Le monte. # Merci, S.C.


  • mkswap
    Crée une partition de swap ou un fichier. L'aire de swap doit être, du coup, activé avec swapon.

  • swapon
    Active/désactive la partition de swap ou le fichier. Ces commandes sont généralement utilisées au démarrage et à l'arrêt.

  • mke2fs
    Crée un système de fichiers ext2 Linux. Cette commande doit être appelée en tant que root.
  • Ajoute un nouveau disque dur
    #!/bin/bash # Ajouter un deuxième disque dur au système. # Configuration logiciel. suppose que le matériel est déjà monté. # A partir d'un article de l'auteur de ce document dans le numéro # #38 de la "Linux Gazette", http://www.linuxgazette.com. ROOT_UID=0 # Ce script doit être lancé en tant que root. E_NOTROOT=67 # Erreur pour les utilisateurs non privilégiés. if [ "$UID" -ne "$ROOT_UID" ] then echo "Vous devez être root pour utiliser ce script." exit $E_NOTROOT fi # A utiliser avec beaucoup de précautions! # Si quelque chose se passe mal, vous pourriez supprimer votre système de #+ fichiers complet. NOUVEAUDISQUE=/dev/hdb # Suppose que /dev/hdb est disponible. A vérifier! POINTMONTAGE=/mnt/newdisk # Ou choisissez un autre point de montage. fdisk $NOUVEAUDISQUE1 mke2fs -cv $NOUVEAUDISQUE1 # Vérifie les mauvais blocs et rend la sortie verbeuse. # Note: /dev/hdb1, *pas* /dev/hdb! mkdir $POINTMONTAGE chmod 777 $POINTMONTAGE # Rend le nouveau disque accessible à tous les utilisateurs. # Maintenant, testez... # mount -t ext2 /dev/hdb1 /mnt/newdisk # Essayez de créer un répertoire. # Si cela fonctionne, démontez-le et faites. # Etape finale: # Ajoutez la ligne suivante dans /etc/fstab. # /dev/hdb1 /mnt/newdisk ext2 defaults 1 1 exit 0
  • Voir aussi Création d'un système de fichiers dans un fichier et Créer un disque ram.

  • tune2fs
    Configure finement le système de fichiers ext2. Peut être utilisé pour modifier les paramètres du système de fichiers, tels que le nombre maximum de montage. Il doit être utilisé en tant que root.Cette commande est extrêmement dangereuse. Utilisez-la à vos propres risques, car vous pourriez détruire par inavertance votre système de fichiers.

  • dumpe2fs
    Affiche sur stdout énormément d'informations sur le système de fichiers. Elle doit aussi être appelée en tant que root.root# dumpe2fs /dev/hda7 | grep 'ount count' dumpe2fs 1.19, 13-Jul-2000 for EXT2 FS 0.5b, 95/08/09 Mount count: 6 Maximum mount count: 20

  • hdparm
    Liste ou modifie les paramètres des disques durs. Cette commande doit être appelée en tant que root et peut être dangereuse si elle est mal utilisée.

  • fdisk
    Crée ou modifie une table des partitions sur un périphérique de stockage, habituellement un disque dur. Cette commande doit être appelée en tant que root.Utilisez cette commande avec d'infinies précautions. Si quelque chose se passe mal, vous pouvez détruire un système de fichiers existant.

  • fsck
    Ensemble de commandes de vérification, réparation et déboguage des systèmes de fichiers.fsck: une interface pour vérifier un système de fichiers UNIX (peut appeler d'autres utilitaires). Le type de système de fichiers est par défaut ext2.e2fsck: vérificateur du système de fichiers ext2.debugfs: débogueur du système de fichiers ext2. Une des utilités de cette commande versatile, mais dangereuse, est de récupérer (ou plutôt d'essayer de récupérer) des fichiers supprimés. A réserver aux utilisateurs avancés! Attention:Toutes ce commandes doivent être appelées en tant que root et peuvent endommager, voire détruire, un système de fichiers si elles sont mal utilisées.

  • badblocks
    Vérifie les blocs défectueux (défauts physiques du média) sur un périphérique de stockage. Cette commande trouve son utilité lors du formattage d'un nouveau disque dur ou pour tester l'intégrité du média de sauvegarde. (40) Comme exemple, badblocks /dev/fd0 teste une disquette.La commande badblocks peut être appelé de façon destructive (écrasement de toutes les données) ou dans un mode lecture-seule non destructif. Si l'utilisateur root est le propriétaire du périphérique à tester, comme c'est le cas habituellement, alors root doit appeler cette commande.

  • mkbootdisk
    Crée une disquette de démarrage pouvant être utilisée pour lancer le système si, par exemple, le MBR (master boot record) est corrumpu. La commande mkbootdisk est en fait un script Bash, écrit par Erik Troan, et disponible dans le répertoire /sbin.

  • chroot
    CHange ROOT directory (modifie le répertoire racine). Habituellement, les commandes sont récupérées à partir de $PATH, depuis la racine /, le répertoire racine par défaut. Cette commande modifie le répertoire racine par un autre répertoire (et modifie aussi le répertoire de travail). Ceci est utile dans des buts de sécurité, par exemple lorsqu'un administrateur système souhaite restreindre certains utilisateurs notamment ceux utilisant telnet, pour sécuriser une partie du système de fichiers (c'est souvent assimilé à confiner un utilisateur invité dans une prison chroot ( "chroot jail" )). Notez qu'après un chroot, le chemin d'exécution des binaires du système n'est plus valide.Un
  • chroot /opt
  • ferait que toutes les références à /usr/bin seraient traduites en /opt/usr/bin. De même,
  • chroot /aaa/bbb /bin/ls
  • redirigerait tous les futurs appels à ls en /aaa/bbb comme répertoire de base, plutôt que / comme c'est habituellement le cas. Un alias XX 'chroot /aaa/bbb ls' dans le ~/.bashrc d'un utilisateur restreint réllement la portion du système de fichiers où elle peut lancer des commandes.La commande chroot est aussi pratique lors du lancement du disquette d'urgence (chroot vers /dev/fd0), ou comme option de lilo lors de la récupération après un crash système. D'autres utilisations incluent l'installation à partir d'un autre système de fichiers (une option rpm) ou le lancement d'un système de fichiers en lecture-seule à partir d'un CDROM. Ne peut s'appeller qu'en tant que root, et à utiliser avec précaution. Attention:Il pourrait être nécessaire de copier certains fichiers système vers un répertoire compris dans le répertoire de base du chroot, car le $PATH n'est plus fiable.

  • lockfile
    Cet utilitaire fait partie du package procmail (www.procmail.org). Il crée un fichier de verrouillage, un fichier sémaphore qui contrôle l'accès à un fichier, périphérique ou ressource. Le fichier de verrouillage sert en tant qu'indicateur qu'un fichier, périphérique, ressource est utilisé par un processus particulier ( "occupé" ), et ne permet aux autres processus qu'un accès restreint (ou pas d'accès).Les fichiers de verrouillage sont utilisés par des applications pour protéger les répertoires de courriers électroniques des utilisateurs de modifications simultanées, pour indiquer qu'un port modem est utilisé ou pour montrer qu'une instance de Netscape utilise son cache. Les scripts peuvent vérifier l'existence d'un fichier de verrouillage créé par un certain processus pour vérifier si le processus existe. Notez que si un script essaie de créer un fichier de verrouillage déjà existant, le script a toutes les chances de se terminer précipitamment.Habituellement, les applications créent et vérifient les fichiers de verrouillage dans le répertoire /var/lock. Un script peut tester la présence d'un fichier de verrouillage de la façon suivante.

  • mknod
    Crée des fichiers de périphériques blocs ou caractères (peut être nécessaire lors de l'installation d'un nouveau matériel sur le système).

  • tmpwatch
    Supprime automatiquement les fichiers qui n'ont pas été accédés depuis une certaine période. Appelé habituellement par crond pour supprimer les fichiers journaux.

  • MAKEDEV
    Utilitaire pour la création des fichiers périphériques. Il doit être lancé en tant que root et dans le répertoire /dev.
  • root# ./MAKEDEV
  • C'est une espèce de version avancée de mknod.

  • dump
    La commande dump est un utilitaire élaboré de sauvegarde du système de fichiers, généralement utilisé sur des grosses installations et du réseau. (41) Il lit les partitions brutes du disque et écrit un fichier de sauvegarde dans un format binaire. Les fichiers à sauvegarder peuvent être enregistrés sur un grand nombre de média de stockage incluant les disques et lecteurs de cassettes. La commande restore restaure les sauvegardes faites avec dump.

  • fdformat
    Réalise un formattage bas-niveau sur une disquette.

  • ulimit
    Initialise une limite supérieure sur l'utilisation des ressources système. Habituellement appelé avec l'option -f, qui intialise une limite sur la taille des fichiers (ulimit -f 1000 limite les fichiers à un mégaoctet maximum). L'option -t limite la taille du coredump (ulimit -c 0 élimine les coredumps). Normalement, la valeur de ulimit serait configurée dans /etc/profile et/ou ~/.bash_profile (voir ). IMPORTANT:Un emploi judicieux de ulimit peut protéger un système contre l'utilisation des bombes fork. IMPORTANT: IMPORTANT:Un ulimit -Hu XX (où XX est la limite du nombre de processus par utilisateur) dans /etc/profile annulerait ce script lorsqu'il dépassera cette limite.

  • umask
    Masque pour la création d'un fichier utilisateur (User MASK). Limite les attributs par défaut d'un fichier pour un utilisateur particulier. Tous les fichiers créés par cet utilisateur prennent les attributs spécifiés avec umask. La valeur (octale) passée à umask définit les droits du fichiers non actifs. Par exemple, umask 022 nous assure que les nouveaux fichiers auront tout au plus le droit 0755 (777 NAND 022). (42) Bien sûr, l'utilisateur peut ensuite modifier les attributs de fichiers spécifiques avec chmod. La pratique habituelle est d'initialiser la valeur de umask dans /etc/profile et/ou ~/.bash_profile (voir ).

  • rdev
    Obtenir des informations sur ou modifier le périphérique racine, l'espace swap ou le mode vidéo. La fonctionnalité de rdev a été principalement repris par lilo, mais rdev reste utile pour configurer un disque ram. C'est une autre commande dangereuse si elle est mal utilisée.

  • lsmod
    Affiche les modules noyau installés.
  • bash$ lsmod Module Size Used by autofs 9456 2 (autoclean) opl3 11376 0 serial_cs 5456 0 (unused) sb 34752 0 uart401 6384 0 [sb] sound 58368 0 [opl3 sb uart401] soundlow 464 0 [sound] soundcore 2800 6 [sb sound] ds 6448 2 [serial_cs] i82365 22928 2 pcmcia_core 45984 0 [serial_cs ds i82365]
  • Remarque:Faire un cat /proc/modules donne la même information.

  • insmod
    Force l'installation d'un module du noyau (utilise modprobe à la place, lorsque c'est possible). Doit être appelé en tant que root.

  • rmmod
    Force le déchargement d'un module du noyau. Doit être appelé en tant que root.

  • modprobe
    Chargeur de modules normalement appelé à partir d'un script de démarrage. Doit être appelé en tant que root.

  • depmod
    Crée un fichier de dépendances de module, appelé habituellement à partir d'un script de démarrage.

  • env
    Lance un programme ou un script avec certaines variables d'environnement initialisées ou modifiées (sans modifier l'environnement système complet). [nomvariable=xxx] permet la modification d'une variable d'environnement nomvariable pour la durée du script. Sans options spécifiées, cette command affiche tous les paramètrages de variables d'environnement. Remarque:Dans Bash et d'autres dérivatifs du shell Bourne, il est possible d'initialiser des variables dans un environnement d'une seule commande. Astuce:La première ligne d'un script (la ligne "#-!" ) peut utiliser env lorsque le chemin vers le shell ou l'interpréteur est inconnu.

  • ldd
    Affiche les dépendances des bibliothèques partagées d'un exécutables.bash$ ldd /bin/ls libc.so.6 => /lib/libc.so.6 (0x4000c000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000)

  • strip
    Supprime les références symboliques de déboguage à partir d'un exécutable. Ceci réduit sa taille mais rend le déboguage impossible.Cette commande est fréquente dans un Makefile mais bien plus rare dans un script shell.

  • nm
    Affiche les symboles dans un binaire compilé sur lequel la commande strip n'a pas agi.

  • rdist
    Client distant: synchronise, clone ou sauvegarde un système de fichiers sur un serveur distant.

En utilisant notre connaissance des commandes administratives, examinons un script système. Une des façons les plus courtes et les plus simples de comprendre les scripts est killall, utilisée pour suspendre les processus en cours lors de l'arrêt du système.

killall, à partir de /etc/rc.d/init.d
#!/bin/sh # --> Commentiares ajoutés par l'auteur de ce document identifiés par "# -->". # --> Ceci fait partie du paquetage de scripts 'rc' # --> par Miquel van Smoorenburg, miquels@drinkel.nl.mugnet.org> # --> Ce script particulier semble être spécifique à Red Hat # --> (il pourrait ne pas être présent dans d'autres distributions). # Arrête tous les services inutiles qui sont en cours d'exécution (ils ne # devraient pas, donc il s'agit juste d'un test) for i in /var/lock/subsys/*; do # --> Boucle for/in standard, mais comme "do" se trouve sur la même # --> ligne, il est nécessaire d'ajouter ";". # Vérifie si le script existe. [ ! -f $i ] && continue # --> C'est une utilisation intelligente d'une "liste et", équivalente à : # --> if [ ! -f "$i" ]; then continue # Obtient le nom du sous-système. subsys=${i#/var/lock/subsys/} # --> Correspondance de nom de variable qui, dans ce cas, est le nom du # --> fichier. C'est l'équivalent exact de subsys=`basename $i`. # --> Il l'obtient du nom du fichier de verrouillage (si il existe un # --> fichier de verrou, c'est la preuve que le processus est en cours # --> d'exécution). # --> Voir l'entrée "lockfile", ci-dessus. # Arrête le sous-système. if [ -f /etc/rc.d/init.d/$subsys.init ]; then /etc/rc.d/init.d/$subsys.init stop else /etc/rc.d/init.d/$subsys stop # --> Suspend les jobs et démons en cours en utilisant la commande # --> intégrée 'stop'. fi done

Ce n'était pas si mal. En plus d'un léger travail avec la correspondance de variables, il n'y a rien de plus ici.


Exercice 1

Dans /etc/rc.d/init.d, analyse le script halt. C'est un peu plus long que killall mais similaire dans le concept. Faite une copie de ce script quelque part dans votre répertoire personnel et expérimentez-le ainsi (ne le lancez pas en tant que root). Lancez-le simultanément avec les options -vn (

sh -vn nomscript

). Ajoutez des commentaires extensifs. Modifiez les commandes "action" en "echos" .


Exercice 2

Regardez quelques-uns des scripts les plus complexes dans /etc/rc.d/init.d. Regardez si vous comprenez certaines parties d'entre eux. Suivez la procédure ci-dessus pour les analyser. Pour plus d'indications, vous pouvez aussi examiner le fichier sysvinitfiles dans /usr/share/doc/initscripts-?.??, faisant partie de la documentation d' "initscripts" .


3.5. Substitution de commandes

Une substitution de commande réassigne la sortie d'une commande (43) ou même de multiples commandes; elle branche littéralement la sortie d'une commande sur un autre contexte.

La forme classique de la substitution de commande utilise l'apostrophe inverse (`...`). Les commandes placées à l'intérieur de ces apostrophes inverses génèrent du texte en ligne de commande.


La sortie des commandes peut être utilisée comme argument d'une autre commande, pour affecter une variable, voire pour génerer la liste des arguments dans une boucle for .

Attention : Les substitutions de commandes peuvent provoquer des coupures de mots.

Attention : Même s'il n'y a pas coupure de mots, une substitution de commandes peut ôter les retours à la ligne finaux.

Attention : L'utilisation d'echo pour afficher la valeur d'une variable non protégée affectée à l'aide d'une substitution de commande retire les caractères de nouvelle ligne finaux de la sortie des commandes ainsi redirigées. Ce qui peut créer des surprises désagréables.

La substitution de commande permet même d'affecter à une variable le contenu d'un fichier, en utilisant soit une redirection soit la commande cat

Attention : Ne pas affecter le contenu d'un gros fichier texte à une variable à moins que vous n'ayez une bonne raison de le faire. Ne pas affecter le contenu d'un fichier binaire à une variable, même pour blaguer.

Trucs de script stupides
#!/bin/bash # stupid-script-tricks.sh: Ne tentez pas ça chez vous, les gars! # D'après "Trucs de Scripts Stupides," Volume I. variable_dangereuse=`cat /boot/vmlinuz` # Le noyau Linux compressé en personne. echo "longueur de la chaîne \$variable_dangereuse = ${#variable_dangereuse}" # longueur de la chaîne $variable_dangereuse = 794151 # (Ne donne pas le même résultat que 'wc -c /boot/vmlinuz'.) # echo "$variable_dangereuse" # N'essayez pas de faire ça! Cela figerait le script. # L'auteur de ce document n'a pas connaissance d'une l'utilité quelconque pour #+ l'affectation à une variable du contenu d'un fichier binaire. exit 0

Attention : Notez qu'on ne provoque pas de surcharge de tampon. C'est un exemple où un langage interprété, tel que Bash, fournit plus de protection vis à vis des erreurs de programmation qu'un langage compilé.

Une substitution de commande permet d'affecter à une variable la sortie d'une boucle. L'idée pour y parvenir est de se servir de la sortie d'une commande echo placée à l'intérieur de la boucle.

Générer le contenu d'une variable à partir d'une boucle
#!/bin/bash # csubloop.sh: Initialiser une variable à la sortie d'une boucle. variable1=`for i in 1 2 3 4 5 do echo -n "$i" # La commande 'echo' est essentielle done` #+ à la substitution de commande. echo "variable1 = $variable1" # variable1 = 12345 i=0 variable2=`while [ "$i" -lt 10 ] do echo -n "$i" # A nouveau le nécessaire 'echo'. let "i += 1" # Incrémentation. done` echo "variable2 = $variable2" # variable2 = 0123456789 exit 0

La substitution de commande permet d'augmenter l'ensemble des outils disponibles en Bash. Il suffit simplement d'écrire un programme ou un script dont la sortie est stdout (comme il se doit pour tout bon outil UNIX) et d'affecter cette sortie à une variable.

bash$ gcc -o hello hello.c

bash$ sh hello.sh Hello, world.

Remarque : La syntaxe $(COMMANDE) a remplacé les apostrophes inverses pour la substitution de commande.

Exemples de substitution de commandes dans des scripts shell : Un remplaçant de grep pour les fichiers binaires Utiliser la substitution de commandes pour générer la variable case Réinitialiser RANDOM incorrectname élimine dans le répertoire courant les fichiers dont le nom contient des caractères incorrects et des espaces blancs. lowercase: Change tous les noms de fichier du répertoire courant en minuscule. Emuler grep dans un script Utiliser seq pour générer l'incrément d'une boucle Utiliser efax en mode batch Afficher les liens symboliques dans un répertoire Supprimer les commentaires de programmes C Boucle for redirigée tree: Afficher l'arborescence d'un répertoire Trouver le processus associé à un PID Paiement mensuel sur une hypothèque Conversion de base Une autre façon d'invoquer bc


3.6. Expansion arithmétique

L'expansion arithmétique fournit un un outil puissant pour réaliser des opérations arithmétiques dans des scripts. Traduire une chaîne en une expression numérique est relativement immédiat en utilisant des apostrophes inverses, des double parenthèses ou let.


3.7. Redirection d'E/S (entrées/sorties)

Trois différents "fichiers" sont toujours ouvert par défaut, stdin (le clavier), stdout (l'écran), et stderr (la sortie des messages d'erreur vers l'écran). Ceux-ci, et n'importe quel autre fichier ouvert, peuvent être redirigés. La redirection signifie simplement la capture de la sortie d'un fichier, d'une commande, d'un programme, d'un script, voire même d'un bloc de code dans un script (voir Blocs de code et redirection d'E/S et Sauver le résultat d'un bloc de code dans un fichier) et le renvoi du flux comme entrée d'un autre fichier, commande, programme ou script.

Chaque fichier ouvert se voit affecté un descripteur de fichier. (44) Les descripteurs de fichier pour stdin, stdout et stderr sont 0, 1 et 2, respectivement. Pour ouvrir d'autres fichiers, il reste les descripteurs 3 à 9. Il est quelque fois utile d'affecter un de ces descripteurs supplémentaires de fichiers pour stdin, stdout ou stderr comme lien dupliqué temporaire. (45) Ceci simplifie le retour à la normale après une redirection complexe et un remaniement (voir Rediriger stdin en utilisant exec).

Plusieurs instances de redirection d'entrées et de sorties et/ou de tubes peuvent être combinées en une seule ligne de commande. Voir Déballer une archive rpm et fifo: Faire des sauvegardes journalières, en utilisant des tubes nommés.

Plusieurs flux de sortie peuvent être redirigés vers un fichier.

  • n-
    Ferme le descripteur de fichier n.

  • 0-
    Ferme stdin.

  • n-
    Ferme le descripteur de fichiers de sortie n.

  • 1-
    Ferme stdout.

Les processus fils héritent des descripteurs de fichiers ouverts. C'est pourquoi les tubes fonctionnent. Pour empêcher l'héritage d'un fd, fermez-le.

Pour une introduction plus détaillée de la redirection d'E/S, voir .


Utiliser exec

Une commande exec filename redirige stdin vers un fichier. A partir de là, stdin vient de ce fichier, plutôt que de sa source habituelle (généralement un clavier). Ceci fournit une méthode pour lire un fichier ligne par ligne et donc d'analyser chaque ligne de l'entrée en utilisant sed et/ou awk.

Rediriger stdin en utilisant exec
#!/bin/bash # Rediriger stdin en utilisant 'exec'. exec 6<&0 # Lie le descripteur de fichier #6 avec stdin. # Sauvegarde stdin. exec < fichier-donnees # stdin remplacé par le fichier "fichier-donnees" read a1 # Lit la première ligne du fichier "fichier-donnees". read a2 # Lit la deuxième ligne du fichier "fichier-donnees". echo echo "Les lignes suivantes lisent le fichier." echo "---------------------------------------" echo $a1 echo $a2 echo; echo; echo exec 0<&6 6<&- # Maintenant, restaure stdin à partir de fd #6, où il a été sauvegardé, #+ et ferme fd #6 ( 6<&- ) afin qu'il soit libre pour d'autres processus. # # <&6 6<&- fonctionne aussi. echo -n "Entrez des données " read b1 # Maintenant les fonctions lisent ("read") comme d'ordinaire, #+ c'est-à-dire à partir de stdin. echo "Entrée lue à partir de stdin." echo "-----------------------------" echo "b1 = $b1" echo exit 0

De la même façon, une commande exec nomfichier redirige stdout vers un fichier désigné. Ceci envoie toutes les sorties des commandes qui devraient normalement aller sur stdout vers ce fichier.

Rediriger stdout en utilisant exec
#!/bin/bash # reassign-stdout.sh FICHIERTRACES=fichiertraces.txt exec 6>&1 # Lie le descripteur de fichier #6 avec stdout. # Sauvegarde stdout. exec > $FICHIERTRACES # stdout remplacé par le fichier "fichiertraces.txt". # ----------------------------------------------------------- # # Toute sortie des commandes de ce bloc sera envoyée dans le fichier #+ $FICHIERTRACES. echo -n "Fichier traces: " date echo "-------------------------------------" echo echo "Sortie de la commande \"ls -al\"" echo ls -al echo; echo echo "Sortie de la commande \"df\"" echo df # ----------------------------------------------------------- # exec 1>&6 6>&- # Restaure stdout et ferme le descripteur de fichier #6. echo echo "== stdout restauré à sa valeur par défaut == " echo ls -al echo exit 0
Rediriger à la fois stdin et stdout dans le même script avec exec
#!/bin/bash # upperconv.sh # Convertit un fichier d'entrée spécifié en majuscule. E_ACCES_FICHIER=70 E_MAUVAIS_ARGS=71 if [ ! -r "$1" ] # Est-ce que le fichier spécifié est lisible? then echo "Ne peut pas lire le fichier d'entrée!" echo "Usage: $0 fichier-entrée fichier-sortie" exit $E_ACCES_FICHIER fi # Sortira avec la même erreur, #+ même si le fichier d'entrée ($1) n'est pas spécifié. if [ -z "$2" ] then echo "A besoin d'un fichier de sortie." echo "Usage: $0 fichier-entrée fichier-sortie" exit $E_MAUVAIS_ARGS fi exec 4<&0 exec < $1 # Lira le fichier d'entrée. exec 7>&1 exec > $2 # Ecrira sur le fichier de sortie. # S'assure que le fichier de sortie est modifiable #+ (ajoutez une vérification?). # ----------------------------------------------- cat - | tr a-z A-Z # Conversion en majuscule. # ^^^^^ # Lecture de stdin. # ^^^^^^^^^^ # Ecriture sur stdout. # Néanmoins, à la fois stdin et stdout ont été redirigés. # ----------------------------------------------- exec 1>&7 7>&- # Restaure stout. exec 0<&4 4<&- # Restaure stdin. # Après retour à la normale, la ligne suivante s'affiche sur stdout comme #+ attendu. echo "Le fichier \"$1\" a été enregistré dans \"$2\" après une convertion en majuscule." exit 0

Rediriger les blocs de code

Les blocs de code, comme les boucles while, until et for, voire même les blocs de test if/then peuvent aussi incorporer une redirection de stdin. Même une fonction peut utiliser cette forme de redirection (voir Vrai nom pour un utilisateur). L'opérateur à la fin du bloc de code accomplit ceci.

Boucle while redirigée
#!/bin/bash if [ -z "$1" ] then Fichier=noms.donnees # par défaut, si aucun fichier n'est spécifié. else Fichier=$1 fi #+ Fichier=${1:-noms.donnees} # peut remplacer le test ci-dessus (substitution de paramètres). compteur=0 echo while [ "$nom" != Smith ] # Pourquoi la variable $nom est-elle entre guillemets? do read nom # Lit à partir de $Fichier, plutôt que de stdin. echo $nom let "compteur += 1" done <"$Fichier" # Redirige stdin vers le fichier $Fichier. # ^^^^^^^^^^^^ echo; echo "$compteur noms lus"; echo # Notez que dans certains vieux langages de scripts de shells, #+ la boucle redirigée pourrait tourner dans un sous-shell. # Du coup, $compteur renverrait 0, la valeur initialisée en dehors de la boucle. # Bash et ksh évitent de lancer un sous-shell autant que possible, #+ de façon à ce que ce script, par exemple, tourne correctement. # # Merci à Heiner Steven pour nous l'avoir indiqué. exit 0
Autre forme de boucle while redirigée
#!/bin/bash # Ceci est une forme alternative au script précédent. # Suggéré par Heiner Steven #+ comme astuce dans ces situations où une boucle de redirection est lancé #+ comme un sous-shell, et donc que les variables à l'intérieur de la boucle #+ ne conservent pas leurs valeurs une fois la boucle terminée. if [ -z "$1" ] then Fichier=noms.donnees # Par défaut, si aucun fichier spécifié. else Fichier=$1 fi exec 3<&0 # Sauve stdin sur le descripteur de fichier 3. exec 0<"$Fichier" # Redirige l'entrée standard. compteur=0 echo while [ "$nom" != Smith ] do read nom # Lit à partir du stdin redirigé ($Fichier). echo $nom let "compteur += 1" done <"$Fichier" # La boucle lit à partir du fichier $Fichier. # ^^^^^^^^^^^^ exec 0<&3 # Restaure l'ancien stdin. exec 3<&- # Ferme le temporaire fd 3. echo; echo "$compteur noms lus"; echo exit 0
Boucle until redirigée
#!/bin/bash # Identique à l'exemple précédent, mais avec une boucle "until". if [ -z "$1" ] then Fichier=noms.donnees # Par défaut, si aucun nom de fichier n'est spécifié. else Fichier=$1 fi # while [ "$nom" != Smith ] until [ "$nom" = Smith ] # Modification de != en =. do read nom # Lit à partir de $Fichier, plutôt que de stdin. echo $nom done <"$Fichier" # Redirige stdin vers le fichier $Fichier. # ^^^^^^^^^^^^ # Même résultats qu'avec la boucle "while" du précédent exemple. exit 0
Boucle for redirigée
#!/bin/bash if [ -z "$1" ] then Fichier=noms.donnees # Par défaut, si aucun fichier n'est spécifié. else Fichier=$1 fi compteur_lignes=`wc $Fichier | awk '{ print $1 }'` # Nombre de lignes du fichier cible. # # Très peu naturel, néanmoins cela montre qu'il est possible de rediriger #+ stdin à l'intérieur d'une boucle "for"... #+ si vous êtes assez intelligent. # # Une autre façon plus concise est compteur_lignes=$(wc < "$Fichier") for nom in `seq $compteur_lignes` # Rappelez-vous que "seq" affiche une séquence de nombres. # while [ "$nom" != Smith ] -- plus compliqué qu'une boucle "while" -- do read nom # Lit à partir de $Fichier, plutôt que de stdin. echo $nom if [ "$nom" = Smith ] # A besoin de tout ce bagage supplémentaire ici. then break fi done <"$Fichier" # Redirige stdin vers le fichier $Fichier. # ^^^^^^^^^^^^ exit 0

Nous pouvons modifier le précédent exemple pour aussi rediriger la sortie de la boucle.

Rediriger la boucle for (à la fois stdin et stdout)
#!/bin/bash if [ -z "$1" ] then Fichier=noms.donnees # Par défaut, si aucun fichier n'est spécifié. else Fichier=$1 fi FichierSauvegarde=$Fichier.nouveau # Fichier où sauvegarder les résultats. NomFinal=Jonah # Nom par lequel terminer la lecture. nb_lignes=`wc $Fichier | awk '{ print $1 }'` # Nombre de lignes du fichier cible. for nom in `seq $nb_lignes` do read nom echo "$nom" if [ "$nom" = "$NomFinal" ] then break fi done < "$Fichier" > "$FichierSauvegarde" # Redirige stdin dans $Fichier, # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ et sauvegarde dans le fichier. exit 0
Redirigé un test if/then
#!/bin/bash if [ -z "$1" ] then Fichier=noms.donnees # Valeur par défaut, si aucun nom de fichier n'est #+ spécifié. else Fichier=$1 fi VRAI=1 if [ "$VRAI" ] # if true et if : fonctionnent aussi. then read nom echo $nom fi <"$Fichier" # ^^^^^^^^^^^^ # Lit seulement la première ligne du fichier. # Un test "if/then" n'a aucun moyen de faire une itération sauf si il est #+ intégré dans une boucle. exit 0
Fichier de données nom.données pour les exemples ci-dessus
Aristotle Belisarius Capablanca Euler Goethe Hamurabi Jonah Laplace Maroczy Purcell Schmidt Semmelweiss Smith Turing Venn Wilson Znosko-Borowski # This is a data file for #+ "redir2.sh", "redir3.sh", "redir4.sh", "redir4a.sh", "redir5.sh".

Rediriger stdout d'un bloc de code a le même effet que den sauver la sortie dans un fichier. Voir Sauver le résultat d'un bloc de code dans un fichier.

Les documents en ligne sont un cas spécial pour la redirection de blocs de code.


Applications

Une utilisation intelligente de la redirection d'E/S permet d'analyser et de coudre ensemble de petits bouts de la sortie de commandes (voir Utiliser read avec la redirection de fichier). Ceci permet de générer des rapports et des fichiers de traces.

Enregistrer des événements
#!/bin/bash # logevents.sh, by Stephane Chazelas. # Tracer des événements dans un fichier. # Vous devez être root pour exécuter ceci (pour avoir le droit d'écrire dans #+ /var/log). ROOT_UID=0 # Seuls les utilisateurs ayant l'identifiant $UID 0 ont les #+ privilèges de root. E_NONROOT=67 # Code de sortie si non root. if [ "$UID" -ne "$ROOT_UID" ] then echo "Vous devez être root pour exécuter ce script." exit $E_NONROOT fi FD_DEBUG1=3 FD_DEBUG2=4 FD_DEBUG3=5 # Décommentez une des deux lignes ci-dessous pour activer le script. # TRACE_EVENEMENTS=1 # TRACE_VARS=1 log() # Ecrit la date et l'heure dans le fichier de traces. { echo "$(date) $*" &7 # Ceci *ajoute* la date dans le fichier. # Voir ci-dessous. } case $NIVEAU_TRACES in 1) exec 3&2 4 /dev/null 5 /dev/null;; 2) exec 3&2 4&2 5 /dev/null;; 3) exec 3&2 4&2 5&2;; *) exec 3 /dev/null 4 /dev/null 5 /dev/null;; esac FD_TRACEVARS=6 if [[ $TRACE_VARS ]] then exec 6 /var/log/vars.log else exec 6 /dev/null # Bury output. fi FD_TRACEEVENEMENTS=7 if [[ $TRACE_EVENEMENTS ]] then # then exec 7 (exec gawk '{print strftime(), $0}' /var/log/event.log) # La ligne ci-dessus ne fonctionnera pas avec Bash, version 2.04. exec 7 /var/log/event.log # Ajoute dans "event.log". log # Ecrit la date et l'heure. else exec 7 /dev/null # Supprime le sortie. fi echo "DEBUG3: début" &${FD_DEBUG3} ls -l &5 2&4 # commande1 &5 2&4 echo "Done" # commande2 echo "envoi mail" &${FD_LOGEVENTS} # Ecrit "envoi mail" sur fd #7. exit 0

3.8. Documents en ligne

Un document en ligne utilise une forme spéciale de redirection d'E/S pour fournir une liste de commande à un programme ou une commande interactifs, tels que ftp, telnet ou ex. Une "chaîne de caractères délimite" encadre la liste de commandes. Le symbôle spécial désigne la chaîne de caractères de limite. Ceci a pour effet de rediriger la sortie d'un fichier vers le programme, de façon similaire à

programme-interactif fichier-commandes

, où fichier-commandes contient

L'alternative au "document en ligne" ressemble à ceci:

Choisissez une chaîne de caractères de limite suffisamment inhabituelle pour qu'elle ne soit pas présente où que ce soit dans la liste de commandes et qu'aucune confusion ne puisse arriver.

Notez que les documents en ligne peuvent parfois être utilisés correctement avec des utilitaires et des commandes non interactifs.

fichierstupide: Crée un fichier stupide de deux lignes
#!/bin/bash # Utilisation non interactive de 'vi' pour éditer un fichier. # Émule 'sed'. E_MAUVAISARGS=65 if [ -z "$1" ] then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi FICHIERCIBLE=$1 # Insère deux lignes dans le fichier et le sauvegarde. #--------Début document en ligne-----------# vi $FICHIERCIBLE x23LimitStringx23 i Ceci est la ligne 1 du fichier exemple. Ceci est la ligne 2 du fichier exemple. ^[ ZZ x23LimitStringx23 #--------Fin document en ligne-----------# # Notez que ^[ ci-dessus est un échappement littéral, tapé par #+ Control-V Esc. # Bram Moolenaar indique que ceci pourrait ne pas fonctionner avec 'vim', #+ dû à de possibles problèmes avec l'interaction du terminal. exit 0

Le script ci-dessus pourrait avoir été aussi efficacement implémenté avec ex, plutôt que vi. Les documents en ligne contenant une liste de commandes ex sont assez courant pour disposer de leur propre catégorie, connue sous le nom de scripts ex.

broadcast: Envoie un message à chaque personne connectée
#!/bin/bash wall zzz23EndOfMessagezzz23 Envoyez par courrier électronique vos demandes de pizzas à votre administrateur système. (Ajoutez un euro supplémentaire pour les anchois et les champignons.) # Un message texte supplémentaire vient ici. # Note: Les lignes de commentaires sont affichées par 'wall'. zzz23EndOfMessagezzz23 # Peut se faire plus efficacement avec # wall message-file # Néanmoins, sauvegarder un message modèle dans un script fonctionne. exit 0
Message multi-lignes en utilisant cat
#!/bin/bash # 'echo' est bien pour afficher des messages sur une seule ligne, # mais est parfois problèmatique pour des blocs de message. # Un document en ligne style 'cat' permet de surpasser cette limitation. cat Fin-du-message ------------------------------------- Ceci est la ligne 1 du message. Ceci est la ligne 2 du message. Ceci est la ligne 3 du message. Ceci est la ligne 4 du message. Ceci est la dernière ligne du message. ------------------------------------- Fin-du-message exit 0 #-------------------------------------------- # Le code ci-dessous est désactivé, à cause du "exit 0" ci-dessus. # S.C. indique que ce qui suit fonctionne aussi. echo "------------------------------------- Ceci est la ligne 1 du message. Ceci est la ligne 2 du message. Ceci est la ligne 3 du message. Ceci est la ligne 4 du message. Ceci est la dernière ligne du message. -------------------------------------" # Néanmoins, le texte pourrait ne pas inclure les doubles guillemets sauf si #+ ils sont échappés.

L'option - pour marquer la chaîne de caractères de limite d'un document en ligne (

-ChaineLimite

) supprime les tabulations (mais pas les espaces) lors de la sortie. Ceci est utile pour réaliser un script plus lisible.

Message multi-lignes, aves les tabulations supprimées
#!/bin/bash # Identique à l'exemple précédent, mais... # L'option - pour un document en ligne <<- # supprime les tabulations dans le corps du document, mais *pas* les #+ espaces. cat -FINDUMESSAGE Ceci est la ligne 1 du message. Ceci est la ligne 2 du message. Ceci est la ligne 3 du message. Ceci est la ligne 4 du message. Ceci est la dernière ligne du message. FINDUMESSAGE # La sortie du script sera décalée vers la gauche. # La tabulation au début de chaque ligne ne se verra pas. # Les cinq lignes du "message" sont préfacées par une tabulation, et non des espaces, # Les espaces ne sont pas affectés par <<- . exit 0

Un document en ligne supporte la substitution de paramètres et de commandes. Il est donc possible de passer différents paramètres dans le corps du document en ligne, en changeant la sortie de façon appropriée.

Document en ligne avec une substitution de paramètre
#!/bin/bash # Autre document en ligne 'cat', utilisant la substitution de paramètres. # Essayez-le sans arguments, ./scriptname # Essayez-le avec un argument, ./scriptname Mortimer # Essayez-le avec deux arguments entre guillemets, # ./scriptname "Mortimer Jones" CMDLINEPARAM=1 # Attend au moins un paramètre en ligne de commande. if [ $# -ge $CMDLINEPARAM ] then NOM=$1 # Si plus d'un paramètre en ligne de commande, # alors prendre seulement le premier. else NOM="John Doe" # Par défaut, si il n'y a pas de paramètres. fi INTERLOCUTEUR="l'auteur de ce joli script" cat FinDuMessage Salut, $NOM. Bienvenue à toi, $NOM, de la part de $INTERLOCUTEUR. # Ce commentaire s'affiche dans la sortie (pourquoi?). FinDuMessage # Notez que les lignes blanches s'affichent. Ainsi que le commentaire. exit 0

Mettre entre guillemets ou échapper la "chaîne de caractères de limite" au début du document here désactive la substitution de paramètres en son corps. Ceci a un intérêt très limité.

Substitution de paramètres désactivée
#!/bin/bash # Un document en ligne 'cat', mais avec la substitution de paramètres #+ désactivée. NOM="John Doe" INTERLOCUTEUR="l'auteur de ce joli script" cat 'FinDuMessage' Salut, $NOM. Bienvenue à toi, $NOM, de la part de $INTERLOCUTEUR. FinDuMessage # Pas de substitution de paramètres lorsque la chaîne de fin est entre #+ guillemets ou échappée. # L'une des deux commandes ci-dessous à l'entête du document en ligne aura le #+ le même effet. # cat "FinDuMessage" # cat \FinDuMessage exit 0

C'est un script utile contenant un document en ligne avec une substitution de paramètres.

upload: Envoie un fichier vers le répertoire incoming chez Sunsite
#!/bin/bash # upload.sh # Téléchargement de fichiers par pair (Fichier.lsm, Fichier.tar.gz) # pour le répertoire entrant de Sunsite (metalab.unc.edu). E_ERREURSARGS=65 if [ -z "$1" ] then echo "Usage: `basename $0` nomfichier" exit $E_ERREURSARGS fi NomFichier=`basename $1` # Supprime le chemin du nom du fichier. Serveur="metalab.unc.edu" Repertoire="/incoming/Linux" # Ils n'ont pas besoin d'être codés en dur dans le script, # mais peuvent être à la place changés avec un argument en ligne de commande. MotDePasse="votre.addresse.email" # A changer suivant vos besoins. ftp -n $Serveur Fin-De-Session # L'option -n désactive la connexion automatique user anonymous "$MotDePasse" binary bell # Sonne après chaque transfert de fichiers. cd $Repertoire put "$NomFichier.lsm" put "$NomFichier.tar.gz" bye Fin-De-Session exit 0

Un document en ligne peut donner une entrée à une fonction du même script.

Documents en ligne et fonctions
#!/bin/bash # here-function.sh ObtientDonneesPersonnelles () { read prenom read nom read adresse read ville read etat read codepostal } # Ceci ressemble vraiment à une fonction interactive, mais... # Apporter l'entrée à la fonction ci-dessus. ObtientDonneesPersonnelles ENREG001 Bozo Bozeman 2726 Nondescript Dr. Baltimore MD 21226 RECORD001 echo echo "$prenom $nom" echo "$adresse" echo "$ville, $etat $codepostal" echo exit 0

Il est possible d'utiliser : comme commande inactive acceptant une sortie d'un document en ligne. Cela crée un document en ligne "anonyme" .

Document en ligne Anonyme
#!/bin/bash : TESTVARIABLES ${HOSTNAME?}${USER?}${MAIL?} # Affiche un message d'erreur si une des variables n'est pas configurée. TESTVARIABLES exit 0

Astuce : Une variante de la technique ci-dessus permet de "supprimer les commentaires" de blocs de code.

Décommenter un bloc de code
#!/bin/bash # commentblock.sh : << BLOC_COMMENTAIRE echo "Cette ligne n'est pas un echo." C'est une ligne de commentaire sans le préfixe "#". Ceci est une autre ligne sans le préfixe "#". &*@!!++= La ligne ci-dessus ne causera aucun message d'erreur, Parce que l'interpréteur Bash l'ignorera. BLOC_COMMENTAIRE echo "La valeur de sortie du \"BLOC_COMMENTAIRE\" ci-dessus est $?." # 0 # Pas d'erreur. # La technique ici-dessus est aussi utile pour mettre en commentaire un bloc #+ de code fonctionnel pour des raisons de déboguage. # Ceci permet d'éviter de placer un "#" au début de chaque ligne, et d'avoir #+ ensuite à les supprimer. : << DEBUGXXX for fichier in * do cat "$fichier" done DEBUGXXX exit 0

Astuce : Encore une autre variante de cette symmpathique astuce rendant les scripts "auto-documentés" possibles.

Un script auto-documenté
#!/bin/bash # self-document.sh: script auto-documenté # Modification de "colm.sh". DEMANDE_DOC=70 if [ "$1" = "-h" -o "$1" = "--help" ] # Demande de l'aide. then echo; echo "Usage: $0 [nom-repertoire]"; echo cat "$0" | sed --silent -e '/DOCUMENTATIONXX$/,/^DOCUMENTATION/p' | sed -e '/DOCUMENTATIONXX/d'; exit $DEMANDE_DOC; fi : << DOCUMENTATIONXX Liste les statistiques d'un répertoire spécifié dans un format de tabulations. ------------------------------------------------------------------------------ Le paramètre en ligne de commande donne le répertoire à lister. Si aucun répertoire n'est spécifié ou que le répertoire spécifié ne peut être lu, alors liste le répertoire courant. DOCUMENTATIONXX if [ -z "$1" -o ! -r "$1" ] then repertoire=. else repertoire="$1" fi echo "Liste de "$repertoire":"; echo (printf "PERMISSIONS LIENS PROP GROUPE TAILLE MOIS JOUR HH:MM NOM-PROG\n" \ ; ls -l "$repertoire" | sed 1d) | column -t exit 0

Remarque : Les documents en ligne créent des fichiers temporaires, mais ces fichiers sont supprimés après avoir été ouvert et ne sont plus accessibles par aucun autre processus.

Remarque :

bash$ bash -c 'lsof -a -p $$ -d0' EOF EOF lsof 1213 bozo 0r REG 3,5 0 30386 /tmp/t1213-0-sh (deleted)

Attention : Quelques utilitaires ne fonctionneront pas à l'intérieur d'un document en ligne.

Pour ces tâches trop complexes pour un "document en ligne" , considérez l'utilisation du langage de scripts expect, qui est conçu spécifiquement pour alimenter l'entrée de programmes interactifs.


3.9. Récréation


4. Thèmes avancés

A ce point, nous sommes prêt à nous enfoncer dans certains des aspects difficiles et inhabituelles de l'écriture de scripts. Tout au long du chemin, nous essaierons de "vous pousser" de plusieurs façons et d'examiner les conditions limites (qu'arrive-t'il lorsque nous entrons dans ce territoire inconnu?).


4.1. Expressions rationnelles

Pour utiliser complètement la puissance de la programmation par script shell, vous devez maîtriser les expressions rationnelles. Certaines commandes et utilitaires habituellement utilisés dans les scripts, tels que expr, sed et awk interprètent et utilisent les ER.


Une brève introduction aux expressions rationnelles

Une expression est une chaîne de caractères. Ces caractères qui ont une interprétation en dehors de leur signification littérale sont appelés des méta caractères. un symbole entre guillemets, par exemple, peut dénoter la parole d'une personne, ditto, ou une méta signification pour les symboles qui suivent. Les expressions rationnelles sont des ensembles de caractères et/ou méta-caractères auxquels UNIX accorde des fonctionnalités spéciales. (46)

Les principales utilisations des expressions rationnelles (ER) sont la recherche de texte ou la manipulation de chaînes. Une ER correspond à un seul caractère ou à un ensemble de caractères (une sous-chaîne ou une chaîne complète).

  • L'astérisque -- * -- correspond à toute répétition de caractères d'une chaîne ou d'une ER la précédant, incluant zéro caractère. "1133*" correspond à 11 + un ou plus de 3 ainsi que d'autres caractères: 113, 1133, 111312 et ainsi de suite.
  • Le point -- . -- correspond à un seul caractère, sauf le retour à la ligne. (47) "13." correspond à 13 + au moins un caractère (incluant un espace): 1133, 11333 mais pas 13 (un caractère supplémentaire manquant).
  • La puissance -- ^ -- correspond au début d'une ligne, mais quelque fois, suivant le contexte, inverse la signification d'un ensemble de caractères dans une ER.
  • Le signe dollar -- $ -- à la fin d'une ER correspond à la fin d'une ligne. "^$" correspond à des lignes blanches.
  • Les crochets -- [...] -- englobent un ensemble de caractères pour réaliser une correspondance dans une seule ER. "[xyz]" correspond aux caractères x, y, ou z. "[c-n]" correspond à tout caractère compris entre c et n. "[B-Pk-y]" correspond à tout caractère compris entre B et P et entre k et y. "[a-z0-9]" correspond à toute lettre en minuscule et à tout chiffre. "[^b-d]" correspond à tous les caractères sauf ceux compris entre b et d. Ceci est un exemple de l'inversion de la signification de l' ER suivante grâce à l'opérateur ^ (prenant le même rôle que ! dans un contexte différent).Les séquences combinées de caractères entre crochets correspondent à des modèles de mots communs. "[Yy][Ee][Ss]" correspond à yes, Yes, YES, yEs, et ainsi de suite. "[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]" correspond à tout numéro de sécurité sociale (NdT: du pays d'origine de l'auteur).
  • L'anti-slash -- \ -- échappe un caractère spécial, ce qui signifie que le caractère est interprété littéralement.Un "\$" renvoie la singnification littérale de "$" , plutôt que sa signification ER de fin de ligne. De même un "\\" a la signification littérale de "\" .
  • Les signes "inférieur et supérieur" échappés -- \...\ -- indiquent les limites du mot.Ces signes doivent être échappés, sinon ils n'ont que leur signification littérale. "\le\" correspond au mot "le" mais pas aux mots "les" , "leur" , "belle" , etc.
  • bash$ cat fichiertexte This is line 1, of which there is only one instance. This is the only instance of line 2. This is line 3, another line. This is line 4. bash$ grep 'the' fichiertexte This is line 1, of which there is only one instance. This is the only instance of line 2. This is line 3, another line. bash$ grep '\the\' fichiertexte This is the only instance of line 2.
  • ERs étenduesUtilisées dans egrep, awk et Perl
  • Le point d'interrogation -- ? -- correspond à aucune ou une instance de la précédente ER. Il est généralement utilisé pour correspondre à des caractères uniques.
  • Le signe plus -- + -- correspond à un ou plus de la précédente ER. Il joue un rôle similaire à *, mais ne correspond pas à zéro occurrence.
  • Les "accolades" échappées -- \{ \} -- indiquent le nombre d'occurrences à filtrer par une précédente ER.Il est nécessaire d'échapper les accolades car, sinon, elles ont leur signification littérale. Cette usage ne fait techniquement pas partie de l'ensemble des ER de base. "[0-9]\{5\}" correspond exactement à cinq entiers (caractères entre 0 et 9). Remarque:Les accolades ne sont pas disponibles comme ER dans la version "classique" (non conforme à POSIX) de awk. Néanmoins, gawk dispose de l'option --re-interval qui les autorise (sans être échappés). Remarque:
  • bash$ echo 2222 | gawk --re-interval '/2{3}/' 2222
  • Remarque:Perl et quelques versions de egrep ne nécessitent pas les accolades échappées.
  • Les parenthèses -- ( ) -- délimitent des groupes d' ERs. Elles sont utiles avec l'opérateur "| " et lors de l'extraction de sous-chaînes en utilisant expr.
  • L'opérateur d'ER "ou" -- | -- correspond à n'importe lequel d'un ensemble de caractères constituant l'alternative.
  • bash$ egrep 're(a|e)d' misc.txt People who read seem to be better informed than those who do not. The clarinet produces sound by the vibration of its reed.

Remarque : Quelques versions de sed, ed et ex supportent les versions échappées des expressions rationnelles étendues décrites ci-dessus.

  • Classes de caractères POSIX
  • [:class:]
  • Ceci est une autre façon de spécifier un intervalle de caractères à filtrer.
  • [:alnum:]
  • correspond aux caractères alphabétique et numériques. Ceci est équivalent à
  • [A-Za-z0-9]
  • .
  • [:alpha:]
  • correspond aux caractères alphabétique. Ceci est équivalent à
  • [A-Za-z]
  • .
  • [:blank:]
  • correspond à un espace ou à une tabulation.
  • [:cntrl:]
  • correspond aux caractères de contrôle.
  • [:digit:]
  • correspond aux chiffres (décimaux). Ceci est équivalent à
  • [0-9]
  • .
  • [:graph:]
  • (caractères graphiques affichables). Correspond aux caractères compris entre ASCII 33 - 126. Ceci est identique à
  • [:print:]
  • , ci-dessous, mais exclut le caractère espace.
  • [:lower:]
  • correspond aux caractères alphabétique minuscules. Ceci est équivalent à
  • [a-z]
  • .
  • [:print:]
  • (caractères imprimables). Correspond aux caractères compris entre ASCII 32 - 126. C'est identique à
  • [:graph:]
  • , ci-dessus, mais en ajoutant le caractère espace.
  • [:space:]
  • correspond à tout espace blanc (espace et tabulation horizontale).
  • [:upper:]
  • correspond à tout caractère alphabétiques majuscules. Ceci est équivalent à
  • [A-Z]
  • .
  • [:xdigit:]
  • correspond aux chiffres hexadécimaux. Ceci est équivalent à
  • [0-9A-Fa-f]
  • . IMPORTANT:Les classes de caractères POSIX nécessitent généralement d'être protégées ou les doubles crochets ([[ ]]). IMPORTANT:
  • bash$ grep [[:digit:]] fichier.test abc=723
  • IMPORTANT:Ces classes de caractères pourraient même être utilisées avec le remplacement, jusqu'à un certain point. IMPORTANT:
  • bash$ ls -l ?[[:digit:]][[:digit:]]? -rw-rw-r-- 1 bozo bozo 0 Aug 21 14:47 a33b
  • IMPORTANT:Pour voir les classes de caractères POSIX utilisées dans des script, référez-vous à toupper: Transforme un fichier en majuscule. et lowercase: Change tous les noms de fichier du répertoire courant en minuscule..

Sed, awk et Perl, utilisés comme filtres dans des scripts, prennent des ERs en arguments lorqu'une transformation ou une analyse de fichiers, ou de flux doit se faire. Voir behead: Supprimer les en-têtes des courriers électroniques et des nouvelles et tree: Afficher l'arborescence d'un répertoire pour des illustrations sur ceci.

"Sed & Awk", par Dougherty et Robbins traite les ER d'une façon complète et lucide (voir la ).


Remplacement

Bash lui-même ne reconnaît pas les expressions rationnelles. Dans les scripts, les commandes et utilitaires, tels que sed et awk, interprètent les ER.

Bash effectue bien l'expansion de noms de fichiers, un processus connu sous le nom de "globbing" (NdT: remplacement), mais ceci n'utilise pas les ER standards. A la place, le remplacement reconnaît et étend les jokers. Le remplacement interprète les caractères joker standards, * et ?, les listes de caractères entre crochets, et certains autres caractères spéciaux (tels que ^ pour inverser le sens d'une correspondance). Néanmoins il existe d'importantes limitations sur les caractères joker dans le remplacement. Les chaînes contenant * ne correspondront pas aux noms de fichiers commençant par un point, comme, par exemple, .bashrc. (48) De même, le ? a un sens différent dans le cadre du remplacement et comme partie d'une ER.

bash$ ls -l total 2 -rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 a.1 -rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1 -rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1 -rw-rw-r-- 1 bozo bozo 466 Aug 6 17:48 t2.sh -rw-rw-r-- 1 bozo bozo 758 Jul 30 09:02 test1.txt bash$ ls -l t?.sh -rw-rw-r-- 1 bozo bozo 466 Aug 6 17:48 t2.sh bash$ ls -l [ab]* -rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 a.1 -rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1 bash$ ls -l [a-c]* -rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 a.1 -rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1 -rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1 bash$ ls -l [^ab]* -rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1 -rw-rw-r-- 1 bozo bozo 466 Aug 6 17:48 t2.sh -rw-rw-r-- 1 bozo bozo 758 Jul 30 09:02 test1.txt bash$ ls -l {b*,c*,*est*} -rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1 -rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1 -rw-rw-r-- 1 bozo bozo 758 Jul 30 09:02 test1.txt bash$ echo * a.1 b.1 c.1 t2.sh test1.txt bash$ echo t* t2.sh test1.txt

Même une commande echo réalise une expansion des jokers sur les noms de fichiers.

Voir aussi Agir sur des fichiers à l'aide d'une boucle for.


4.2. Sous-shells

L'exécution d'un script shell lance une nouvelle instance de l'interpréteur de commande. De la même manière que sont interprétées les commandes tapées en ligne de commande, un script bash exécute une liste de commandes lues dans un fichier. Chaque script shell exécuté est en réalité un sous-processus du shell parent, celui qui vous donne une invite à la console ou dans une fenêtre xterm

Un script shell peut également lancer des sous-processus. Ces sous-shells permettent au script de faire de l'exécution en parallèle, donc d'exécuter différentes tâches simultanément.

  • ( commande1; commande2; commande3; ... )
    Une liste de commandes placées entre parenthèses est exécutée sous forme de sous-shells

Remarque : Les variables utilisées dans un sous shell ne sont pas visibles en dehors du code du sous-shell. Elles ne sont pas utilisables par le processus parent, le shell qui a lancé le sous-shell. Elles sont en réalité des variables locales.

Etendue des variables dans un sous-shell
#!/bin/bash # subshell.sh echo variable_externe=Outer ( variable_interne=Inner echo "A partir du sous-shell, \"variable_interne\" = $variable_interne" echo "A partir du sous-shell, \"externe\" = $variable_externe" ) echo if [ -z "$variable_interne" ] then echo "variable_interne non définie dans le corps principal du shell" else echo "variable_interne définie dans le corps principal du shell" fi echo "A partir du corps principal du shell, \"variable_interne\" = $variable_interne" # $variable_interne s'affichera comme non initialisée parce que les variables # définies dans un sous-shell sont des "variables locales". echo exit 0

Voir aussi Problèmes des sous-shell.

+

Le changement de répertoire effectué dans un sous-shell n'a pas d'incidence sur le shell parent.

Lister les profiles utilisateurs
#!/bin/bash # allprofs.sh: affiche tous les profils utilisateur. # Ce script a été écrit par Heiner Steven, et modifié par l'auteur du document. FICHIER=.bashrc # Fichier contenant le profil utilisateur, #+ était ".profile" dans le script original. for home in `awk -F: '{print $6}' /etc/passwd` do [ -d "$home" ] || continue # Si pas de répertoire personnel, passez au #+ suivant. [ -r "$home" ] || continue # Si non lisible, passez au suivant. (cd $home; [ -e $FICHIER ] && less $FICHIER) done # Quand le script se termine, il n'y a pas de besoin de retourner dans le #+ répertoire de départ parce que 'cd $home' prend place dans un sous-shell. exit 0

Un sous-shell peut être utilisé pour mettre en place un "environnement dédié" à un groupe de commandes. L'intérêt peut être par exemple de tester si une variable est définie ou pas. Une autre application est de vérifier si un fichier est marquée comme verrouillé :

Des processus peuvent être exécutés en parallèle dans différents sous-shells. Cela permet de séparer des tâches complexes en plusieurs sous composants exécutés simultanément.

Exécuter des processus en parallèle dans les sous-shells
(cat liste1 liste2 liste3 | sort | uniq > liste123) & (cat liste4 liste5 liste6 | sort | uniq > liste456) & # Concatène et trie les 2 groupes de listes simultanément. # Lancer en arrière plan assure une exécution en parallèle. # # Peut également être écrit : # cat liste1 liste2 liste3 | sort | uniq > liste123 & # cat liste4 liste5 liste6 | sort | uniq > liste456 & wait # Ne pas exécuter la commande suivante tant que les sous-shells # n'ont pas terminé diff liste123 liste456

Redirection des Entrées / Sorties (I/O) dans un sous shell en utilisant "|" , l'opérateur tube (pipe en anglais), par exemple

ls -al | (commande)

.

Remarque : Un bloc de commandes entre accolades ne lance pas un sous-shell.

Remarque : { commande1; commande2; commande3; ... }


4.3. Shells restreints


  • Exécuter un script ou une partie de script en mode restreint désactive certaines commandes qui sinon seraient utilisables. C'est une mesure de sécurité ayant pour objectif de limiter les privilèges de l'utilisateur du script et de minimiser les risques liés à l'exécution de ce script.



  • Utiliser cd pour changer de répertoire courant.



  • Changer la valeur des variables d'environnement suivantes : $PATH, $SHELL, $BASH_ENV, $ENV.


  • Lire ou remplacer les options d'environnement de shell $SHELLOPTS.


  • Redirection de sortie.


  • Faire appel à des commandes contenant un ou plus de /.


  • Faire appel à exec pour substituer un processus différent à celui du shell.


  • Divers autres commandes qui pourraient permettre de détourner le script de son objectif initial.


  • Sortir du mode restreint à l'intérieur d'un script.

Exécuter un script en mode restreint
#!/bin/bash # Commencer le script avec "#!/bin/bash -r" lance le script entier en mode # restreint. echo echo "Changement de répertoire." cd /usr/local echo "Maintenant dans `pwd`" echo "Je retourne à la maison." cd echo "Maintenant dans `pwd`" echo # Tout jusqu'ici est en mode normal, non restreint. set -r # set --restricted a le même effet. echo "==> Maintenant en mode restreint. <==" echo echo echo "Tentative de changement de répertoire en mode restreint." cd .. echo "Toujours dans `pwd`" echo echo echo "\$SHELL = $SHELL" echo "Tentative de changement de shell en mode restreint." SHELL="/bin/ash" echo echo "\$SHELL= $SHELL" echo echo echo "Tentative de redirection de sortie en mode restreint." ls -l /usr/bin > bin.files ls -l bin.files # Essayez de lister le fichier que l'on a tenté de créer. echo exit 0

4.4. Substitution de processus

La substitution de processus est la contre-partie de la substitution de commande. La substitution de commande affecte à une variable le résultat d'une commande, comme dans contenu_rep=`ls -al` ou xref=$( grep mot fichdonnées). La substitution de commande "nourrit" un processus avec la sortie d'un autre processus (en d'autres termes, elle envoie le résultat d'une commande à une autre commande).

  • commande à l'intérieur de parenthèses
    (commande) (commande)Ceci lance la substitution de processus. Cette syntaxe utilise les fichiers /dev/fd/n pour envoyer le résultat du processus entre parenthèses vers un autre processus. (49) Remarque:Il n'y a pas d'espace entre le "" ou "" et les parenthèses. Ici, un espace génèrerait un message d'erreur.

bash$ echo (true) /dev/fd/63 bash$ echo (true) /dev/fd/63

Bash crée un tube avec deux descripteurs de fichiers, --fIn et fOut--. Le stdin (entrée standard) de true se connecte à fOut (la sortie standard) (dup2(fOut, 0)), puis Bash passe un /dev/fd/fIn comme argument à la commande echo. Sur les systèmes sans fichier /dev/fd/n, Bash peut utiliser des fichiers temporaires. (Merci, S.C.)

Un lecteur de ce document a envoyé cet intéressant exemple de substitution de processus.


4.5. Fonctions

Comme les "vrais" langages de programmation, Bash supporte les fonctions, bien qu'il s'agisse d'une implémentation quelque peu limitée. Une fonction est une sous-routine, un bloc de code qui implémente un ensemble d'opérations, une "boîte noire" qui réalise une tâche spécifiée. Quand il y a un code répétitif, lorsqu'une tâche se répère avec quelques légères variations, alors utilisez une fonction.


function nom_fonction{
commande...
}

ou
nom_fonction(){
commande...
}

Cette deuxième forme plaira aux programmeurs C (et est plus portable).

Comme en C, l'accolade ouvrante de la fonction peut apparaître de manière optionnelle sur la deuxième ligne.


nom_fonction()
{
commande...
}

Les fonctions sont appelées, lancées, simplement en invoquant leur noms.

Simple fonction
#!/bin/bash funky () { echo "Ceci est la fonction funky." echo "Maintenant, sortie de la fonction funky." } # La déclaration de la fonction doit précéder son appel. # Maintenant, appelons la fonction. funky exit 0

La définition de la fonction doit précéder son premier appel. Il n'existe pas de méthode pour "déclarer" la fonction, comme, par exemple, en C.

Il est même possible d'intégrer une fonction dans une autre fonction bien que cela ne soit pas très utile.

Les déclarations des fonctions peuvent apparaître dans des endroits bien étonnants, même là où irait plutôt une commande.


Fonctions complexes et complexité des fonctions

Les fonctions peuvent récupérer des arguments qui leur sont passés et renvoyer un code de sortie au script pour utilisation ultérieure.

La fonction se réfère aux arguments passés par leur position (comme si ils étaient des paramètres positionnels), c'est-à-dire $1, $2, et ainsi de suite.

Fonction prenant des paramètres
#!/bin/bash # Fonctions et paramètres DEFAUT=defaut # Valeur par défaut. fonc2 () { if [ -z "$1" ] # Est-ce que la taille du paramètre # #1 a une taille zéro? then echo "-Le paramètre #1 a une taille nulle.-" # Ou aucun paramètre n'est passé. else echo "-Le paramètre #1 est \"$1\".-" fi variable=${1-$DEFAUT} # Que montre la substitution de echo "variable = $variable" #+ paramètre? # --------------------------- # Elle fait la distinction entre l'absence l'abssence #+ de paramètre et paramètre nul. if [ "$2" ] then echo "-Le paramètre #2 est \"$2\".-" fi return 0 } echo echo "Aucun argument." fonc2 # Appelé sans argument echo echo "Argument de taille nulle." fonc2 "" # Appelé avec un paramètre de taille zéro echo echo "Paramètre nul." fonc2 "$parametre_non_initialise" # Appelé avec un paramètre non initialisé echo echo "Un paramètre." fonc2 first # Appelé avec un paramètre echo echo "Deux paramètres." fonc2 first second # Appelé avec deux paramètres echo echo "\"\" \"second\" comme argument." fonc2 "" second # Appelé avec un premier paramètre de taille nulle, echo # et une chaîne ASCII pour deuxième paramètre. exit 0

IMPORTANT:La commande shift fonctionne sur les arguments passés aux fonctions (voir Astuce de valeur de retour).

Remarque : En contraste avec certains autres langages de programmation, les scripts shell passent normalement seulement des paramètres par valeur aux fonctions. (50) Les noms de variables (qui sont réellement des pointeurs), s'ils sont passés en tant que paramètres de fonctions, seront traités comme des chaînes de caractères et ne pourront être déréférencés. Les fonctions interprètent leur arguments littéralement.

  • code de sortie
    Les fonctions renvoient une valeur, appellée un code (ou état) de sortie. Le code de sortie peut être explicitement spécifié par une instruction return, sinon il s'agit du code de sortie de la dernière commande de la fonction (0 en cas de succès et une valeur non nulle sinon). Ce status de sortie peut être utilisé dans le script en le référanceant à l'aide de la variable $?. Ce mécanisme permet effectivement aux fonctions des scripts d'avoir une "valeur de retour" similaire à celle des fonctions C.

  • return
    Termine une fonction. Une commande return (51) prend optionnellement un argument de type entier, qui est renvoyé au script appelant comme "code de sortie" de la fonction, et ce code de sortie est affecté à la variable $?.
  • Maximum de deux nombres
    #!/bin/bash # max.sh: Maximum de deux entiers. E_PARAM_ERR=-198 # Si moins de deux paramètres passés à la fonction. EGAL=-199 # Code de retour si les deux paramètres sont égaux. max2 () # Envoie le plus important des deux entiers. { # Note: les nombres comparés doivent être plus petits que 257. if [ -z "$2" ] then return $E_PARAM_ERR fi if [ "$1" -eq "$2" ] then return $EGAL else if [ "$1" -gt "$2" ] then return $1 else return $2 fi fi } max2 33 34 return_val=$? if [ "$return_val" -eq $E_PARAM_ERR ] then echo "Vous devez donner deux arguments à la fonction." elif [ "$return_val" -eq $EGAL ] then echo "Les deux nombres sont identiques." else echo "Le plus grand des deux nombres est $return_val." fi exit 0 # Exercice (facile): # --------------- # Convertir ce script en une version interactive, #+ c'est-à-dire que le script vous demande les entrées (les deux nombres).
  • Astuce:Pour qu'une fonction renvoie une chaîne de caractères ou un tableau, utilisez une variable dédiée.
  • Convertire des nombres en chiffres romains
    #!/bin/bash # Conversion d'un nombre arabe en nombre romain # Echelle: 0 - 200 # C'est brut, mais cela fonctionne. # Etendre l'échelle et améliorer autrement le script est laissé en exercice. # Usage: roman nombre-a-convertir LIMITE=200 E_ERR_ARG=65 E_HORS_ECHELLE=66 if [ -z "$1" ] then echo "Usage: `basename $0` nombre-a-convertir" exit $E_ERR_ARG fi num=$1 if [ "$num" -gt $LIMITE ] then echo "En dehors de l'échelle!" exit $E_HORS_ECHELLE fi vers_romain () # Doit déclarer la fonction avant son premier appel. { nombre=$1 facteur=$2 rchar=$3 let "reste = nombre - facteur" while [ "$reste" -ge 0 ] do echo -n $rchar let "nombre -= facteur" let "reste = nombre - facteur" done return $nombre # Exercice: # -------- # Expliquer comment fonctionne cette fonction. # Astuce: division par une soustraction successive. } vers_romain $nombre 100 C nombre=$? vers_romain $nombre 90 LXXXX nombre=$? vers_romain $nombre 50 L nombre=$? vers_romain $nombre 40 XL nombre=$? vers_romain $nombre 10 X nombre=$? vers_romain $nombre 9 IX nombre=$? vers_romain $nombre 5 V nombre=$? vers_romain $nombre 4 IV nombre=$? vers_romain $nombre 1 I echo exit 0
  • Voir aussi Vérification d'une entrée alphabétique. IMPORTANT:L'entier positif le plus grand qu'une fonction peut renvoyer est 256. La commande return est très liée au code de sortie, qui tient compte de cette limite particulière. Heureusement, il existe quelques astuces pour ces situations réclamant une valeur de retour sur un grand entier.
  • Tester les valeurs de retour importantes dans une fonction
    #!/bin/bash # return-test.sh # La plus grande valeur positive qu'une fonction peut renvoyer est 256. test_retour () # Renvoie ce qui lui est passé. { return $1 } test_retour 27 # OK. echo $? # Renvoie 27. test_retour 256 # Toujours OK. echo $? # Renvoie 256. test_retour 257 # Erreur! echo $? # Renvoie 1 (code d'erreur divers). test_retour -151896 # Néanmoins, les valeurs négatives peuvent être plus #+ importantes. echo $? # Renvoie -151896. exit 0
  • IMPORTANT:Comme nous l'avons vu, une fonction peut retourner une valeur négative importante. Ceci permet aussi de retourner un grand entier positif, en utilisant un peu d'astuce. IMPORTANT:Un autre moyen d'accomplir ceci est d'affecter simplement le "code de retour" à une variable globale.
  • Comparer deux grands entiers
    #!/bin/bash # max2.sh: Maximum de deux entiers LARGES. # Ceci correspond au précédent exemple "max.sh", modifié pour permettre la # comparaison d'entiers larges. EGAL=0 # Code de retour si les deux paramètres sont égaux. VALRETOURMAX=256 # Code de retour positif maximum à partir d'une fonction. E_ERR_PARAM=-99999 # Erreur de paramètre. E_ERR_NPARAM=99999 # Erreur de paramètre "normalisé". max2 () # Renvoie le plus grand des deux nombres. { if [ -z "$2" ] then return $E_ERR_PARAM fi if [ "$1" -eq "$2" ] then return $EGAL else if [ "$1" -gt "$2" ] then retval=$1 else retval=$2 fi fi # -------------------------------------------------------------- # # Ceci est une astuce pour renvoyer un entier large à partir de # cette fonction. if [ "$retval" -gt "$VALRETOURMAX" ] # Si en dehors de l'échelle, then # alors let "retval = (( 0 - $retval ))" # ajuster à une valeur négative. # (( 0 - $VALUE )) change le signe de VALUE. fi # Heureusement les codes de retour négatifs larges sont permis. # -------------------------------------------------------------- # return $retval } max2 33001 33997 return_val=$? # -------------------------------------------------------------------------- # if [ "$return_val" -lt 0 ] # Si nombre négatif "ajusté", then # alors let "return_val = (( 0 - $return_val ))" # retour au positif. fi # "Valeur absolue" de $return_val. # -------------------------------------------------------------------------- # if [ "$return_val" -eq "$E_ERR_NPARAM" ] then # La variable d'erreur de paramètre a aussi changé # de signe. echo "Erreur: Pas assez de paramètres." elif [ "$return_val" -eq "$EGAL" ] then echo "Les deux nombres sont égaux." else echo "Le plus grand des deux nombres est $return_val." fi exit 0
  • IMPORTANT:Voir aussi days-between: Calculer le nombre de jours entre deux dates. IMPORTANT:
  • Exercice:
  • Utiliser ce que nous venons d'apprendre, étendre l'exemple précédent sur le nombres romains pour accepter une entrée arbitrairement grande.

  • Rediriger le stdin d'une fonction
    Une fonction est essentiellement un bloc de code, ce qui signifie que stdin peut être redirigé (comme dans Blocs de code et redirection d'E/S).
  • Vrai nom pour un utilisateur
    #!/bin/bash # A partir du nom utilisateur, obtenir le "vrai nom" dans /etc/passwd. NBARGS=1 # Attend un arg. E_MAUVAISARGS=65 fichier=/etc/passwd modele=$1 if [ $# -ne "$NBARGS" ] then echo "Usage: `basename $0` NOMUTILISATEUR" exit $E_MAUVAISARGS fi partie_fichier () # Scanne le fichier pour trouver le modèle, la portion pertinante # des caractères de la ligne. { while read ligne # while n'a pas nécessairement besoin d'une "[ condition]" do echo "$ligne" | grep $1 | awk -F":" '{ print $5 }' # awk utilise le délimiteur ":". done } $fichier # Redirige dans le stdin de la fonction. partie_fichier $modèle # Oui, le script entier peut être réduit en # grep MODELE /etc/passwd | awk -F":" '{ print $5 }' # ou # awk -F: '/MODELE/ {print $5}' # ou # awk -F: '($1 == "nomutilisateur") { print $5 }' # vrai nom à partir du nom utilisateur # Néanmoins, ce n'est pas aussi instructif. exit 0
  • Il existe une autre méthode, certainement moins compliquée , de rediriger le stdin d'une fonction. Celle-ci fait intervenir la redirection de stdin vers un bloc de code entre accolades contenu à l'intérieur d'une fonction.


Variables locales

  • variables locales
    Une variable déclarée localement n'est visible qu'à l'intérieur du bloc de code dans laquelle elle apparaît. Elle a une "visibilité" locale. Dans une fonction, une variable locale n' a une signification qu'à l'intérieur du bloc de la fonction.
  • Visibilité de la variable locale
    #!/bin/bash fonc () { local var_local=23 # Déclaré en local. echo echo "\"var_local\" dans la fonction = $var_local" var_global=999 # Non déclaré en local. echo "\"var_global\" dans la fonction = $var_global" } fonc # Maintenant, voyons si il existe une variable locale en dehors de la fonction. echo echo "\"var_loc\" en dehors de la fonction = $var_loc" # "var_loc" en dehors de la fonction = # Non, $var_local n'est pas visible. echo "\"var_global\" en dehors de la fonction = $var_global" # "var_global" en dehors de la fontion = 999 # $var_global est visible globalement. echo exit 0
  • Attention:Avant qu'une fonction ne soit appelée, toutes les variables déclarées dans la fonction sont invisibles à l'extérieur du corps de la fonction, et pas seulement celles déclarées explicitement locales.


Les variables locales rendent la récursion possible.

Les variables locales permettent la récursion, (52) mais cette pratique implique généralement beaucoup de calculs supplémentaires et n'est vraiment pas recommendée dans un script shell. (53)

Récursion, en utilisant une variable locale
#!/bin/bash # facteurs # --------- # Bash permet-il la récursion? # Eh bien, oui, mais... # Vous devrez vous accrocher pour y arriver. MAX_ARG=5 E_MAUVAIS_ARGS=65 E_MAUVAISE_ECHELLE=66 if [ -z "$1" ] then echo "Usage: `basename $0` nombre" exit $E_MAUVAIS_ARGS fi if [ "$1" -gt $MAX_ARG ] then echo "En dehors de l'échelle (5 est le maximum)." # Maintenant, allons-y. # Si vous souhaitez une échelle plus importante, réécrivez-le # dans un vrai langage de programmation. exit $E_MAUVAISE_ECHELLE fi fact () { local nombre=$1 # La variable "nombre" doit être déclarée en local. # Sinon cela ne fonctionne pas. if [ "$nombre" -eq 0 ] then factoriel=1 # Le factoriel de 0 = 1. else let "decrnum = nombre - 1" fact $decrnum # Appel à la fonction récursive. let "factoriel = $nombre * $?" fi return $factoriel } fact $1 echo "Le factoriel de $1 est $?." exit 0

Voir aussi primes: Générer des nombres aléatoires en utilisant l'opérateur modulo pour un exemple de récursion dans un script. Faites attention que la récursion demande beaucoup de ressources et s'exécute lentement. Son utilisation n'est donc pas appropriée dans un script.


4.6. Alias

Un alias Bash n'est essentiellement rien de plus qu'un raccourci clavier, une abbréviation, un moyen d'éviter de taper une longue séquence de commande. Si, par exemple, nous incluons alias lm="ls -l | more" dans le fichier ~/.bashrc, alors chaque

lm

tapé sur la ligne de commande sera automatiquement remplacé par un ls -l | more. Ceci peut économiser beaucoup de temps lors de frappes en ligne de commande et éviter d'avoir à se rappeler des combinaisons complexes de commandes et d'options. Diposer de alias rm="rm -i" (suppression en mode interactif) peut vous empêcher de faire des bêtises, car il previent la perte par inavertance de fichiers importants.

Dans un script, les alias ont une utilité très limitée. Il serait assez agréable que les alias assument certaines des fonctionnalités du préprocesseur C, telles que l'expansion de macros mais malheureusement Bash ne supporte pas l'expansion d'arguments à l'intérieur du corps des alias. (54) Pire encore, un script échoue à étendre un alias lui-même à l'intérieur d'une "construction composée" , telle que les instructions if/then, les boucles et les fonctions. Une limitation supplémentaire est qu'un alias ne peut être étendu récursivement. De façon pratiquement invariable, tout ce que nous voudrions que les alias puissent faire est faisable bien plus efficacement avec une fonction.

Alias à l'intérieur d'un script
#!/bin/bash # Appelez-le avec un paramètre en ligne de commande pour utiliser la dernière section de #+ ce script. shopt -s expand_aliases # Cette option doit être activée, sinon le script n'étendra pas les alias. # Tout d'abord, un peu d'humour. alias Jesse_James='echo "\"Alias Jesse James\" était une comédie de 1959 avec Bob Hope."' Jesse_James echo; echo; echo; alias ll="ls -l" # Vous pouvez utiliser soit les simples guillemets (') soit les doubles (") pour définir #+ un alias. echo "Essai de l'alias \"ll\":" ll /usr/X11R6/bin/mk* #* L'alias fonctionne. echo repertoire=/usr/X11R6/bin/ prefixe=mk* # Voir si le caractère joker va causer des problèmes. echo "Les variables \"repertoire\" + \"prefixe\" = $repertoire$prefixe" echo alias lll="ls -l $repertoire$prefixe" echo "Essai de l'alias \"lll\":" lll # Longue liste de tous les fichiers de /usr/X11R6/bin commençant avec mk. # Les alias gèrent les variables concaténées, en incluant les caractères joker. VRAI=1 echo if [ VRAI ] then alias rr="ls -l" echo "Essai de l'alias \"rr\" à l'intérieur d'une instruction if/then:" rr /usr/X11R6/bin/mk* #* Message d'erreur! # Les aliases ne sont pas étendues à l'intérieur d'instructions composées. echo "Néanmoins, l'alias précédemment étendu est toujours reconnu:" ll /usr/X11R6/bin/mk* fi echo count=0 while [ $count -lt 3 ] do alias rrr="ls -l" echo "Essai de l'alias \"rrr\" à l'intérieur de la boucle \"while\":" rrr /usr/X11R6/bin/mk* #* L'alias ne sera pas étendu ici non plus. # alias.sh: line 57: rrr: command not found let count+=1 done echo; echo alias xyz='cat $0' # Le script se liste lui-même. # Notez les simples guillemets. xyz # Ceci semble fonctionne, #+ bien que la documentation Bash suggère que cela ne devrait pas. # # Néanmoins, comme l'indique Steve Jacobson, #+ le paramètre "$0" s'étend tout de suite après la déclaration de l'alias. exit 0

La commande unalias supprime un alias précédemment configuré.

unalias: Configurer et supprimer un alias
#!/bin/bash shopt -s expand_aliases # Active l'expansion d'alias. alias llm='ls -al | more' llm echo unalias llm # Supprime la configuration de l'alias. llm # Résulte en un message d'erreur, car 'llm' n'est plus reconnu. exit 0
bash$ ./unalias.sh total 6 drwxrwxr-x 2 bozo bozo 3072 Feb 6 14:04 . drwxr-xr-x 40 bozo bozo 2048 Feb 6 14:04 .. -rwxr-xr-x 1 bozo bozo 199 Feb 6 14:04 unalias.sh ./unalias.sh: llm: command not found

4.7. Constructeurs de listes

Les constructions de "liste and" et de "liste or" apportent un moyen de réaliser un certain nombre de commandes consécutivement. Elles peuvent remplacer efficacement des if/then complexes, voire imbriqués ou même des instructions case.

  • liste and
    Chaque commande s'exécute à son tour à condition que la dernière commande ait renvoyé un code de retour true (zéro). Au premier retour false (différent de zéro), la chaîne de commande s'arrête (la première commande renvoyant false est la dernière à être exécutée).
  • Utiliser une liste and pour tester des arguments de la ligne de commande
    #!/bin/bash # "liste et" if [ ! -z "$1" ] && echo "Argument #1 = $1" && [ ! -z "$2" ] && echo "Argument #2 = $2" then echo "Au moins 2 arguments passés au script." # Toute la commande chaînée doit être vraie. else echo "Moins de 2 arguments passés au script." # Au moins une des commandes de la chaîne a renvoyé faux. fi # Notez que "if [ ! -z $1 ]" fonctionne, mais que son supposé équivalent, # if [ -n $1 ] ne fonctionne pas. Néanmoins, mettre entre guillemets corrige # cela: if [ -n "$1" ] fonctionne. Attention! # Il est mieux de toujours mettre entre guillemets les variables testées. # Ceci accomplit la même chose, en utilisant une instruction if/then pure. if [ ! -z "$1" ] then echo "Argument #1 = $1" fi if [ ! -z "$2" ] then echo "Argument #2 = $2" echo "Au moins 2 arguments passés au script." else echo "Moins de 2 arguments passés au script." fi # C'est plus long et moins élégant que d'utiliser une "liste et". exit 0
  • Un autre test des arguments de la ligne de commande en utilisant une liste and
    #!/bin/bash ARGS=1 # Nombre d'arguments attendus. E_BADARGS=65 # Valeur de sortie si un nombre incorrect d'arguments est passé. test $# -ne $ARGS && echo "Usage: `basename $0` $ARGS argument(s)" && exit $E_BADARGS # Si condition-1 vrai (mauvais nombre d'arguments passés au script), # alors le reste de la ligne s'exécute et le script se termine. # La ligne ci-dessous s'exécute seulement si le test ci-dessus a échoué. echo "Bon nombre d'arguments passés à ce script." exit 0 # Pour vérifier la valeur de sortie, faites un "echo $?" après la fin du script.
  • Bien sûr, une liste and peut aussi initialiser des variables à une valeur par défaut.

  • liste or
    Chaque commande s'exécute à son tour aussi longtemps que la commande précédente renvoie false. Au premier retour true, la chaîne de commandes s'arrête (la première commande renvoyant true est la dernière à être exécutée). C'est évidemment l'inverse de la "liste and" .
  • Utiliser des listes or en combinaison avec une liste and
    #!/bin/bash # delete.sh, utilitaire pas-si-stupide de suppression de fichier. # Usage: delete nomfichier E_MAUVAISARGS=65 if [ -z "$1" ] then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS # Pas argument? On sort. else fichier=$1 # Initialisation du nom du fichier. fi [ ! -f "$fichier" ] && echo "Le fichier \"$fichier\" introuvable. \ Je refuse peureusement d'effacer un fichier inexistant." # LISTE ET, pour donner le message d'erreur si le fichier est absent. # Notez que le message echo continue sur la seconde ligne avec un échappement. [ ! -f "$file" ] || (rm -f $file; echo "Fichier \"$file\" supprimé.") # LISTE OU, pour supprimer le fichier si présent. # Notez la logique inversée ci-dessus. # La LISTE-ET s'exécute si vrai, la LISTE-OU si faux. exit 0
  • Attention:Si la première commande dans une "liste or" renvoie true, elle sera exécutée.

IMPORTANT:Le code de sortie d'une

liste and

ou d'une

liste or

correspond au code de sortie de la dernière commande exécutée.

Les combinaisons intelligentes de listes "and" et "or" sont possibles, mais la logique pourrait rapidement devenir difficile et nécessiter des phases de déboguages intensives.

Voir days-between: Calculer le nombre de jours entre deux dates et Test de liens cassés pour des illustrations de l'utilisation de listes and / or pour tester des variables.


4.8. Tableaux

Les versions récentes de Bash supportent les tableaux à une dimension. Les éléments du tableau devraient être initialisés avec la notation

variable[xx]

. Autrement, un script peut introduire le tableau entier par une instruction explicite

declare -a variable

. Pour déréférencer (trouver le contenu d') un élément du tableau, utilisez la notation à accolade, c'est-à-dire

${variable[xx]}

.

Utilisation d'un tableau simple
#!/bin/bash aire[11]=23 aire[13]=37 aire[51]=UFOs # Les membres d'un tableau peuvent ne pas être consécutifs ou contigus. # Certains membres peuvent rester non initialisés. # Les trous dans le tableau sont OK. echo -n "aire[11] = " echo ${aire[11]} # {accolades} nécessaires echo -n "aire[13] = " echo ${aire[13]} echo "Le contenu de aire[51] est ${aire[51]}." # Le contenu d'une variable non initialisée d'un tableau n'affiche rien. echo -n "aire[43] = " echo ${aire[43]} echo "(aire[43] non affecté)" echo # Somme de deux variables tableaux affectée à une troisième. aire[5]=`expr ${aire[11]} + ${aire[13]}` echo "aire[5] = aire[11] + aire[13]" echo -n "aire[5] = " echo ${aire[5]} aire[6]=`expr ${aire[11]} + ${aire[51]}` echo "aire[6] = aire[11] + aire[51]" echo -n "aire[6] = " echo ${aire[6]} # Ceci échoue car ajouter un entier à une chaîne de caractères n'est pas permis. echo; echo; echo # ----------------------------------------------------------------- # Autre tableau, "aire2". # Autre façon d'affecter les variables d'un tableau... # nom_tableau=( XXX YYY ZZZ ... ) aire2=( zero un deux trois quatre ) echo -n "aire2[0] = " echo ${aire2[0]} # Aha, indexage commençant par 0 (le premier élément du tableau est [0], et non # pas [1]). echo -n "aire2[1] = " echo ${aire2[1]} # [1] est le deuxième élément du tableau. # ----------------------------------------------------------------- echo; echo; echo # ----------------------------------------------- # Encore un autre tableau, "aire3". # Encore une autre façon d'affecter des variables de tableau... # nom_tableau=([xx]=XXX [yy]=YYY ...) aire3=([17]=dix-sept [24]=vingt-quatre) echo -n "aire3[17] = " echo ${aire3[17]} echo -n "aire3[24] = " echo ${aire3[24]} # ----------------------------------------------- exit 0
Formattage d'un poème
#!/bin/bash # poem.sh # Lignes d'un poème (simple stanza). Ligne[1]="I do not know which to prefer," Ligne[2]="The beauty of inflections" Ligne[3]="Or the beauty of innuendoes," Ligne[4]="The blackbird whistling" Ligne[5]="Or just after." # Attribution. Attrib[1]=" Wallace Stevens" Attrib[2]="\"Thirteen Ways of Looking at a Blackbird\"" for index in 1 2 3 4 5 # Cinq lignes. do printf " %s\n" "${Ligne[index]}" done for index in 1 2 # Deux lignes d'attribution. do printf " %s\n" "${Attrib[index]}" done exit 0

Les variables tableau ont une syntaxe propre, et même les commandes standard Bash et les opérateurs ont des options spécifiques adaptées à l'utilisation de tableaux.

Dans un contexte de tableau, quelques commandes intégrées Bash ont une signification légèrement modifiée. Par exemple, unset supprime des éléments du tableau, voire un tableau entier.

Quelques propriétés spéciales des tableaux
#!/bin/bash eclare -a colors # Permet de déclarer un tableau sans spécifier sa taille. echo "Entrez vos couleurs favorites (séparées par un espace)." read -a couleurs # Entrez au moins trois couleurs pour démontrer les #+ fonctionnalités ci-dessous. # Option spéciale pour la commande 'read', #+ permettant d'affecter les éléments dans un tableau. echo nb_element=${#colors[@]} # Syntaxe spéciale pour extraire le nombre d'éléments d'un tableau. # nb_element=${#colors[*]} fonctionne aussi. # # La variable "@" permet de diviser les mots compris dans des guillemets #+ (extrait les variables séparées par des espaces blancs). index=0 while [ "$index" -lt "$nb_element" ] do # Liste tous les éléments du tableau. echo ${colors[$index]} let "index = $index + 1" done # Chaque élément du tableau est listé sur une ligne séparée. # Si ceci n'est pas souhaité, utilisez echo -n "${colors[$index]} " # # Pour le faire avec une boucle "for": # for i in "${colors[@]}" # do # echo "$i" # done # (Thanks, S.C.) echo # Encore une fois, liste tous les éléments d'un tableau, mais en utilisant une #+ méthode plus élégante. echo ${colors[@]} # echo ${colors[*]} fonctionne aussi. echo # La commande "unset" supprime les éléments d'un tableau, ou un tableau entier. unset colors[1] # Supprime le deuxième élément d'un tableau. # Même effet que colors[1]= echo ${colors[@]} # Encore un tableau liste, dont le deuxième # élément est manquant. unset colors # Supprime le tableau entier. # unset colors[*] et #+ unset colors[@] fonctionnent aussi. echo; echo -n "Colors gone." echo ${colors[@]} # Liste le tableau une nouvelle fois, maintenant #+ vide. exit 0

Comme vu dans le précédent exemple, soit ${nom_tableau[@]} soit ${nom_tableau[*]} fait réfèrence à tous les éléments du tableau. De même, pour obtenir le nombre d'éléments dans un tableau, utilisez soit ${#nom_tableau[@]} soit ${#nom_tableau[*]}. ${#nom_tableau} est la longueur (nombre de caractères) de ${nom_tableau[0]}, le premier élément du tableau.

Des tableaux vides et des éléments vides
#!/bin/bash # empty-array.sh # Un tableau vide n'est pas la même chose qu'un tableau avec des éléments vides. tableau0=( premier deuxieme troisieme ) tableau1=( '' ) # "tableau1" a un élément vide. tableau2=( ) # Pas d'éléments... "tableau2" est vide. echo echo "Eléments dans le tableau0: ${tableau0[@]}" echo "Eléments dans le tableau1: ${tableau1[@]}" echo "Eléments dans le tableau2: ${tableau2[@]}" echo echo "Longueur du premier élément dans le tableau0 = ${#tableau0}" echo "longueur du premier élément dans le tableau1 = ${#tableau1}" echo "longueur du premier élément dans le tableau2 = ${#tableau2}" echo echo "Nombre d'éléments dans le tableau0 = ${#tableau0[*]}" # 3 echo "Nombre d'éléments dans le tableau1 = ${#tableau1[*]}" # 1 (surprise!) echo "Nombre d'éléments dans le tableau2 = ${#tableau2[*]}" # 0 echo exit 0 # Merci, S.C.

La relation entre ${nom_tableau[@]} et ${nom_tableau[*]} est analogue à celle entre $@ et $*. Cette notation de tableau très puissante a un certain nombre d'intérêts.

Astuce : L'opération d'initialisation tableau=( element1 element2 ... elementN ), avec l'aide de la substitution de commandes, rend possible de charger le contenu d'un fichier texte dans un tableau.

Astuce :

Les tableaux permettent de déployer de bons vieux algorithmes familiers en scripts shell. Que ceci soit obligatoirement une bonne idée est laissé à l'appréciation du lecteur.

Un viel ami: Le tri Bubble Sort
#!/bin/bash # bubble.sh: Tri bulle, en quelque sorte. # Rappelle l'algorithme de tri bulle. Enfin, une version particulière... # A chaque itération successive à travers le tableau à trier, compare deux #+ éléments adjacents et les échange si ils ne sont pas ordonnés. # A la fin du premier tour, l'élémennt le "plus lourd" est arrivé tout en bas. # A la fin du deuxième tour, le "plus lourd" qui suit est lui-aussi à la fin #+ mais avant le "plus lourd". # Et ainsi de suite. # Ceci signifie que chaque tour a besoin de se balader sur une partie de plus #+ en plus petite du tableau. # Vous aurez donc noté un accélération à l'affichage lors des derniers tours. echange() { # Echange deux membres d'un tableau local temp=${Pays[$1]} # Stockage temporaire #+ pour les éléments à échanger. Pays[$1]=${Pays[$2]} Pays[$2]=$temp return } declare -a Pays # Déclaration d'un tableau, #+ optionnel ici car il est initialisé tout de suite après. # Est-il permis de diviser une variable tableau sur plusieurs lignes en #+ utilisant un caractère d'échappement? # Oui. Pays=(Hollande Ukraine Zaire Turquie Russie Yémen Syrie \ Brésil Argentine Nicaragua Japon Mexique Vénézuela Grèce Angleterre \ Israël Pérou Canada Oman Danemark France Kenya \ Xanadu Qatar Liechtenstein Hongrie) # "Xanadu" est la place mythique où, selon Coleridge, #+ Kubla Khan a "pleasure dome decree". clear # Efface l'écran pour commencer. echo "0: ${Pays[*]}" # Liste le tableau entier lors du premier tour. nombre_d_elements=${#Pays[@]} let "comparaisons = $nombre_d_elements - 1" index=1 # Nombre de tours. while [ "$comparaisons" -gt 0 ] # Début de la boucle externe. do index=0 # Réinitialise l'index pour commencer au début du tableau à chaque #+ tour. while [ "$index" -lt "$comparaisons" ] # Début de la boucle interne. do if [ ${Pays[$index]} \> ${Pays[`expr $index + 1`]} ] # Si non ordonné... # Rappelez-vous que \> est un opérateur de comparaison ASCII à l'intérieur #+ de simples crochets. # if [[ ${Pays[$index]} > ${Pays[`expr $index + 1`]} ]] #+ fonctionne aussi. then echange $index `expr $index + 1` # Echange. fi let "index += 1" done # Fin de la boucle interne. let "comparaisons -= 1" # Comme l'élément le "plus lourd" est tombé en bas, #+ nous avons besoin de faire une comparaison de moins #+ à chaque tour. echo echo "$index: ${Pays[@]}" # Affiche le tableau résultat à la fin de chaque tour echo let "index += 1" # Incrémente le compteur de tour. done # Fin de la boucle externe. # Fini. exit 0

--

Les tableaux permettent l'implémentation d'une version script shell du Crible d' Eratosthene. Bien sûr, une application intensive en ressources de cette nature devrait être réellement écrite avec un langage compilé tel que le C. Il fonctionne lamentablement lentement en tant que script.

Application complexe des tableaux : Crible d' Eratosthene
#!/bin/bash # sieve.sh # Sieve of Eratosthenes # Ancien algorithme pour trouver les nombres premiers. # Ceci s'exécute bien moins rapidement que le programme équivalent en C. LIMITE_BASSE=1 # Commençant avec 1. LIMITE_HAUTE=1000 # Jusqu'à 1000. # (Vous pouvez augmenter cette valeur... si vous avez du temps devant vous.) PREMIER=1 NON_PREMIER=0 let DIVISE=LIMITE_HAUTE/2 # Optimisation: # Nécessaire pour tester les nombres à mi-chemin de la limite supérieure. declare -a Premiers # Premiers[] est un tableau. initialise () { # Initialise le tableau. i=$LIMITE_BASSE until [ "$i" -gt "$LIMITE_HAUTE" ] do Premiers[i]=$PREMIER let "i += 1" done # Assume que tous les membres du tableau sont coupables (premiers) avant d'être # reconnus innocents. } affiche_premiers () { # Affiche les membres du tableau Premiers[] indiqués comme premiers. i=$LIMITE_BASSE until [ "$i" -gt "$LIMITE_HAUTE" ] do if [ "${Premiers[i]}" -eq "$PREMIER" ] then printf "%8d" $i # 8 espaces par nombre rend l'affichage joli, avec colonne. fi let "i += 1" done } examine () # Examine minutieusement les non premiers. { let i=$LIMITE_BASSE+1 # Nous savons que 1 est premier, donc commençons avec 2. until [ "$i" -gt "$LIMITE_HAUTE" ] do if [ "${Premiers[i]}" -eq "$PREMIER" ] # Ne nous embêtons pas à examiner les nombres déjà examinés (indiqués comme #+ non premiers). then t=$i while [ "$t" -le "$LIMITE_HAUTE" ] do let "t += $i " Premiers[t]=$NON_PREMIER # Indiqué comme non premier tous les multiples. done fi let "i += 1" done } # Appeler les fonctions séquenciellement. initialise examine affiche_premiers # C'est ce qu'ils appelent de la programmation structurée. echo exit 0 # ----------------------------------------------- # # Le code ci-dessous ne sera pas executé. # Cette version améliorée de Sieve, par Stephane Chazelas, # s'exécute un peu plus rapidement. # Doit être appelé avec un argument en ligne de commande (limite des premiers). LIMITE_HAUTE=$1 # A partir de la ligne de commande. let DIVISE=LIMITE_HAUTE/2 # Mi-chemin du nombre max. Premiers=( '' $(seq $LIMITE_HAUTE) ) i=1 until (( ( i += 1 ) > DIVISE )) # A besoin de vérifier à mi-chemin. do if [[ -n $Premiers[i] ]] then t=$i until (( ( t += i ) > LIMITE_HAUTE )) do Premiers[t]= done fi done echo ${Premiers[*]} exit 0

Comparez ce générateur de nombres premiers basé sur les tableaux avec un autre ne les utilisant pas, primes: Générer des nombres aléatoires en utilisant l'opérateur modulo.

--

Les tableaux tendent eux-même à émuler des structures de données pour lesquelles Bash n'a pas de support natif.

Emuler une pile
#!/bin/bash # stack.sh: simulation d'une pile place-down # Similaire à la pile du CPU, une pile "place-down" enregistre les éléments #+ séquentiellement, mais les récupère en ordre inverse, le dernier entré étant #+ le premier sorti. BP=100 # Pointeur de base du tableau de la pile. # Commence à l'élément 100. SP=$BP # Pointeur de la pile. # Initialisé à la base (le bas) de la pile. Donnees= # Contenu de l'emplacement de la pile. # Doit être une variable locale à cause de la limitation #+ sur l'échelle de retour de la fonction. declare -a pile place() # Place un élément dans la pile. { if [ -z "$1" ] # Rien à y mettre ? then return fi let "SP -= 1" # Déplace le pointeur de pile. pile[$SP]=$1 return } recupere() # Récupère un élément de la pile. { Donnees= # Vide l'élément. if [ "$SP" -eq "$BP" ] # Pile vide ? then return fi # Ceci empêche aussi SP de dépasser 100, #+ donc de dépasser la capacité du tampon. Donnees=${pile[$SP]} let "SP += 1" # Déplace le pointeur de pile. return } rapport_d_etat() # Recherche ce qui se passe { echo "-------------------------------------" echo "REPORT" echo "Stack Pointer = $SP" echo "\""$Donnees"\" juste récupéré de la pile." echo "-------------------------------------" echo } # ======================================================= # Maintenant, amusons-nous... echo # Voyons si nous pouvons récupérer quelque chose d'une pile vide. recupere rapport_d_etat echo place garbage recupere rapport_d_etat # Garbage in, garbage out. valeur1=23; place $valeur1 valeur2=skidoo; place $valeur2 valeur3=FINAL; place $valeur3 recupere # FINAL rapport_d_etat recupere # skidoo rapport_d_etat recupere # 23 rapport_d_etat # dernier entré, premier sorti ! # Remarquez comment le pointeur de pile décrémente à chaque insertion et #+ incrémente à chaque récupération. echo # ======================================================= # Exercices: # --------- # 1) Modifier la fonction "place()" pour permettre le placement de plusieurs # + éléments sur la pile en un seul appel. # 2) Modifier la fonction "recupere()" pour récupérer plusieurs éléments de la # + pile en un seul appel de la fonction. # 3) En utilisant ce script comme base, écrire une calculatrice 4 fonctions # + basée sur une pile. exit 0

--

Des manipulations amusantes de tableaux pourraient nécessiter des variables intermédiaires. Pour des projets le nécessitant, considérez encore une fois l'utilisation d'un langage de programmation plus puissant comme Perl ou C.

Application complexe des tableaux Exploration d'une étrange série mathématique
#!/bin/bash # Les célèbres "Q-series" de Douglas Hofstadter: # Q(1) = Q(2) = 1 # Q(n) = Q(n - Q(n-1)) + Q(n - Q(n-2)), for n2 # C'est une série chaotique d'entiers avec un comportement étange et non #+ prévisible. # Les premiers 20 termes de la série sont: # 1 1 2 3 3 4 5 5 6 6 6 8 8 8 10 9 10 11 11 12 # Voir le livre d'Hofstadter, "Goedel, Escher, Bach: An Eternal Golden Braid", # p. 137, ff. LIMITE=100 # Nombre de termes à calculer LONGUEURLIGNE=20 # Nombre de termes à afficher par ligne. Q[1]=1 # Les deux premiers termes d'une série sont 1. Q[2]=1 echo echo "Q-series [$LIMITE termes]:" echo -n "${Q[1]} " # Affiche les deux premiers termes. echo -n "${Q[2]} " for ((n=3; n <= $LIMITE; n++)) # Conditions de boucle style C. do # Q[n] = Q[n - Q[n-1]] + Q[n - Q[n-2]] for n2 # Nécessaire de casser l'expression en des termes intermédiaires, # car Bash ne gère pas très bien l'arithmétique des tableaux complexes. let "n1 = $n - 1" # n-1 let "n2 = $n - 2" # n-2 t0=`expr $n - ${Q[n1]}` # n - Q[n-1] t1=`expr $n - ${Q[n2]}` # n - Q[n-2] T0=${Q[t0]} # Q[n - Q[n-1]] T1=${Q[t1]} # Q[n - Q[n-2]] Q[n]=`expr $T0 + $T1` # Q[n - Q[n-1]] + Q[n - ![n-2]] echo -n "${Q[n]} " if [ `expr $n % $LONGUEURLIGNE` -eq 0 ] # Formatte la sortie. then # mod echo # Retour chariot pour des ensembles plus jolis. fi done echo exit 0 # C'est une implémentation itérative de la Q-series. # L'implémentation récursive plus intuitive est laissée comme exercice. # Attention: calculer cette série récursivement prend *beaucoup* de temps.

--

Bash supporte uniquement les tableaux à une dimension, néanmoins une petite astuce permet de simuler des tableaux à plusieurs dimensions.

Simuler un tableau à deux dimensions, puis son test
#!/bin/bash # Simuler un tableau à deux dimensions. # Un tableau à deux dimensions stocke les lignes séquentiellement. Lignes=5 Colonnes=5 declare -a alpha # char alpha [Lignes] [Colonnes]; # Déclaration inutile. charge_alpha () { local rc=0 local index for i in A B C D E F G H I J K L M N O P Q R S T U V W X Y do local ligne=`expr $rc / $Colonnes` local colonne=`expr $rc % $Lignes` let "index = $ligne * $Lignes + $colonne" alpha[$index]=$i # alpha[$ligne][$colonne] let "rc += 1" done # Un peu plus simple # declare -a alpha=( A B C D E F G H I J K L M N O P Q R S T U V W X Y ) # mais il manque néanmoins le "bon goût" d'un tableau à deux dimensions. } affiche_alpha () { local ligne=0 local index echo while [ "$ligne" -lt "$Lignes" ] # Affiche dans l'ordre des lignes - do # les colonnes varient # tant que la ligne (la boucle externe) reste # identique local colonne=0 while [ "$colonne" -lt "$Colonnes" ] do let "index = $ligne * $Lignes + $colonne" echo -n "${alpha[index]} " # alpha[$ligne][$colonne] let "colonne += 1" done let "ligne += 1" echo done # Un équivalent plus simple serait # echo ${alpha[*]} | xargs -n $Colonnes echo } filtrer () # Filtrer les index négatifs du tableau. { echo -n " " if [[ "$1" -ge 0 && "$1" -lt "$Lignes" && "$2" -ge 0 && "$2" -lt "$Colonnes" ]] then let "index = $1 * $Lignes + $2" # Maintenant, l'affiche après rotation. echo -n " ${alpha[index]}" # alpha[$ligne][$colonne] fi } rotate () # Bascule le tableau de 45 degrés { # (le "balance" sur le côté gauche en bas). local ligne local colonne for (( ligne = Lignes; ligne > -Lignes; ligne-- )) # Traverse le tableau en # sens inverse. do for (( colonne = 0; colonne < Colonnes; colonne++ )) do if [ "$ligne" -ge 0 ] then let "t1 = $colonne - $ligne" let "t2 = $colonne" else let "t1 = $colonne" let "t2 = $colonne + $ligne" fi filtrer $t1 $t2 # Filtre les index négatifs du tableau. done echo; echo done # Rotation du tableau inspirée par les exemples (pp. 143-146) de # "Advanced C Programming on the IBM PC", par Herbert Mayer # (voir bibliographie). } #-----------------------------------------------------# charge_alpha # Charge le tableau. affiche_alpha # L'affiche. rotate # Le fait basculer sur 45 degrés dans le sens contraire des # aiguilles d'une montre. #-----------------------------------------------------# # C'est une simulation assez peu satisfaisante. # # Exercices: # --------- # 1) Réécrire le chargement du tableau et les fonctions d'affichage # + d'une façon plus intuitive et élégante. # # 2) Comprendre comment les fonctions de rotation fonctionnent. # Astuce: pensez aux implications de l'indexage arrière du tableau. exit 0

Un tableau à deux dimensions est essentiellement équivalent à un tableau à une seule dimension mais avec des modes d'adressage supplémentaires pour les références et les manipulations d'éléments individuels par la position de la "ligne" et de la "colonne" .

Pour un exemple encore plus élaboré de simulation d'un tableau à deux dimensions, voir life: Jeu de la Vie.


4.9. Fichiers


  • Ces fichiers contiennent les alias et variables d'environnement rendus accessibles au Bash exécuté en tant qu'utilisateur shell et à tous les scripts Bash appelés après l'initialisation du système.

  • /etc/profile
    défauts valables pour le système entier, configure essentiellement l'environnement (tous les shells de type Bourne, pas seulement Bash (55))

  • /etc/bashrc
    fonctions valables pour le système entier et alias pour Bash

  • $HOME/.bash_profile
    configuration de l'environnement par défaut spécifique à l'utilisateur, trouvée dans chaque répertoire personnel des utilisateurs (la contre-partie locale de /etc/profile)

  • $HOME/.bashrc
    fichier d'initialisation Bash spécifique à l'utilisateur, trouvé dans chaque répertoire personnel des utilisateurs (la contre-partie locale de /etc/bashrc). Seuls les shells interactifs et les scripts utilisateurs lisent ce fichier. Voir pour un fichier .bashrc d'exemple.

  • $HOME/.bash_logout
    fichier d'instructions spécifique à l'utilisateur, trouvé dans chaque répertoire personnel des utilisateurs. En sortie d'un shell login (Bash), les commandes de ce fichier sont exécutées.


4.10. /dev et /proc

Une machine Linux ou UNIX a typiquement deux répertoires ayant un but particulier, /dev et /proc.


/dev

Le répertoire /dev contient des entrées pour les périphériques physiques qui pourraient être présent sur votre système. (56) Les partitions du disque dur contenant les systèmes de fichiers montés ont des entrées dans /dev, comme un simple df le montre.

bash$ df Filesystem 1k-blocks Used Available Use% Mounted on /dev/hda6 495876 222748 247527 48% / /dev/hda1 50755 3887 44248 9% /boot /dev/hda8 367013 13262 334803 4% /home /dev/hda5 1714416 1123624 503704 70% /usr

Entre autre choses, le répertoire /dev contient aussi des périphériques loopback, tels que /dev/loop0. Un périphérique loopback est une astuce qui permet à un fichier ordinaire d'être accédé comme si il était un périphérique bloc. (57) Ceci rend possible le montage d'un système de fichier entier en un seul gros fichier. Voir Création d'un système de fichiers dans un fichier et Vérifier une image.

Quelques pseudo-périphériques dans /dev ont d'autres utilisations spécialisées, telles que /dev/null, /dev/zero et /dev/urandom.


/proc

Le répertoire /proc est en fait un pseudo système de fichiers. Les fichiers dans le répertoire /proc sont un miroir du système en cours d'exécution et des processus du noyau, et contiennent des informations et des statistiques sur elles.

bash$ cat /proc/devices Character devices: 1 mem 2 pty 3 ttyp 4 ttyS 5 cua 7 vcs 10 misc 14 sound 29 fb 36 netlink 128 ptm 136 pts 162 raw 254 pcmcia Block devices: 1 ramdisk 2 fd 3 ide0 9 md bash$ cat /proc/interrupts CPU0 0: 84505 XT-PIC timer 1: 3375 XT-PIC keyboard 2: 0 XT-PIC cascade 5: 1 XT-PIC soundblaster 8: 1 XT-PIC rtc 12: 4231 XT-PIC PS/2 Mouse 14: 109373 XT-PIC ide0 NMI: 0 ERR: 0 bash$ cat /proc/partitions major minor #blocks name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq 3 0 3007872 hda 4472 22260 114520 94240 3551 18703 50384 549710 0 111550 644030 3 1 52416 hda1 27 395 844 960 4 2 14 180 0 800 1140 3 2 1 hda2 0 0 0 0 0 0 0 0 0 0 0 3 4 165280 hda4 10 0 20 210 0 0 0 0 0 210 210 ... bash$ cat /proc/loadavg 0.13 0.42 0.27 2/44 1119

Des scripts shells peuvent extraire des données à partir de certains des fichiers de /proc. (58)

bash$ cat /proc/filesystems | grep iso9660 iso9660

Le répertoire /proc contient des sous-répertoires avec des noms numériques inhabituels. Chacun de ces noms correspond à un numéro de processus d'un processus en cours d'exécution. A l'intérieur de ces sous-répertoires, il existe un certain nombre de fichiers contenant des informations sur le processus correspondant. Les fichiers stat et status maintiennent des statistiques sur le processus, le fichier cmdline contient les arguments de la ligne de commande avec lesquels le processus a été appelé et le fichier exe est un lien symbolique vers le chemin complet du processus. Il existe encore quelques autres fichiers, mais ceux-ci sont les plus intéressants du point de vue de l'écriture de scripts.

Trouver le processus associé à un PID
#!/bin/bash # pid-identifier.sh: Donne le chemin complet du processus associé avec ce pid. NBARGS=1 # Nombre d'arguments que le script attend. E_MAUVAISARGS=65 E_MAUVAISPID=66 E_PROCESSUS_INEXISTANT=67 E_SANSDROIT=68 PROCFILE=exe if [ $# -ne $NBARGS ] then echo "Usage: `basename $0` PID" >&2 # Message d'erreur >stderr. exit $E_MAUVAISARGS fi nopid=$( ps ax | grep $1 | awk '{ print $1 }' | grep $1 ) # Cherche le pid dans l'affichage de "ps", car il est le champ #1. # S'assure aussi qu'il s'agit du bon processus, et non pas du processus appelé # par ce script. # Le dernier "grep $1" supprime cette possibilité. if [ -z "$nopid" ] # Si, après tous ces filtres, le résultat est une chaîne vide then # aucun processus en cours ne correspond au pid donné. echo "Aucun processus en cours." exit $E_PROCESSUS_INEXISTANT fi # Autrement: # if ! ps $1 > /dev/null 2>&1 # then # Aucun processus ne correspond au pid donné. # echo "Ce processus n'existe pas" # exit $E_PROCESSUS_INEXISTANT # fi # Pour simplifier tout cet algorithme, utilisez "pidof". if [ ! -r "/proc/$1/$PROCFILE" ] # Vérifiez les droits en lecture. then echo "Processus $1 en cours, mais..." echo "Ne peut obtenir le droit de lecture sur /proc/$1/$PROCFILE." exit $E_SANSDROIT # Un utilisateur standard ne peut accéder à certains fichiers de /proc. fi # Les deux derniers tests peuvent être remplacés par: # if ! kill -0 $1 > /dev/null 2>&1 # '0' n'est pas un signal, mais # ceci testera s'il est possible # d'envoyer un signal au processus. # then echo "PID n'existe pas ou vous n'êtes pas son propriétaire" >&2 # exit $E_MAUVAISPID # fi fichier_exe=$( ls -l /proc/$1 | grep "exe" | awk '{ print $11 }' ) # Ou fichier_exe=$( ls -l /proc/$1/exe | awk '{print $11}' ) # # /proc/pid-number/exe est un lien symbolique # vers le chemin complet du processus appelé. if [ -e "$fichier_exe" ] # Si /proc/pid-number/exe existe... then # le processus correspondant existe. echo "Processus #$1 appelé par $fichier_exe" else echo "Processus inexistant" fi # Ce script élaboré peut *pratiquement* être remplacé par # ps ax | grep $1 | awk '{ print $5 }' # Néanmoins, cela ne fonctionnera pas... # parce que le cinquième champ de 'ps' est le argv[0] du processus, # et non pas le chemin vers l'exécutable. # # Néanmoins, une des deux méthodes suivantes devrait fonctionner. # find /proc/$1/exe -printf '%l\n' # lsof -aFn -p $1 -d txt | sed -ne 's/^n//p' # Commentaires supplémentaires par Stephane Chazelas. exit 0
Etat de la connexion
#!/bin/bash NOMPROC=pppd # démon ppp. NOMFICHIERPROC=status # Où chercher. NONCONNECTE=65 INTERVALLE=2 # Mise à jour toutes les 2 secondes. nopid=$( ps ax | grep -v "ps ax" | grep -v grep | grep $NOMPROC | awk '{ print $1 }' ) # Trouver le numéro de processus de 'pppd', le 'démon ppp'. # Doit filtrer les lignes de processus générées par la recherche elle-même. # # Néanmoins, comme Oleg Philon l'a indiqué, #+ ceci pourrait être considérablement simplifié en utilisant "pidof". # nopid=$( pidof $NOMPROC ) # # Morale de l'histoire: #+ Quand une séquence de commandes devient trop complexe, cherchez un raccourci. if [ -z "$pidno" ] # Si pas de pid, alors le processus ne tourne pas. then echo "Non connecté." exit $NONCONNECTE else echo "Connecté."; echo fi while [ true ] # Boucle sans fin, le script peut être amélioré ici. do if [ ! -e "/proc/$pidno/$NOMFICHIERPROC" ] # Quand le processus est en cours d'exécution, alors le fichier "status" #+ existe. then echo "Déconnecté." exit $NONCONNECTE fi netstat -s | grep "packets received" # Obtenir quelques statistiques de netstat -s | grep "packets delivered" #+ connexion. sleep $INTERVALLE echo; echo done exit 0 # De cette façon, le script ne se termine qu'avec un Control-C. # Exercices: # --------- # Améliorer le script pour qu'il se termine suite à l'appui sur la touche # "q". # Rendre le script plus facilement utilisable d'autres façons.

En général, il est dangereux d'écrire dans les fichiers de /proc, car cela peut corrompre le système de fichiers ou provoquer une erreur fatale.


4.11. Des Zéros et des Nulls

  • Utilisation de /dev/null
    Vous pouvez considérer /dev/null comme un "trou noir" . L'équivalent le plus proche serait un fichier en écriture seulement. Tout ce qui y est écrit disparaît à jamais. Toute tentative de lecture n'aboutira à rien. Néanmoins, /dev/null peut être très utile à la fois en ligne de commande et dans certains scripts. Supprimer stdout. Supprimer stderr (provenant de incorrectname élimine dans le répertoire courant les fichiers dont le nom contient des caractères incorrects et des espaces blancs.). Supprimer les sortiesà la fois de stdout et stderr. Supprimer le contenu d'un fichier, mais en conservant le fichier lui-même, avec ses permissions (provenant de cleanup: Un script pour nettoyer les fichiers de trace dans /var/log et cleanup: Une version améliorée et généralisée du script précédent.): Vider automatiquement le contenu d'un fichier de traces (spécialement intéressant pour s'occuper de ces fichiers dégoutants que sont les "cookies" envoyés par les sites Web commerciaux):
  • Cacher le cookie jar
    if [ -f ~/.netscape/cookies ] # Remove, if exists. then rm -f ~/.netscape/cookies fi ln -s /dev/null ~/.netscape/cookies # Maintenant, tous les cookies se trouvent envoyés dans un trou noir, plutôt que # d'être sauvé sur disque.


  • Utilisation de /dev/zero
    Comme /dev/null, /dev/zero est un pseudo fichier, mais il contient réellement des caractères zéros (des zéros numériques, pas du type ASCII). Toute écriture dans ce fichier disparait, et il est plutôt difficile de lire les zéros dans /dev/zero, bien que ceci puisse se faire avec od ou un éditeur héxadécimal. L'utilisation principale de /dev/zero est de créer un fichier factice initialisé à une taille spécifiée, pour en faire un fichier de swap temporaire.
  • Créer un fichier de swap en utilisant /dev/zero
    #!/bin/bash # Crée un fichier de swap. # Ce script doit être exécuté en tant que root. ROOT_UID=0 # Root a l'$UID 0. E_MAUVAIS_UTILISATEUR=65 # Not root? FICHIER=/swap TAILLEBLOC=1024 BLOCSMINS=40 SUCCES=0 if [ "$UID" -ne "$ROOT_UID" ] then echo; echo "Vous devez être root pour exécuter ce script."; echo exit $E_MAUVAIS_UTILISATEUR fi blocs=${1:-$BLOCSMINS} # Par défaut à 40 blocs, si rien n'est #+ spécifié sur la ligne de commande. # Ceci est l'équivalent du bloc de commande ci-dessous. # -------------------------------------------------- # if [ -n "$1" ] # then # blocs=$1 # else # blocs=$BLOCSMINS # fi # -------------------------------------------------- if [ "$blocs" -lt $BLOCSMINS ] then blocs=$BLOCSMINS # Doit être au moins long de 40 blocs. fi echo "Création du fichier swap d'une taille de $blocs blocs (Ko)." dd if=/dev/zero of=$FICHIER bs=$TAILLEBLOC count=$blocs # Vide le fichier. mkswap $FICHIER $blocs # Indique son type: swap. swapon $FICHIER # Active le fichier swap. echo "Fichier swap créé et activé." exit $SUCCES
  • Une autre application de /dev/zero est de "remplir de zéros" un fichier d' une taille spécifiée pour une raison particulière, telle que monter un système de fichiers sur un périphérique loopback (voir Création d'un système de fichiers dans un fichier) ou telle que la suppression sécurisée d'un fichier (voir Effacer les fichiers de façon sure).
  • Créer un disque ram
    #!/bin/bash # ramdisk.sh # Un disque ram ("ramdisk") est un segment de mémoire RAM système agissant #+ comme un système de fichiers. # Son avantage est son accès très rapide (temps de lecture/écriture). # Inconvénients: volatile, perte de données au redémarrage ou à l'arrêt. # moins de RAM disponible pour le système. # # En quoi un disque ram est intéressant? # Conserver un ensemble de données large, comme une table ou un dictionnaire, #+ sur un disque ram, accélère les recherches de données car l'accès mémoire est #+ bien plus rapide que l'accès disque. E_UTILISATEUR_NON_ROOT=70 # Doit être root. NOM_UTILISATEUR_ROOT=root POINT_MONTAGE=/mnt/ramdisk TAILLE=2000 # 2000 blocs (modifiez comme vous l'entendez) TAILLE_BLOC=1024 # 1K (1024 octets) en taille de bloc PERIPH=/dev/ram0 # Premier périphérique ram nom_utilisateur=`id -nu` if [ "$nom_utilisateur" != "$NOM_UTILISATEUR_ROOT" ] then echo "Vous devez être root pour exécuter \"`basename $0`\"." exit $E_UTILISATEUR_NON_ROOT fi if [ ! -d "$POINT_MONTAGE" ] # Teste si le point de montage est déjà créé, then #+ pour qu'il n'y ait pas d'erreur après mkdir $POINT_MONTAGE #+ plusieurs exécutions de ce script fi dd if=/dev/zero of=$PERIPH count=$TAILLE bs=$TAILLE_BLOC # Vide le périphérique #+ ram mke2fs $PERIPH # Crée un système de fichiers ext2 dessus. mount $PERIPH $POINT_MONTAGE # Monte le périphérique. chmod 777 $POINT_MONTAGE # Pour que les utilisateurs standards puissent y #+ accéder. # Néanmoins, seul root pourra le démonter. echo "\"$POINT_MONTAGE\" est maintenant disponible" # Le disque ram est maintenant accessible pour stocker des fichiers, y compris # par un utilisateur standard. # Attention, le disque ram est volatile et son contenu disparaîtra au prochain #+ redémarrage ou au prochain arrêt. # Copiez tout ce que vous voulez sauvegarder dans un répertoire standard. # Après redémarrage, lancez de nouveau ce script pour initialiser le disque ram. # Remonter /mnt/ramdisk sans les autres étapes ne fonctionnera pas. exit 0



4.12. Déboguage

Le shell Bash ne contient ni débogueur ni même de commandes ou d'instructions spécifiques pour le déboguage. (59) Les erreurs de syntaxe ou de frappe dans les scripts génèrent des messages d'erreur incompréhensibles n'apportant souvent aucune aide pour déboguer un script non fonctionnel.

Un script bugué
#!/bin/bash # ex74.sh # C'est un script buggué. a=37 if [$a -gt 27 ] then echo $a fi exit 0

Sortie d'un script:

./ex74.sh: [37: command not found

Que se passe-t'il avec ce script (petite aide: après le if)?

Mot clé manquant
#!/bin/bash # missing-keyword.sh: Quel message d'erreur sera généré? for a in 1 2 3 do echo "$a" # done # Requiert le mot clé 'done' mis en commentaire ligne 7. exit 0

Sortie d'un script:

missing-keyword.sh: line 10: syntax error: unexpected end of file

Notez que le message d'erreur ne fait pas nécessairement référence à la ligne où l'erreur se trouve mais à la ligne où l'interpréteur Bash s'aperçoit de l'erreur.

Les messages d'erreur peuvent ne pas tenir compte des lignes de commentaires d'un script lors de l'affichage du numéro de ligne de l'instruction ayant provoqué une erreur de syntaxe.

Que faire si le script s'exécute mais ne fonctionne pas comme vous vous y attendiez? C'est une erreur de logique trop commune.

test24, un autre script bogué
#!/bin/bash # Ceci est supposé supprimer tous les fichiers du répertoire courant contenant #+ des espaces dans le nom. # Cela ne fonctionne pas. Pourquoi? mauvaisnom=`ls | grep ' '` # echo "$mauvaisnom" rm "$mauvaisnom" exit 0

Essayez de trouver ce qui ne va pas avec test24, un autre script bogué en supprimant les caractères de commentaires de la ligne echo "$badname". Les instructions echo sont utiles pour voir si ce que vous attendiez est bien ce que vous obtenez.

Dans ce cas particulier,

rm "$badname"

ne donnera pas les résultats attendus parce que $badname ne devrait pas être entre guillemets. Le placer entre guillemets nous assure que rm n'a qu'un seul argument (il correspondra à un seul nom de fichier). Une correction partielle est de supprimer les guillemets de $badname et de réinitialiser $IFS pour contenir seulement un retour à la ligne,

IFS=$'\n'

. Néanmoins, il existe des façons plus simples de faire cela.

Résumer les symptômes d'un script bogué, Il quitte brutalement avec un message d'erreur de syntaxe ( "syntax error" ), ou Il se lance bien, mais ne fonctionne pas de la façon attendue (erreur logique, logic error). Il fonctionne comme vous vous y attendiez, mais a de déplaisants effets indésirables (logic bomb).

Il existe des outils pour déboguer des scripts non fonctionnels des instructions echo aux points critiques du script pour tracer les variables, ou pour donner un état de ce qui se passe. utiliser le filtre tee pour surveiller les processus ou les données aux points critiques. initialiser des paramètres optionnelles -n -v -xsh -n nomscript vérifie les erreurs de syntaxe sans réellement exécuter le script. C'est l'équivalent de l'insertion de set -n ou set -o noexec dans le script. Notez que certains types d'erreurs de syntaxe peuvent passer à côté de cette vérification.sh -v nomscript affiche chaque commande avant de l'exécuter. C'est l'équivalent de l'insertion de set -v ou set -o verbose dans le script.Les options -n et -v fonctionnent bien ensemble. sh -nv nomscript permet une vérification verbeuse de la syntaxe.sh -x nomscript affiche le résultat de chaque commande, mais d'une façon abrégée. C'est l'équivalent de l'insertion de set -x ou set -o xtrace dans le script.Insérer set -u ou set -o nounset dans le script le lance, mais donne un message d'erreur unbound variable à chaque essai d'utilisation d'une variable non déclarée. Utiliser une fonction "assert" pour tester une variable ou une condition aux points critiques d'un script. (Cette idée est empruntée du C.) #!/bin/bash # assert.sh assert () # Si la condition est fausse, { #+ sort du script avec un message d'erreur. E_PARAM_ERR=98 E_ASSERT_FAILED=99 if [ -z "$2" ] # Pas assez de paramètres passés. then return $E_PARAM_ERR # Pas de dommages. fi noligne=$2 if [ ! $1 ] then echo "Mauvaise assertion: \"$1\"" echo "Fichier \"$0\", ligne $noligne" exit $E_ASSERT_FAILED # else (sinon) # return (retour) # et continue l'exécution du script. fi } a=5 b=4 condition="$a -lt $b" # Message d'erreur et sortie du script. # Essayer de configurer la "condition" en autre chose #+ et voir ce qui se passe. assert "$condition" $LINENO # Le reste du script s'exécute si assert n'échoue pas. # Quelques commandes. # ... echo "Cette instruction s'exécute seulement si \"assert\" n'échoue pas." # ... # Quelques commandes de plus. exit 0 piéger la sortie.La commande exit d'un script déclenche un signal 0, terminant le processus, c'est-à-dire le script lui-même. (60) Il est souvent utilisé pour récupérer la main lors de exit, en forçant un "affichage" des variables, par exemple. Le trap doit être la première commande du script.

  • trap
    Spécifie une action à la réception d'un signal; aussi utile pour le déboguage. Remarque:Un signal est un simple message envoyé au processus, soit par le noyau soit par un autre processus lui disant de réaliser quelque action spécifiée (habituellement pour finir son exécution). Par exemple, appuyer sur ControlC, envoit une interruption utilisateur, un signal INT, au programme en cours d'exécution.

Récupérer la sortie
#!/bin/bash trap 'echo Liste de Variables --- a = $a b = $b' EXIT # EXIT est le nom du signal généré en sortie d'un script. a=39 b=36 exit 0 # Notez que mettre en commentaire la commande 'exit' ne fait aucune différence, # car le script sort dans tous les cas après avoir exécuté les commandes.
Nettoyage après un Control-C
#!/bin/bash # logon.sh: Un script rapide mais sale pour vérifier si vous êtes déjà connecté. VRAI=1 JOURNAL=/var/log/messages # Notez que $JOURNAL doit être lisible (chmod 644 /var/log/messages). FICHIER_TEMPORAIRE=temp.$$ # Crée un fichier temporaire "unique", en utilisant l'identifiant du processus. MOTCLE=adresse # A la connexion, la ligne "remote IP address xxx.xxx.xxx.xxx" # ajoutée à /var/log/messages. ENLIGNE=22 INTERRUPTION_UTILISATEUR=13 VERIFIE_LIGNES=100 # Nombre de lignes à vérifier dans le journal. trap 'rm -f $FICHIER_TEMPORAIRE; exit $INTERRUPTION_UTILISATEUR' TERM INT # Nettoie le fichier temporaire si le script est interrompu avec control-c. echo while [ $VRAI ] # Boucle sans fin. do tail -$VERIFIE_LIGNES $JOURNAL> $FICHIER_TEMPORAIRE # Sauve les 100 dernières lignes du journal dans un fichier temporaire. # Nécessaire, car les nouveaux noyaux génèrent beaucoup de messages lors de la # connexion. search=`grep $MOTCLE $FICHIER_TEMPORAIRE` # Vérifie la présence de la phrase "IP address", # indiquant une connexion réussie. if [ ! -z "$search" ] # Guillemets nécessaires à cause des espaces possibles. then echo "En ligne" rm -f $FICHIER_TEMPORAIRE # Suppression du fichier temporaire. exit $ENLIGNE else echo -n "." # l'option -n supprime les retours à la ligne de echo, # de façon à obtenir des lignes de points continues. fi sleep 1 done # Note: Si vous modifiez la variable MOTCLE par "Exit", # ce script peut être utilisé lors de la connexion pour vérifier une déconnexion # inattendue. # Exercice: Modifiez le script, suivant la note ci-dessus, et embellissez-le. exit 0 # Nick Drage suggère une autre méthode. while true do ifconfig ppp0 | grep UP 1> /dev/null && echo "connecté" && exit 0 echo -n "." # Affiche des points (.....) jusqu'au moment de la connexion. sleep 2 done # Problème: Appuyer sur Control-C pour terminer ce processus peut être # insuffisant (des points pourraient toujours être affichés). # Exercice: Corrigez ceci. # Stephane Chazelas a lui-aussi suggéré une autre méthode. CHECK_INTERVAL=1 while ! tail -1 "$JOURNAL" | grep -q "$MOTCLE" do echo -n . sleep $CHECK_INTERVAL done echo "On-line" # Exercice: Discutez les avantages et inconvénients de chacune des méthodes.

Remarque : L'argument DEBUG pour trap exécute une action spécifique après chaque commande dans un script. Cela permet de tracer les variables, par exemple. #!/bin/bash trap 'echo "VARIABLE-TRACE> \$variable = \"$variable\""' DEBUG # Affiche la valeur de $variable après chaque commande. variable=29 echo "Initialisation de \"\$variable\" à $variable." let "variable *= 3" echo "Multiplication de \"\$variable\" par 3." # La construction "trap 'commandes' DEBUG" serait plus utile dans le contexte #+ d'un script complexe, où placer de nombreuses instructions #+ "echo $variable" serait difficile et long. # Merci, Stephane Chazelas pour cette information. exit 0

Remarque :

trap '' SIGNAL

(deux apostrophes adjacentes) désactive SIGNAL pour le reste du script.

trap SIGNAL

restaure la fonctionnalité de SIGNAL. C'est utile pour protéger une portion critique d'un script d'une interruption indésirable.


4.13. Options

Les options sont des paramètrages modifiant le comportement du shell et/ou du script.

La commande set active les options dans un script. Là où vous voulez que les options soient effectives dans le script, utilisez set -o nom-option ou, plus court, set -abreviation-option. Ces deux formes sont équivalentes.

Remarque : Pour désactiver une option dans un script, utilisez set +o nom-option ou set +abreviation-option.

Une autre méthode d'activation des options dans un script est de les spécifier tout de suite après l'entête #! du script.

Il est aussi possible d'activer les options du script à partir de la ligne de commane. Certaines options qui ne fonctionneront pas avec set sont disponibles de cette façon. Parmi celles-ci se trouve -i, forçant un script à se lancer de manière interactive.

bash -v nom-script

bash -o verbose nom-script

Ce qui suit est une liste de quelques options utiles. Elles sont spécifiées soit dans leur forme abrégée soit par leur nom complet.

Abréviation Nom Effet
noclobber Empêche l'écrasement de fichiers par une redirection (peut être outrepassé par )
(aucune) Affiche les chaînes entre guillemets doubles préfixées par un , mais n'exécute par les commandes du script.
allexport Exporte toutes les variables définies
notify Notifie lorsque un travail en tâche de fond se termine (pas d'une grande utilité dans un script)
(aucune) Lit les commandes à partir de
noglob Expansion des noms de fichier désactivée
interactive Script lancé dans un mode
privileged Script lancé avec "" (attention!)
restricted Script lancé en mode (voir ).
nounset Essayer d'utiliser une variable non définie affiche un message d'erreur et force l'arrêt du script
verbose Affiche chaque commande sur avant des les exécuter
xtrace Similaire à , mais étend les commandes
errexit Arrête le script à la première erreur (lorsque la commande s'arrête avec un code différent de zéro)
noexec Lit les commandes dans un script, mais ne les exécute pas (vérification de la syntaxe)
stdin Lit les commandes à partir de
(aucune) Quitte après la première commande
(aucune) Fin des options. Tous les autres arguments sont des .
(aucune) Désinitialise les paramètres de position. Si des arguments sont donnés (-- arg1 arg2), les paramètres de position sont initialisés avec ces arguments.

4.14. Trucs et astuces

Turandot: Gli enigmi sono tre, la morte una!

Caleph: No, no! Gli enigmi sono tre, una la vita!

Affecter des mots réservés à des noms de variables.

Utiliser un tiret ou d'autres caractères réservés dans un nom de variable.

Utiliser le même nom pour une variable et une fonction. Ceci rend le script difficile à comprendre.

Utiliser des espaces blancs inappropriés (en contraste avec d'autres langages de programmation, Bash peut être assez chatouilleux avec les espaces blancs).

Supposer que des variables non initialisées (variables avant qu'une valeur leur soit affectée) sont "remplies de zéros" . Une variable non initialisée a une valeur "null" , et non pas zéro.

Mixer = et -eq dans un test. Rappelez-vous, = permet la comparaison de variables littérales et -eq d'entiers.

Mélanger les opérateurs de comparaison d'entiers et de chaînes.

Quelque fois, des variables à l'intérieur des crochets de "test" ([ ]) ont besoin d'être mis entre guillemets (doubles). Ne pas le faire risque de causer un comportement inattendu. Voir vérification si une chaîne nulle, Boucle while redirigée et arglist: Affichage des arguments avec $* et $@.

Les commandes lancées à partir d'un script peuvent échouer parce que le propriétaire d'un script ne possède pas les droits d'exécution. Si un utilisateur ne peut exécuter une commande à partir de la ligne de commande, alors la placer dans un script échouera de la même façon. Essayer de changer les droits de la commande en question, peut-être même en initialisant le bit suid (en tant que root, bien sûr).

Essayer d'utiliser - comme opérateur de redirection (qu'il n'est pas) résultera habituellement en une surprise peu plaisante.

Utiliser les fonctionnalités de Bash version 2+ peut poser des soucis avec les messages d'erreur. Les anciennes machines Linux peuvent avoir une version 1.XX de Bash en installation par défaut.

Utiliser les fonctionnalités spécifiques à Bash dans un script shell Bourne (

#!/bin/sh

) sur une machine non Linux peut causer un comportement inattendu. Un système Linux crée habituellement un alias sh vers bash, mais ceci n'est pas nécessairement vrai pour une machine UNIX générique.

Un script avec des retours à la ligne DOS (\r\n) ne pourra pas s'exécuter car

#!/bin/bash\r\n

n'est pas reconnu, pas la même chose que l'attendu

#!/bin/bash\n

. La correction est de convertir le script en des retours chariots style UNIX.

Un script shell commenaçant par

#!/bin/sh

peut ne pas se lancer dans un mode de compatibilité complète avec Bash. Quelques fonctions spécifiques à Bash pourraient être désactivées. Les scripts qui ont besoin d'un accès complet à toutes les extensions spécifiques à Bash devraient se lancer avec

#!/bin/bash

.

Un script peut ne pas faire un export de ses variables à son processus parent, le shell ou à l'environnement. Comme nous l'avons appris en biologie, un processus fils peut hériter de son parent, mais le contraire n'est pas vrai.

bash$ echo $NIMPORTEQUOI bash$

De façon certaine, au retour à l'invite commande, $NIMPORTEQUOI reste sans valeur.

Initialiser et manipuler des variables dans un sous-shell, puis essayer d'utiliser ces mêmes variables en dehors du sous-shell résultera en une mauvaise surprise.

Problèmes des sous-shell
#!/bin/bash # Problèmes des variables dans un sous-shell. variable_externe=outer echo echo "variable_externe = $variable_externe" echo ( # Début du sous-shell echo "variable_externe à l'intérieur du sous-shell = $variable_externe" variable_interne=interne # Initialise echo "variable_interne à l'intérieur du sous-shell = $variable_interne" variable_externe=interne # Sa valeur va-t'elle changer globalement? echo "variable_externe à l'intérieur du sous-shell = $variable_externe" # Fin du sous-shell ) echo echo "variable_interne à l'extérieur du sous-shell = $variable_interne" # Déinitialisée. echo "variable_externe à l'extérieur du sous-shell = $variable_externe" # Non modifiée. echo exit 0

Envoyer dans un tube la sortie de echo pour un read peut produire des résultats inattendus. Dans ce scénario, read agit comme si elle était lancée dans un sous-shell. A la place, utilisez la commande set (comme dans Réaffecter les paramètres de position).

Envoyer dans un tube la sortie de echo pour un read
#!/bin/bash # badread.sh: # Essai d'utiliser 'echo' et 'read' #+ pour affecter non interactivement des variables. a=aaa b=bbb c=ccc echo "un deux trois" | read a b c # Essaie d'affecter a, b et c. echo echo "a = $a" # a = aaa echo "b = $b" # b = bbb echo "c = $c" # c = ccc # L'affectation a échoué. # ------------------------------ # Essaie l'alternative suivante. var=`echo "un deux trois"` set -- $var a=$1; b=$2; c=$3 echo "-------" echo "a = $a" # a = un echo "b = $b" # b = deux echo "c = $c" # c = trois # Affectation réussie. # ------------------------------ # Notez aussi qu'un echo pour un 'read' fonctionne à l'intérieur d'un #+ sous-shell. # Néanmoins, la valeur de la variable change *seulement* à l'intérieur du #+ sous-shell. a=aaa # On recommence. b=bbb c=ccc echo; echo echo "un deux trois" | ( read a b c; echo "A l'intérieur du sous-shell: "; echo "a = $a"; echo "b = $b"; echo "c = $c" ) # a = un # b = deux # c = trois echo "-----------------" echo "A l'extérieur du sous-shell: " echo "a = $a" # a = aaa echo "b = $b" # b = bbb echo "c = $c" # c = ccc echo exit 0

Utiliser les commandes "suid" à l'intérieur de scripts est risqué et peut compromettre la sécurité de votre système. (61)

Utiliser des scripts shell en programmation CGI peut être assez problématique. Les variables des scripts shell ne sont pas "sûres" , et ceci peut causer un comportement indésirable en ce qui concerne CGI. De plus, il est difficile de "sécuriser" des scripts shell.

Les scripts Bash écrits pour Linux ou BSD peuvent nécessiter des corrections pour fonctionner sur une machine UNIX commerciale. De tels scripts emploient souvent des commandes et des filtres GNU qui ont plus de fonctionnalités que leur contrepartie UNIX. Ceci est particulièrement vrai pour les utilitaires texte comme tr.

Danger is near thee --

Beware, beware, beware, beware.

Many brave hearts are asleep in the deep.

So beware --

Beware.


4.15. Ecrire des scripts avec style

Prenez l'habitude d'écrire vos scripts shell d'une façon structurée et méthodique. Même des scripts écrits "sur le dos d'une enveloppe" et "sans trop réfléchir" peuvent en bénéficier si vous prenez le temps de plannifier et organiser vos pensées avant de vous assoir pour l'écrire.

Du coup, il existe quelques lignes de conduites pour le style. Ceci n'a pas pour but d'être la feuille de style officielle pour l'écriture de scripts.


Feuille de style non officielle d'écriture de scripts

  • Commentez votre code. Cela le rend plus facile à comprendre (et apprécier) par les autres, et plus facile pour vous à maintenir. Ajoutez des entêtes descriptives à votre script et à vos fonctions. Assurez-vous de mettre le #!/bin/bash au début de la première ligne d'un script, précédant tout entête de commentaires.
  • Eviter d'utiliser des "nombres magiques" , (62) c'est-à-dire, des constantes littérales "codées en dur" . Utilisez des noms de variables significatifs à la place. Ceci rend le script plus facile à comprendre et permet de faire des changements et des mises à jour sans casser l'application.
  • Choisissez des noms descriptifs pour les variables et les fonctions.
  • Utiliser des codes de sortie d'une façon systématique et significative. Voir aussi .
  • Casser les scripts complexes en modules plus simples. Utiliser des fonctions si c'est approprié. Voir Utiliser des tableaux et autres astuces pour gérer quatre mains aléatoires dans un jeu de cartes.
  • N'utilisez pas une construction complexe lorsqu'une plus simple fait l'affaire.

... reading the UNIX source code to the Bourne shell (/bin/sh). I was shocked at how much simple algorithms could be made cryptic, and therefore useless, by a poor choice of code style. I asked myself, "Could someone be proud of this code?"

... lisant le code source UNIX du shell Bourne (/bin/sh). J'ai été choqué de voir à quel point de simples algorithmes pouvaient être rendus incompréhensibles, et du coup inutiles, par un mauvais choix dans le style de codage. Je me suis demandé, "Quelqu'un peut-il être fier de ce code?"


4.16. Divers

Personne ne connait réellement ce qu'est la grammaire du shell Bourne. Même l'examination du code source est de peu d'aide.


Shells et scripts interactifs et non interactifs

Un shell interactif lit les commandes à partir de l'entrée utilisateur sur un terminal tty. Entre autres choses, un tel script lit les fichiers de démarrage lors de l'activation, affiche une invite et active un contrôle de job par défaut. L'utilisateur peut interagir avec le shell.

Un shell exécutant un script est toujours un shell non interactif. Tout de même, le script peut toujours accéder au tty. Il est même possible d'émuler un shell interactif dans un script.

Considérons un script interactif qui demande une entrée par l'utilisateur, habituellement avec des fonctions read (voir Affectation d'une variable, en utilisant read). Dans la "vraie vie" , c'est en fait un peu moins simple que ça. A partir de maintenant, on supposera qu'un script interactif est lié à un terminal tty, script appelé par un utilisateur à partir d'une console ou d'une xterm.

Des scripts d'initialisation et de démarrage sont nécessairement non interactifs car ils doivent fonctionner sans intervention humaine. Beaucoup de scripts administratifs et de maintenance système sont aussi non interactifs. Les tâches répétitives invariables nécessitent une automatisation par des scripts non interactifs.

Les scripts non interactifs peuvent fonctionner en arrière-plan, alors que les interactifs sont suspendus, attendant une entrée qui ne viendra jamais. Gérez cette difficulté en utilisant un script expect ou une entrée intégrée de type here document vers un script interactif fonctionnant comme une tâche de fond. Dans le cas le plus simple, redirigez un fichier pour apporter l'entrée à la fonction read (read variable file). Ces détournements particuliers rendent possible l'utilisation de scripts à usage général tournant en mode soit interactif soit non interactif.

Si un script a besoin de tester si oui ou non il est lancé de manière interactive, il suffit simplement de savoir si la variable de l'invite, $PS1 est configurée. (Si le script attend une entrée de l'utilisateur, alors il a besoin d'afficher une invite). Comme alternative, le script peut tester la présence de l'option "i" dans le drapeau $-.

Remarque : Il est possible de forcer un script à fonctionner en mode interactif avec l'option -i ou avec l'entête

#!/bin/bash -i

. Faites attention au fait que ceci peut entraîner un comportement étrange du script ou afficher des messages d'erreurs même si aucune erreur n'est présente.


Scripts d'invocation

Un "script d'invocation" (wrapper) est un script shell qui inclut une commande système ou un utilitaire, qui sauvegarde un ensemble de paramètres passés à cette commande. Intégrer un script dans une ligne de commande complexe simplifie son invocation. Ceci est spécialement utile avec sed et awk.

Un script sed ou awk serait normalement invoqué à partir de la ligne de commande par un

sed -e 'commands'

ou

awk 'commands'

. Intégrer un tel script dans un script Bash permet de l'appeler plus simplement et le rend "réutilisable" . Ceci autorise aussi la combinaison des fonctionnalités de sed et awk, par exemple pour renvoyer dans un tuyau la sortie d'un ensemble de commandes sed vers awk. Comme un fichier exécutable sauvé, vous pouvez alors l'invoqué de manière répétée dans sa forme originale ou modifiée, sans les inconvénients d'avoir à le retaper sur la ligne de commande.

script d'invocation
#!/bin/bash # C'est un simple script supprimant les lignes blanches d'un fichier. # Pas de vérification des arguments. # # Vous pouvez souhaiter ajouter quelque chose comme ça: # if [ -z "$1" ] # then # echo "Usage: `basename $0` fichier-cible" # exit 65 # fi # Identique à # sed -e '/^$/d' nomfichier # appelé à partir de la ligne de commande. sed -e /^$/d "$1" # Le '-e' signifie qu'une commande d'"édition" suit (optionnel ici). # '^' est le début de la ligne, '$' en est la fin. # Ceci correspond aux lignes n'ayant rien entre le début et la fin de la ligne. # 'd' est la commande de suppression. # Mettre entre guillemets l'argument de la ligne de commande permet de saisie #+ des espaces blancs et des caractères spéciaux dans le nom du fichier. exit 0
Un script d'invocation légèrement plus complexe
#!/bin/bash # "subst", un script qui substitue un modèle pour un autre dans un fichier, # c'est-à-dire "subst Smith Jones letter.txt". ARGS=3 E_MAUVAISARGS=65 # Mauvais nombre d'arguments passés au script. if [ $# -ne "$ARGS" ] # Teste le nombre d'arguments du script (toujours une bonne idée). then echo "Usage: `basename $0` ancien-modele nouveau-modele nomfichier" exit $E_MAUVAISARGS fi ancien_modele=$1 nouveau_modele=$2 if [ -f "$3" ] then nom_fichier=$3 else echo "Le fichier \"$3\" n'existe pas." exit $E_MAUVAISARGS fi # Voici où se trouve le vrai boulot. sed -e "s/$ancien_modele/$nouveau_modele/g" $file_name # 's' est, bien sûr, la commande de substitution dans sed, # et /modele/ appelle la correspondance d'adresse. # The "g", ou global flag entraîne la substitution pour *chaque* # occurence de $ancien_modele à chaque ligne, pas seulement la première. # Lisez la documentation de 'sed' pour une explication plus détaillée. exit 0 # Appel avec succès du script qui renvoie 0.
Un script d'invocation autour d'un script awk
#!/bin/bash # Ajoue une colonne spécifiée (de nombres) dans le fichier cible. ARGS=2 E_MAUVAISARGS=65 if [ $# -ne "$ARGS" ] # Vérifie le bon nombre d'arguments sur la ligne de # de commandes. then echo "Usage: `basename $0` nomfichier numéro_colonne" exit $E_MAUVAISARGS fi nomfichier=$1 numero_colonne=$2 # Passer des variables shell à la partie awk du script demande un peu d'astuces. # Voir la documentation awk pour plus de détails. # Un script multi-ligne awk est appelé par awk ' ..... ' # Début du script awk. # ----------------------------- awk ' { total += $'"${numero_colonne}"' } END { print total } ' "$nomfichier" # ----------------------------- # Fin du script awk. # Il pourrait ne pas être sûr de passer des variables shells à un script awk # embarqué, donc Stephane Chazelas propose l'alternative suivante: # --------------------------------------- # awk -v numero_colonne="$numero_colonne" ' # { total += $numero_colonne # } # END { # print total # }' "$nomfichier" # --------------------------------------- exit 0

Pour ces scripts nécessitant un seul outil qui-fait-tout, il existe une espèce de couteau suisse nommée Perl. Perl combine les capacités de sed et awk, et y ajoute un grand sous-ensemble de fonctionnalités C. Il est modulaire et contient le support de pratiquement tout ce qui est connu en commençant par la programmation orientée. Des petits scripts Perl vont eux-mêmes s'intégrer dans d'autres scripts, et il existe quelques raisons de croire que Perl peut totalement remplacer les scripts shells (bien que l'auteur de ce document reste sceptique).

Perl inclus dans un script Bash
#!/bin/bash # Les commandes shell peuvent précéder un script Perl. echo "Ceci précède le script Perl embarqué à l'intérieur de \"$0\"." echo "===============================================================" perl -e 'print "Ceci est un script Perl embarqué.\n";' # Comme sed, Perl utilise aussi l'option "-e". echo "===============================================================" echo "Néanmoins, le script peut aussi contenir des commandes shell et système." exit 0

Il est même possible de combiner un script Bash et un script Perl dans le même fichier. Dépendant de la façon dont le script est invoqué, soit la partie Bash soit la partie Perl sera exécutée.

Combinaison de scripts Bash et Perl
#!/bin/bash # bashandperl.sh echo "Bienvenue de la partie Bash de ce script." # Plus de commandes Bash peuvent suivre ici. exit 0 # Fin de la partie Bash de ce script. # ======================================================= #!/usr/bin/perl # Cette partie du script doit être appelé avec l'option -x. print "Bienvenue de la partie Perl de ce script.\n"; # Plus de commandes Perl peuvent suivre ici. # Fin de la partie Perl de ce script.

bash$ bash bashandperl.sh Bienvenue de la partie Bash du script. bash$ perl -x bashandperl.sh Bienvenue de la partie Perl du script.


Tests et comparaisons: Alternatives

Pour les tests, la construction [[ ]] peut être plus appropriée que

[ ]

. De même, les comparaisons arithmétiques pourraient bénéficier de la construction (( )).


Récursion

Un script peut-il s'appeler récursivement? En fait, oui.

Un script (inutile) qui s'appelle récursivement
#!/bin/bash # recurse.sh # Un script peut-il s'appeler récursivement? # Oui, Mais est-ce d'une utilité quelconque? # (Voir le script suivant.) ECHELLE=10 MAXVAL=9 i=$RANDOM let "i %= $ECHELLE" # Génère un nombre aléatoire entre 0 et $MAXVAL. if [ "$i" -lt "$MAXVAL" ] then echo "i = $i" ./$0 # Le script lance récursivement une nouvelle instance de lui-même. fi # Chaque fils du script fait de même, jusqu'à ce que la valeur #+ générée $i soit égale à $MAXVAL. # Utiliser une boucle "while" au lieu d'un test "if/then" pose des problèmes. # Expliquez pourquoi. exit 0
Un script (utile) qui s'appelle récursivement
#!/bin/bash # pb.sh: carnet de téléphones. # Ecrit par Rick Boivie, et utilisé avec sa permission. # Modifications par l'auteur du document. MINARGS=1 # Le script a besoin d'au moins un argument. FICHIERDONNEES=./carnet_telephone NOMPROG=$0 E_SANSARGS=70 # Erreur lorsque sans arguments. if [ $# -lt $MINARGS ]; then echo "Usage: "$NOMPROG" donnees" exit $E_SANSARGS fi if [ $# -eq $MINARGS ]; then grep $1 "$FICHIERDONNEES" else ( shift; "$NOMPROG" $* ) | grep $1 # Le script s'appelle récursivement. fi exit 0 # Le script sort ici. # On peut mettre des commentaires sans '#' et des données après #+ ce point. # ------------------------------------------------------------------------ # Exemple d'un carnet d'adresses: John Doe 1555 Main St., Baltimore, MD 21228 (410) 222-3333 Mary Moe 9899 Jones Blvd., Warren, NH 03787 (603) 898-3232 Richard Roe 856 E. 7th St., New York, NY 10009 (212) 333-4567 Sam Roe 956 E. 8th St., New York, NY 10009 (212) 444-5678 Zoe Zenobia 4481 N. Baker St., San Franciso, SF 94338 (415) 501-1631 # ------------------------------------------------------------------------ $bash pb.sh Roe Richard Roe 856 E. 7th St., New York, NY 10009 (212) 333-4567 Sam Roe 956 E. 8th St., New York, NY 10009 (212) 444-5678 $bash pb.sh Roe Sam Sam Roe 956 E. 8th St., New York, NY 10009 (212) 444-5678 # Lorsqu'au moins un argument est passé au script, celui-ci n'affiche *que* #+ le(s) ligne(s) contenant tous les arguments.

Attention : Trop de niveaux de récursivité peut vider la pile du script, causant une erreur de segmentation (segfault).


Coloriser des scripts

Les séquences d'échappement d'ANSI (63) permettent de régler les attributs de l'écran, tels que le texte en gras et la couleur d'affichage et de fond. Les fichiers batch DOS utilisaient communément les séquences d'échappement ANSI pour les affichages couleur, comme peuvent le faire les scripts Bash.

Une base de données d'adresses colorisée
#!/bin/bash # ex30a.sh: Version "colorisée" de ex30.sh. # Base de données d'adresses. clear # Efface l'écran. echo -n " " echo -e '\E[37;44m'"\033[1mListe de contacts\033[0m" # Blanc sur fond bleu echo; echo echo -e "\033[1mChoisissez une des personnes suivantes:\033[0m" # Bold tput sgr0 echo "(Entrez seulement la première lettre du nom.)" echo echo -en '\E[47;34m'"\033[1mE\033[0m" # Bleu tput sgr0 # Réinitialise les couleurs à la #+ "normale." echo "vans, Roland" # "[E]vans, Roland" echo -en '\E[47;35m'"\033[1mJ\033[0m" # Magenta tput sgr0 echo "ones, Mildred" echo -en '\E[47;32m'"\033[1mS\033[0m" # Vert tput sgr0 echo "mith, Julie" echo -en '\E[47;31m'"\033[1mZ\033[0m" # Rouge tput sgr0 echo "ane, Morris" echo read personne case "$personne" in # Notez que la variable est entre guillemets. "E" | "e" ) # Accepte une entrée en majuscule ou minuscule. echo echo "Roland Evans" echo "4321 Floppy Dr." echo "Hardscrabble, CO 80753" echo "(303) 734-9874" echo "(303) 734-9892 fax" echo "revans@zzy.net" echo "Business partner & old friend" ;; "J" | "j" ) echo echo "Mildred Jones" echo "249 E. 7th St., Apt. 19" echo "New York, NY 10009" echo "(212) 533-2814" echo "(212) 533-9972 fax" echo "milliej@loisaida.com" echo "Girlfriend" echo "Birthday: Feb. 11" ;; # Ajoutez de l'info pour Smith & Zane plus tard. * ) # Option par défaut. # Une entrée vide (en appuyant uniquement sur RETURN) vient ici aussi. echo echo "Pas encore dans la base de données." ;; esac tput sgr0 # Réinitialisation des couleurs à la #+ "normale". echo exit 0

La séquence d'échappement ANSI la plus simple et peut-être la plus utile est du texte gras, \033[1m ... \033[0m. \033 représente un escape, "[1" active l'attribut gras, alors que "[0" la désactive. "m" termine chaque terme de la séquence d'échappement.

bash$ echo -e "\033[1mCeci est un texte en gras.\033[0m"

Un séquence d'échappement similaire active l'attribut de soulignement (sur un rxvt et un aterm).

bash$ echo -e "\033[4mCe texte est souligné.\033[0m"

Remarque : Avec un echo, l'option -e active les séquences d'échappement.

D'autres séquences d'échappement modifie le texte et/ou la couleur du fond.

bash$ echo -e '\E[34;47mCeci est affiché en bleu.'; tput sgr0 bash$ echo -e '\E[33;44m'"texte jaune sur fond bleu"; tput sgr0

tput sgr0 restaure les paramétrages du terminal en normal. L'omettre laisse toute sortie ultérieure à partir de ce terminal en bleu.

Utiliser le modèle suivant pour écrire du texte coloré sur un fond coloré.

echo -e '\E[COLOR1;COLOR2mDu texte vient ici.'

Les caractères "\E[" commencent la séquence d'échappement. Les nombres "COLOR1" et "COLOR2" séparés par le point-virgule spécifient une couleur de texte et de fond, suivant la table ci-dessous. (L'ordre des nombres importe peu, car les nombres d'avant et d'arrière-plan tombent dans des échelles qui ne se couvrent pas.) "m" termine la séquence d'échappement, et le texte commence immédiatement après ça.

Notez aussi que les guillemets simples enferment le reste de la séquence de commandes suivant le echo -e.

Les nombres dans la table suivante fonctionnent pour un terminal rxvt. Les résultats peuvent varier pour d'autres émulateurs de terminaux.

Couleur Avant-plan Arrière-plan
30 40
31 41
32 42
33 43
34 44
35 45
36 46
37 47
Afficher du texte coloré
#!/bin/bash # color-echo.sh: Affiche des messages texte en couleur. # Modifier ce script pour vos besoins propres. # C'est plus facile que de coder manuellement les couleurs. noir='\E[30;47m' rouge='\E[31;47m' vert='\E[32;47m' jaune='\E[33;47m' bleu='\E[34;47m' magenta='\E[35;47m' cyan='\E[36;47m' blanc='\E[37;47m' alias init="tput sgr0" # Initialise les attributs texte à la normale #+ sans effacer l'écran. cecho () # Echo couleur. # Argument $1 = message # Argument $2 = couleur { local msg_par_defaut="Pas de message." # N'a pas réellement besoin d'être une variable # locale. message=${1:-$msg_par_defaut}# Message par défaut. couleur=${2:-$noir} # Noir par défaut, si non spécifié. echo -e "$color" echo "$message" init # Retour à la normale. return } # Maintenant, essayons-le. # ---------------------------------------------------- cecho "Je me sens bleu..." $bleu cecho "Le magenta ressemble plus à du violet." $magenta cecho "Vert avec envie." $vert cecho "Vous voyez rouge?" $rouge cecho "Cyan, mieux connu sous le nom d'aqua." $cyan cecho "Pas de couleur précisée (noir par défaut)." # Argument $color manquant. cecho "Couleur \"vide\" passée (noir par défaut)." "" # Argument $color vide. cecho # Arguments $message et $color manquants. cecho "" "" # Arguments $message et $color vides. # ---------------------------------------------------- echo exit 0 # Exercices: # --------- # 1) Ajouter l'attribut "gras" à la fonction 'cecho ()'. # 2) Ajouter des options pour des fonds colorés.

Attention : Il existe néanmoins un problème majeur avec tout ceci. Les séquences d'échappement ANSI sont généralement non portables. Ce qui fonctionne bien sur certains émulateurs de terminaux (ou la console) peut fonctionner différemment, ou pas du tout, sur les autres. Un script "coloré" ayant une excellente forme sur la machine de l'auteur du script peut produire une sortie illisible chez quelqu'un d'autre. Ceci compromet grandement l'utilité de la "colorisation" des scripts, et relègue cette technique au statut de gadget, voire de "jeu" .

L'utilitaire color de Moshe Jacobson (http://runslinux.net/projects/color) simplifie considérement l'utilisation des séquences d'échappement ANSI. Il substitue une syntaxe claire et logique aux constructions bizarres dont on vient de discuter.


Optimisations

La plupart des scripts shell sont des solutions rapides et sales pour des problèmes non complexes. Du coup, les optimiser pour la rapidité n'est pas vraiment un problème. Considérez le cas où un script réalise une tâche importante, le fait bien mais tourne trop lentement. Le réécrire avec un langage compilé peut ne pas être une option trés agréable. La solution la plus simple serait de réécrire les parties du script qui le ralentissent. Est-il possible d'appliquer les principes de l'optimisation de code même à un script lent?

Vérifiez les boucles dans le script. Le temps consommé par des opérations répétitives s'ajoute rapidement. Si c'est possible, supprimez les opérations consomnatrices de temps des boucles.

Utilisez les commandes internes plutôt que les commandes système. Ces commandes intégrées s'exécutent plus rapidement et ne lancent habituellement pas un sous-shell lors de leur appel.

Eviter les commandes non nécessaires, particulièrement dans un tuyau. La commande cat semble particulièrement sujette à une sur-utilisation dans les scripts.

Utilisez les outils time et times pour vérifier les commandes particulièrement intensives. Considérez la réécriture des sections critiques en code C, voire en assembleur.

Essayez de minimiser les entrées/sorties fichier. Bash n'est pas particulièrement efficace sur la gestion des fichiers, donc considérez l'utilisation d'outils plus appropriés pour ceci dans le script, tels que awk ou Perl.

Ecrivez vos scripts d'une façon structurée, cohérente, ainsi ils peuvent être réorganisés et sécurisés selon les besoins. Quelques unes des techniques d'optimisation applicables aux langages de haut niveau peuvent fonctionner pour des scripts, mais d'autres, tels que le déroulement de boucles, sont pratiquement impossibles. Par dessus tout, utilisez votre bon sens.

Pour une excellente démonstration du fait qu'une optimisation drastique réduit le temps d'exécution d'un script, voir Paiement mensuel sur une hypothèque.


Astuces assorties

  • Pour conserver un enregistrement des scripts utilisateur lancés lors de certaines sessions ou lors d'un certain nombre de sessions, ajoutez les lignes suivantes à chaque script dont vous voulez garder la trace. Ceci va conserver un fichier d'enregistrement des noms de script et des heures d'invocation.
  • L'opérateur ajoute des lignes dans un fichier. Qu'en est-il si vous voulez ajouter une ligne au début d'un fichier existant, c'est-à-dire la coller au tout début?Bien sûr, sed peut aussi le faire.
  • Un script shell peut agir comme une commande interne à l'intérieur d'un autre script shell, d'un script Tcl ou d'un script wish, voire même d'un Makefile. Il peut être appelé comme une commande shell externe dans un programme C en utilisant l'appel system(), c'est-à-dire system("nom_du_script");.
  • Réunissez les fichiers contenant vos définitions et vos fonctions les plus utiles. Quand nécessaire, "incluez" un ou plus de ces "fichiers bibliothèque" dans des scripts avec soit le point (.) soit la commande source.
  • Utiliser des entêtes de commentaires pour accroître la clarté et la compréhension des scripts.
  • En utilisant la variable d'état de sortie $?, un script peut tester si un paramètre contient seulement des chiffres, ainsi il peut être traité comme un entier.
  • L'échelle 0 - 255 des valeurs de retour des fonctions est une limitation importante. Les variables globales et autres moyens de contourner ce problème sont souvent des problèmes en eux-même. Une autre méthode, pour que la fonction communique une valeur de retour au corps principal du script, est que la fonction écrive sur stdout la "valeur de sortie" , et d'assigner cette sortie à une variable.
  • Astuce de valeur de retour
    #!/bin/bash # multiplication.sh multiplie () # Multiplie les paramètres passés. { # Acceptera un nombre variable d'arguments. local produit=1 until [ -z "$1" ] # Jusqu'à la fin de tous les arguments... do let "produit *= $1" shift done echo $produit # N'affichera pas sur stdout, } #+ car cela va être affecté à une variable. mult1=15383; mult2=25211 val1=`multiplie $mult1 $mult2` echo "$mult1 X $mult2 = $val1" # 387820813 mult1=25; mult2=5; mult3=20 val2=`multiplie $mult1 $mult2 $mult3` echo "$mult1 X $mult2 X $mult3 = $val2" # 2500 mult1=188; mult2=37; mult3=25; mult4=47 val3=`multiplie $mult1 $mult2 $mult3 $mult4` echo "$mult1 X $mult2 X $mult3 X mult4 = $val3" # 8173300 exit 0
  • La même technique fonctionne aussi pour les chaînes de caractères alphanumériques. Ceci signifie qu'une fonction peut "renvoyer" une valeur non-numérique.Il est même possible pour une fonction de "renvoyer" plusieurs valeurs avec cette méthode.
  • Une astuce permettant de renvoyer plus d'une valeur de retour
    #!/bin/bash # sum-product.sh # Une fonction peut "renvoyer" plus d'une valeur. somme_et_produit () # Calcule à la fois la somme et le produit des arguments. { echo $(( $1 + $2 )) $(( $1 * $2 )) # Envoie sur stdout chaque valeur calculée, séparée par un espace. } echo echo "Entrez le premier nombre " read premier echo echo "Entrez le deuxième nombre " read second echo valretour=`somme_et_produit $premier $second` # Affecte à la variable la sortie #+ de la fonction. somme=`echo "$valretour" | awk '{print $1}'` # Affecte le premier champ. produit=`echo "$valretour" | awk '{print $2}'`# Affecte le deuxième champ. echo "$premier + $second = $somme" echo "$premier * $second = $produit" echo exit 0
  • Ensuite dans notre liste d'astuces se trouvent les techniques permettant de passer un tableau à une fonction, "renvoyant " alors un tableau en retour à la fonction principale du script.Le passage d'un tableau nécessite de charger des éléments séparés par un espace d'un tableau dans une variable avec la substitution de commandes. Récupérer un tableau comme "valeur de retour" à partir d'une fonction utilise le stratagème mentionné précédemment de la sortie (echo) du tableau dans la fonction, puis d'invoquer la substitution de commande et l'opérateur ( ... ) pour l'assigner dans un tableau.
  • Passer et renvoyer un tableau
    #!/bin/bash # array-function.sh: Passer un tableau à une fonction et... # "renvoyer" un tableau à partir d'une fonction Passe_Tableau () { local tableau_passe # Variable locale. tableau_passe=( `echo "$1"` ) echo "${tableau_passe[@]}" # Liste tous les éléments du nouveau tableau déclaré #+ et initialisé dans la fonction. } tableau_original=( element1 element2 element3 element4 element5 ) echo echo "tableau_original = ${tableau_original[@]}" # Liste tous les éléments du tableau original. # Voici une astuce qui permet de passer un tableau à une fonction. # ********************************** argument=`echo ${tableau_original[@]}` # ********************************** # Emballer une variable #+ avec tous les éléments du tableau original séparés avec un espace. # # Notez que d'essayer de passer un tableau en lui-même ne fonctionnera pas. # Voici une astuce qui permet de récupérer un tableau comme "valeur de retour". # ***************************************** tableau_renvoye=( `Passe_Tableau "$argument"` ) # ***************************************** # Affecte une sortie de la fonction à une variable de type tableau. echo "tableau_renvoye = ${tableau_renvoye[@]}" echo "=============================================================" # Maintenant, essayez encore d'accèder au tableau en dehors de la #+ fonction. Passe_Tableau "$argument" # La fonction liste elle-même le tableau, mais... #+ accèder au tableau de l'extérieur de la fonction est interdit. echo "Tableau passé (de l'intérieur de la fonction) = ${tableau_passe[@]}" # Valeur NULL comme il s'agit d'une variable locale. echo exit 0
  • Pour un exemple plus élaboré du passage d'un tableau dans les fonctions, voir life: Jeu de la Vie.
  • En utilisant la construction en double parenthèses, il est possible d'utiliser la syntaxe style C pour initialiser et incrémenter des variables ainsi que dans des boucles for et while. Voir Une boucle for à la C et Syntaxe à la C pour une boucle while.
  • Une technique de scripts utile est d'envoyer de manière répétée la sortie d'un filtre (par un tuyau) vers le même filtre, mais avec un ensemble différent d'arguments et/ou options. Ceci est spécialement intéressant pour tr et grep.
  • Un peu de fun avec des anagrammes
    #!/bin/bash # agram.sh: Jouer avec des anagrammes. # Trouver les anagrammes de... LETTRES=etaoinshrdlu anagram "$LETTRES" | # Trouver tous les anagrammes de cet ensemble de lettres... grep '.......' | # Avec au moins 7 lettres, grep '^is' | # commençant par 'is' grep -v 's$' | # sans les puriels grep -v 'ed$' # Sans verbe au passé ("ed" en anglais) # Utilise l'utilitaire "anagram" #+ qui fait partie du paquetage de liste de mots "yawl" de l'auteur. # http://ibiblio.org/pub/Linux/libs/yawl-0.2.tar.gz exit 0 # Fin du code. bash$ sh agram.sh islander isolate isolead isotheral
  • Voir aussi Etat de la connexion, Générer des puzzles Crypto-Citations, et soundex: Conversion phonétique.
  • Utiliser des "documents anonymes " pour mettre en commentaire des blocs de code, pour ne pas avoir à mettre en commentaire chaque ligne avec un #. Voir Décommenter un bloc de code.
  • Lancer sur une machine un script dépendant de la présence d'une commande qui peut être absente est dangereux. Utilisez whatis pour éviter des problèmes potentiels avec ceci.
  • La commande run-parts est utile pour lancer un ensemble de scripts dans l'ordre, particulièrement en combinaison avec cron ou at.
  • Il serait bien d'être capable d'invoquer les objets X-Windows à partir d'un script shell. Il existe plusieurs paquets qui disent le faire, à savoir Xscript, Xmenu, et widtools. Les deux premiers ne semblent plus maintenus. Heureusement, il est toujours possible d'obtenir widtools ici. Attention:Le paquet widtools (widget tools, outils pour objets) nécessite que la bibliothèque XForms soit installée. De plus, le Makefile a besoin d'être édité de façon judicieuse avant que le paquet ne soit construit sur un système Linux typique. Finalement, trois des six objets offerts ne fonctionnent pas (en fait, ils génèrent un défaut de segmentation). Pour plus d'efficacité des scripts utilisant des widgets, essayez Tk ou wish (des dérivés de Tcl), PerlTk (Perl avec des extensions Tk), tksh (ksh avec des extensions Tk), XForms4Perl (Perl avec des extensions XForms), Gtk-Perl (Perl avec des extensions Gtk), ou PyQt (Python avec des extensions Qt).

Problèmes de sécurité

Un bref message d'avertissement sur la sécurité des scripts est approprié. Un script shell peut contenir un ver (worm), un troyen (trojan), ou même un virus. Pour cette raison, ne lancez jamais un script en tant que root (ou ne permettez jamais son insertion dans les scripts de démarrage du système /etc/rc.d) à moins que vous n'ayez obtenu ledit script d'une source de confiance ou que vous l'ayez consenscieusement analysé pour vous assurer qu'il ne fait rien de nuisible.

De nombreux chercheurs chez Bell Labs et d'autres sites, incluant M. Douglas McIlroy, Tom Duff et Fred Cohen ont étudié dans les implications des virus de scripts shell. Ils concluent qu'il est tout à fait facile même pour un novice, un "script kiddie" , d'en écrire un. (64)

Voici encore une autre raison d'apprendre les scripts. Etre capable de regarder et de comprendre les scripts peut protéger votre système d'être piraté ou endommagé.


Problèmes de portabilité

Ce livre s'occupe principalement des scripts Bash sur un système GNU/Linux. De la même façon, les utilisateurs de sh et ksh y trouveront beaucoup d'idées de grande valeur.

Un grand nombre de shells et de langages de scripts semble converger vers le standard POSIX 1003.2. Appeler Bash avec l'option --posix ou insérer un set -o posix au début d'un script fait que Bash se conforme très étroitement à ce standard. Même en oubliant ceci, la plupart des scripts Bash tourneront de la même façon sous ksh, et vice-versa, puisque Chet Ramey a porté les fonctionnalités de ksh aux dernières versions de Bash.

Sur un UNIX commercial, les scripts utilisant les fonctionnalités spécifiques aux commandes standards GNU peuvent ne pas fonctionner. Ceci devient de moins en moins un problème ces dernières années, car les outils GNU ont petit à petit remplacé les versions propriétaires même sur les UNIX "solides" . La récente relâche des sources de nombreux outils de Caldera ne fera qu'accélérer la tendance.


Scripts sous Windows

Même les utilisateurs sous d'autres OS peuvent lancer des scripts shell de type UNIX et donc bénéficier d'un grand nombre des leçons de ce livre. Le paquet Cygwin de Cygnus et les utilitaires MKS de Mortice Kern Associates ajoutent des fonctionnalités de scripts à Windows.


4.17. Bash, version 2

La version actuelle de Bash, celle que vous avez sur votre machine, est la version 2.XX.Y.

bash$ echo $BASH_VERSION 2.05.8(1)-release

Cette mise à jour du langage de script Bash classique ajoute les variables de type tableau, (65) l'expansion de chaînes de caractères et de paramètres, et une meilleure méthode pour les références de variables indirectes, parmi toutes les fonctionnalités.

Expansion de chaîne de caractères
#!/bin/bash # Expansion de chaînes de caractères. # Introduit avec la version 2 de Bash. # Les chaînes de caractères de la forme $'xxx' ont les caractères d'échappement # standard interprétés. echo $'Trois cloches sonnant à la fois \a \a \a' echo $'Trois retours chariot \f \f \f' echo $'10 retours chariot \n\n\n\n\n\n\n\n\n\n' exit 0

Références de variables indirectes - la nouvelle façon
#!/bin/bash # Référencement de variables indirectes. # Ceci a quelques uns des attributs du C++. a=lettre_de_l_alphabet lettre_de_l_alphabet=z echo "a = $a" # Référence directe. echo "Maintenant a = ${!a}" # Référence indirecte. # La notation ${!variable} est bien supérieure à l'ancien "eval var1=\$$var2" echo t=cellule_table_3 cellule_table_3=24 echo "t = ${!t}" # t = 24 cellule_table_3=387 echo "La valeur de t a changé en ${!t}" # 387 # Ceci est utile pour référencer les membres d'un tableau ou d'une table, # ou pour simuler un tableau multi-dimensionnel. # Une option d'indexage aurait été bien (sigh). exit 0
Simple application de base de données, utilisant les références de variables indirectes
#!/bin/bash # resistor-inventory.sh # Simple base de données utilisant le référencement indirecte de variables. # ============================================================== # # Données B1723_value=470 # ohms B1723_powerdissip=.25 # watts B1723_colorcode="yellow-violet-brown" # bandes de couleur B1723_loc=173 # où elles sont B1723_inventory=78 # combien B1724_value=1000 B1724_powerdissip=.25 B1724_colorcode="brown-black-red" B1724_loc=24N B1724_inventory=243 B1725_value=10000 B1725_powerdissip=.25 B1725_colorcode="brown-black-orange" B1725_loc=24N B1725_inventory=89 # ============================================================== # echo PS3='Entrez le numéro du catalogue: ' echo select numero_catalogue in "B1723" "B1724" "B1725" do Inv=${numero_catalogue}_inventory Val=${numero_catalogue}_value Pdissip=${numero_catalogue}_powerdissip Loc=${numero_catalogue}_loc Ccode=${numero_catalogue}_colorcode echo echo "Catalogue numéro $numero_catalogue:" echo "Il existe ${!Inv} résistances de [${!Val} ohm / ${!Pdissip} watt] en stock." echo "Elles sont situées dans bin # ${!Loc}." echo "Leur code couleur est \"${!Ccode}\"." break done echo; echo # Exercice: # -------- # Réécrire ce script en utilisant des tableaux, plutôt qu'en utilisant le #+ référencement indirecte des variables. # Quelle methode est plus logique et intuitive? # Notes: # ----- # Les scripts shells sont inappropriés pour tout, sauf des applications simples #+ de base de données, et même là, cela implique des astuces. # Il est bien mieux d'utiliser un langage supportant nativement les structures #+ de données, tels que C++ ou Java (voire même Perl). exit 0
Utiliser des tableaux et autres astuces pour gérer quatre mains aléatoires dans un jeu de cartes
#!/bin/bash # A besoin d'être appelé avec #!/bin/bash2 sur les anciennes machines. # Cartes: # gère quatre mains d'un jeu de cartes. NON_RECUPERE=0 RECUPERE=1 DUPE_CARD=99 LIMITE_BASSE=0 LIMITE_HAUTE=51 CARTES_DANS_SUITE=13 CARTES=52 declare -a Jeu declare -a Suites declare -a Cartes # Il aurait été plus simple et plus intuitif avec un seul tableau à trois dimensions. # Peut-être qu'une future version de Bash gèrera des tableaux multi-dimensionnels. initialise_Jeu () { i=$LIMITE_BASSE until [ "$i" -gt $LIMITE_HAUTE ] do Jeu[i]=$NON_RECUPERE # Initialise chaque carte d'un "Jeu" comme non récupérée. let "i += 1" done echo } initialise_Suites () { Suites[0]=C #Carreaux Suites[1]=D #Piques Suites[2]=H #Coeurs Suites[3]=S #Trèfles } initialise_Cartes () { Cartes=(2 3 4 5 6 7 8 9 10 J Q K A) # Autre méthode pour initialiser un tableau. } recupere_une_carte () { numero_carte=$ALEATOIRE let "numero_carte %= $CARTES" if [ "${Jeu[numero_carte]}" -eq $NON_RECUPERE ] then Jeu[numero_carte]=$RECUPERE return $numero_carte else return $DUPE_CARD fi } analyse_carte () { nombre=$1 let "suit_nombre = nombre / CARTES_DANS_SUITE" suite=${Suites[suit_nombre]} echo -n "$suit-" let "no_carte = nombre % CARTES_DANS_SUITE" Carte=${Cartes[no_carte]} printf %-4s $Carte # Affiche proprement les cartes. } recherche_nombre_aleatoire () # Générateur de nombres aléatoires. { recherche=`eval date +%s` let "recherche %= 32766" ALEATOIRE=$recherche } gere_cartes () { echo cartes_recuperees=0 while [ "$cartes_recuperees" -le $LIMITE_HAUTE ] do recupere_une_carte t=$? if [ "$t" -ne $DUPE_CARD ] then analyse_carte $t u=$cartes_recuperees+1 # Retour à un indexage simple (temporairement). let "u %= $CARTES_DANS_SUITE" if [ "$u" -eq 0 ] # Condition if/then imbriquée. then echo echo fi # Mains séparées. let "cartes_recuperees += 1" fi done echo return 0 } # Programmation structurée : # la logique entière du programme est modularisée en fonctions. #================ recherche_nombre_aleatoire initialise_Jeu initialise_Suites initialise_Cartes gere_cartes exit 0 #================ # Exercice 1: # Ajouter des commentaires détaillées de ce script. # Exercice 2: # Revoir ce script pour afficher chaque main triée par suite. # Vous pouvez ajouter d'autres fonctionnalités suivant vos souhaits. # Exercice 3: # Simplifier et améliorer la logique du script.

5. Notes finales


Note de l'auteur

Comment en suis-je venu à écrire un livre sur l'écriture de scripts Bash? C'est une étrange histoire. Il semble qu'il y a quelques années, j'avais besoin d'apprendre à écrire des scripts shell -- et quelle meilleure façon de le faire que de lire un bon livre sur le sujet? J'ai cherché à acheter un tutoriel et une référence couvrant tous les aspects du sujet. Je cherchais un livre qui prendrait tous les concepts difficiles, les expliquerait dans un soucis du détail avec des exemples bien commentés. (66) En fait, je recherchais quelque chose dans ce genre. Malheureusement, il n'existait pas, et si je le voulais, je devais l'écrire. Et donc nous en sommes là.

Ceci me rappelle l'histoire apocryphe du professeur fou. Il était complètement fou. Au vu d'un livre, tout livre -- à la bibliothèque, à la librairie, partout -- il devenait complètement obsédé avec l'idée qu'il pourrait l'avoir écrit, devrait l'avoir écrit et fait un meilleur travail pour commencer. Il aurait foncer chez lui et fait simplement cela, écrire un livre avec exactement le même titre. A sa mort quelques années après, il avait plusieurs milliers de livre à son actif, plaçant Asimov lui-même dans la honte. Les livres pouvaient ne pas être bon, qui sait, mais est-ce que cela comptait? Voici un brave homme qui a vécu son rêve, même si il l'a obsédé, et je ne peux m'empêcher d'admirer ce vieux fou...


A propos de l'auteur

L'auteur ne se prétend aucun crédit ou qualifications spéciales, en dehors d'une certaine compulsion pour l'écriture. (67) Ce livre est un peu à l'oposé de son autre travail majeur, HOW-2 Meet Women: The Shy Man's Guide to Relationships (NdT: Comment rencontrer les femmes: le guide des relations à l'usage de l'homme timide). Il a aussi écrit le Software-Building HOWTO.

Utilisateur Linux depuis 1995 (Slackware 2.2, noyau 1.2.1), l'auteur a émis quelques truffes, incluant l'utilitaire de cryptage en un temps cruft , le calculateur mcalc , l'arbitre pour le Scrabble, et le paquetage d'une liste de jeux de mots yawl. Il a débuté en programmant en FORTRAN IV sur un CDC 3800, mais il n'est pas le moins du monde nostalgique de ces jours.

Vivant dans une communauté reculée du désert avec son épouse et son chien, il chérit la faiblesse humaine.


Outils utilisés pour produire ce livre


Matériel

Un IBM Thinkpad usé, modèle 760XL (P166, 104 meg RAM) sous Red Hat 7.1/7.3. OK, il est lent et a un drôle de clavier, mais il bat la tablette du grand chef et un stylo No 2.


Logiciel et Impression

  1. L'éditeur de texte de Bram Moolenaar, avec sa connaissance puissante de SGML, vim.
  2. OpenJade, un moteur de rendu DSSSL pour convertir des documents SGML en d'autres formats.
  3. Les feuilles de style DSSSL de Norman Walsh.
  4. DocBook, The Definitive Guide, par Norman Walsh et Leonard Muellner (O'Reilly, ISBN 1-56592-580-7). C'est la référence standard pour tout ceux qui essaient d'écrire un document avec le format Docbook SGML.

Crédits

La participation de la communauté a rendu ce projet possible. L'auteur reconnait qu'écrire ce libre aurait été une tâche impossible sans l'aide et les retours de toutes ces personnes.

Philippe Martin a traduit ce document en DocBook/SGML. Alors que ce n'est pas son travail dans cette petite compagnie française où il est développeur, il aime travailler sur la documentation et le logiciel GNU/Linux, lire de la littérature, jouer de la musique et rendre heureux ses amis. Vous pouvez le rencontrer en France ou dans le pays Basque, ou lui envoyer un courrier électronique à feloy@free.fr.

Philippe Martin m'a aussi indiqué que les paramètres positionnels après $9 sont possibles en utilisant la notation des {accolades}, voir Paramètres positionnels .

Stephane Chazelas a envoyé une longue liste de corrections, ajouts et exemples de scripts. Plus qu'un contributeur, il a, dans les faits, pris le rôle d'éditeur pour ce document. Merci beaucoup!

Je voudrais spécialement remercier Patrick Callahan, Mike Novak et Pal Domokos pour avoir trouvé les bugs, indiqué les ambiguités et suggéré des clarifications et des modifications. Leur discussion vivante m'a inspiré pour essayer de rendre ce document lisible.

Je suis reconnaissant à Jim Van Zandt d'avoir pointé les erreurs et omissions dans la version 0.2 de ce document. Il a aussi contribué à un script d'exemple instructif.

Beaucoup de remerciements à Jordi Sanfeliu pour m'avoir donné la permission d'utiliser son script (tree: Afficher l'arborescence d'un répertoire).

De même, merci à Michel Charpentier pour sa permission d'utiliser ce script de factorisation dc (Factoring).

Merci à Noah Friedman pour sa permission d'utiliser son script de fonctions sur les chaînes de caractères (string: Manipuler les chaînes de caractères comme en C ).

Emmanuel Rouat a suggéré des corrections et ajouts sur la substitution de commandes et sur les alias. Il a aussi contribué à un très joli exemple de fichier .bashrc ().

Heiner Steven m'a gentimment donné la permission d'utiliser son script de conversion de base, Conversion de base. Il a aussi fait quelques corrections et beaucoup d'autres suggestions d'une grande aide. Grands mercis..

Rick Boivie a contribué au script délicieusement récursif pb.sh (Un script (utile) qui s'appelle récursivement) et aux améliorations de performances pour le script monthlypmt.sh (Paiement mensuel sur une hypothèque).

Florian Wisser m'a montré des points très fin sur les tests des chaînes de caractères (voir vérification si une chaîne nulle), mais aussi sur d'autres points.

Oleg Philon a envoyé des suggestions concernant cut et pidof.

Marc-Jano Knopp a envoyé des corrections sur les fichiers batch DOS.

Hyun Jin Cha a trouvé plusieurs erreurs dans la documentation lors d'un traduction coréenne. Merci de me les avoir indiqué.

Andreas Abraham a envoyé une longue liste d'erreurs de typographie et d'autres corrections. Un grand merci!

D'autres ont fait des suggestions intéressantes et ont indiqué des erreurs: Gabor Kiss, Leopold Toetsch, Peter Tillier, Marcus Berglof, Tony Richardson, Nick Drage (idées sur les scripts!), Rich Bartell, Jess Thrysoee, Adam Lazur, Bram Moolenaar, Baris Cicek, Greg Keraunen, Keith Matthews, Sandro Magi, Albert Reiner, Dim Segebart, Rory Winston, Lee Bigelow, Wayne Pollock, "jipe" , and David Lawyer (lui-même auteur de quatre guides pratiques).

Ma gratitude pour Chet Ramey et Brian Fox pour l'écriture d'un élégant et puissant outil de scripts, Bash.

Et un très grand merci pour les volontaires qui ont durement travaillé au Linux Documentation Project. Le LDP contient un dépôt de connaissances Linux et a, sur une grande étendue, permis la publication de ce livre.

Merci en particulier à ma femme, Anita, pour ses encouragements et pour son support émotionnel.


6. Bibliographie

  • Titre:Computers Under Attack: Intruders, Worms, and Viruses
    Editeur:ACM Press1990
    ISBN : 0-201-53067-8
    This compendium contains a couple of articles on shell script viruses.*

  • Titre:Sed and Awk
    2nd edition Editeur:O'Reilly and Associates1997
    ISBN : 1-156592-225-5
    To unfold the full power of shell scripting, you need at least a passing familiarity with sed and awk. This is the standard tutorial. It includes an excellent introduction to "regular expressions" . Read this book.*

  • Titre:Essential System Administration
    2nd edition Editeur:O'Reilly and Associates1995
    ISBN : 1-56592-127-5
    This excellent sys admin manual has a decent introduction to shell scripting for sys administrators and does a nice job of explaining the startup and initialization scripts. The book is long overdue for a third edition (are you listening, Tim O'Reilly?).*

  • Titre:Unix Shell Programming
    Editeur:Hayden1990
    ISBN : 067248448X
    The standard reference, though a bit dated by now.*

  • Titre:Beginning Linux Programming
    Editeur:Wrox Press1996
    ISBN : 1874416680
    Good in-depth coverage of various programming languages available for Linux, including a fairly strong chapter on shell scripting.*

  • Titre:Advanced C Programming on the IBM PC
    Editeur:Windcrest Books1989
    ISBN : 0830693637
    Excellent coverage of algorithms and general programming practices.*

  • Titre:Unix Shell Programming Tools
    Editeur:McGraw-Hill1999
    ISBN : 0070397333
    Good info on shell scripting, with examples, and a short intro to Tcl and Perl.*

  • Titre:Learning the Bash Shell
    2nd edition Editeur:O'Reilly and Associates1998
    ISBN : 1-56592-347-2
    This is a valiant effort at a decent shell primer, but somewhat deficient in coverage on programming topics and lacking sufficient examples.*

  • Titre:Bourne Shell Quick Reference Guide
    Editeur:ASP, Inc.1991
    ISBN : 093573922X
    A very handy pocket reference, despite lacking coverage of Bash-specific features.*

  • Titre:Unix Power Tools
    2nd edition Editeur:O'Reilly and Associates Editeur:Random House1997
    ISBN : 1-56592-260-3
    Contains a couple of sections of very informative in-depth articles on shell programming, but falls short of being a tutorial. It reproduces much of the regular expressions tutorial from the Dougherty and Robbins book, above.*

  • Titre:Computers, Pattern, Chaos, and Beauty
    Editeur:St. Martin's Press1990
    ISBN : 0-312-04123-3
    A treasure trove of ideas and recipes for computer-based exploration of mathematical oddities.*

  • Titre:How To Solve It
    Editeur:Princeton University Press1973
    ISBN : 0-691-02356-5
    The classic tutorial on problem solving methods (i.e., algorithms).*

  • Titre:Bash Reference Card
    Editeur:SSC1998
    ISBN : 1-58731-010-5
    Excellent Bash pocket reference (don't leave home without it). A bargain at $4.95, but also available for free download on-line in pdf format.*

  • Titre:Effective Awk Programming
    Editeur:Free Software Foundation / O'Reilly and Associates2000
    ISBN : 1-882114-26-4
    The absolute best awk tutorial and reference. The free electronic version of this book is part of the awk documentation, and printed copies of the latest version are available from O'Reilly and Associates.This book has served as an inspiration for the author of this document.*

  • Titre:Learning the Korn Shell
    Editeur:O'Reilly and Associates1993
    ISBN : 1-56592-054-6
    This well-written book contains some excellent pointers on shell scripting.*

  • Titre:LINUX: Rute User's Tutorial and Exposition
    1st edition Editeur:2002
    ISBN : 0-13-033351-4
    Very detailed and readable introduction to Linux system administration.The book is available in print, or on-line.*

  • Titre:Linux in a Nutshell
    2nd edition Editeur:O'Reilly and Associates1999
    ISBN : 1-56592-585-8
    The all-around best Linux command reference, even has a Bash section.*

  • Titre:The UNIX CD Bookshelf
    2nd edition Editeur:O'Reilly and Associates2000
    ISBN : 1-56592-815-6
    An array of six UNIX books on CD ROM, including UNIX Power Tools, Sed and Awk, and Learning the Korn Shell. A complete set of all the UNIX references and tutorials you would ever need at about $70. Buy this one, even if it means going into debt and not paying the rent.Unfortunately, out of print at present.*

  • The O'Reilly books on Perl. (Actually, any O'Reilly books.)---

  • Ben Okopnik's well-written introductory Bash scripting articles in issues 53, 54, 55, 57, and 59 of the Linux Gazette , and his explanation of "The Deep, Dark Secrets of Bash" in issue 56.

  • Chet Ramey's bash - The GNU Shell, a two-part series published in issues 3 and 4 of the Linux Journal, July-August 1994.

  • Mike G's Bash-Programming-Intro HOWTO.

  • Richard's UNIX Scripting Universe.

  • Chet Ramey's Bash F.A.Q.

  • Ed Schaefer's Shell Corner in Unix Review.

  • Example shell scripts at Lucc's Shell Scripts .

  • Example shell scripts at SHELLdorado .

  • Example shell scripts at Noah Friedman's script site.

  • Steve Parker's Shell Programming Stuff.

  • Example shell scripts at SourceForge Snippet Library - shell scrips.

  • Giles Orr's Bash-Prompt HOWTO.

  • The sed F.A.Q. / Do It With Sed.

  • Very nice sed, awk, and regular expression tutorials at The UNIX Grymoire.

  • The GNU gawk reference manual (gawk is the extended GNU version of awk available on Linux and BSD systems).

  • Trent Fisher's groff tutorial.

  • Mark Komarinski's Printing-Usage HOWTO.

  • There is some nice material on I/O redirection in chapter 10 of the textutils documentation at the University of Alberta site.

  • Rick Hohensee has written the osimpa i386 assembler entirely as Bash scripts.

  • Rocky Bernstein is in the process of developing a "full-fledged" debugger for Bash.---

  • The excellent "Bash Reference Manual", by Chet Ramey and Brian Fox, distributed as part of the "bash-2-doc" package (available as an rpm). See especially the instructive example scripts in this package.

  • The comp.os.unix.shell newsgroup.

  • The manpages for bash and bash2, date, expect, expr, find, grep, gzip, ln, patch, tar, tr, bc, xargs. The texinfo documentation on bash, dd, m4, gawk, and sed.


7. Appendice : Scripts contribués

Ces scripts, bien que ne rentrant pas dans le texte de ce document, illustrent quelques techniques intéressantes de programmation shell. Ils sont aussi utiles. Amusez-vous à les analyser et à les lancer.

manview: Visualiser des pages man formatées
#!/bin/bash # manview.sh: Formatte la source d'une page man pour une visualisation. # Ceci est utile lors de l'écriture de la source d'une page man et que vous #+ voulez voir les résultats intermédiaires lors de votre travail. E_MAUVAISARGS=65 if [ -z "$1" ] then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi groff -Tascii -man $1 | less # De la page man de groff. # Si la page man inclut des tables et/ou des équations, # alors le code ci-dessus échouera. # La ligne suivante peut gérer de tels cas. # # gtbl < "$1" | geqn -Tlatin1 | groff -Tlatin1 -mtty-char -man # # Merci, S.C. exit 0
mailformat: Formater un courrier électronique
#!/bin/bash # mail-format.sh : Formatte les courriers électroniques. # Supprime les caractères '>', les tabulations et coupe les lignes #+ excessivement longues. # ================================================================= # Vérification standard des argument(s) du script ARGS=1 E_MAUVAISARGS=65 mail-format.sh: if [ $# -ne $ARGS ] # Le bon nombre d'arguments a-t'il été passé au script? then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi if [ -f "$1" ] # Vérifie si le fichier existe. then nomfichier=$1 else echo "Le fichier \"$1\" n'existe pas." exit $E_PASDEFICHIER fi # ================================================================= LONGUEUR_MAX=70 # Longueur à partir de laquelle on coupe les lignes. # Suppression du caractère '>', des tabulations en début de ligne #+ et coupure des lignes après $LONGUEUR_MAX caractères. sed ' s/^>// s/^ *>// s/^ *// s/ *// ' $1 | fold -s --width=$LONGUEUR_MAX # L'option -s de "fold" coupe les lignes à un espace blanc, si #+ possible. # Ce script a été inspiré par un article d'un journal bien connu #+ proposant un utilitaire Windows de 164Ko pour les mêmes fonctionnalités. # # Un joli ensemble d'utilitaire de manipulation de texte et un langage de #+ scripts efficace apportent une alternative à des exécutables gonflés. exit 0
rn: Un utilitaire simple pour renommer des fichiers
#! /bin/bash # # Un très simplifié "renommeur" de fichiers (basé sur "lowercase.sh"). # # L'utilitaire "ren", par Vladimir Lanin (lanin@csd2.nyu.edu), #+ fait un bien meilleur travail que ceci. ARGS=2 E_MAUVAISARGS=65 UN=1 # Pour avoir correctement singulier ou pluriel # (voir plus bas.) if [ $# -ne "$ARGS" ] then echo "Usage: `basename $0` ancien-modele nouveau-modele" # Comme avec "rn gif jpg", qui renomme tous les fichiers gif du répertoire #+ courant en jpg. exit $E_MAUVAISARGS fi nombre=0 # Garde la trace du nombre de fichiers renommés. for fichier in *$1* # Vérifie tous les fichiers correspondant du répertoire. do if [ -f "$fichier" ] # Si trouve une correspondance... then fname=`basename $fichier` # Supprime le chemin. n=`echo $fname | sed -e "s/$1/$2/"` # Substitue nouveau par ancien dans # le fichier. mv $fname $n # Renomme. let "nombre += 1" fi done if [ "$nombre" -eq "$UN" ] # Pour une bonne grammaire. then echo "$nombre fichier renommé." else echo "$nombre fichiers renommés." fi exit 0 # Exercices: # --------- # Avec quel type de fichiers cela ne fonctionnera pas? # Comment corriger cela? # # Réécrire ce script pour travailler sur tous les fichiers d'un répertoire, #+ contenant des espaces dans leur noms, et en les renommant après avoir #+ substitué chaque espace par un tiret bas.
blank-rename: Renommer les fichiers dont le nom contient des espaces
#! /bin/bash # blank-rename.sh # # Substitue les tirets soulignés par des blancs dans tous les fichiers d'un #+ répertoire. UN=1 # Pour obtenir le singulier/puriel correctement (voir # plus bas). nombre=0 # Garde trace du nombre de fichiers renommés. TROUVE=0 # Valeur de retour en cas de succès. for fichier in * #Traverse tous les fichiers du répertoire. do echo "$fichier" | grep -q " " # Vérifie si le nom du fichier if [ $? -eq $TROUVE ] #+ contient des espace(s). then nomf=$fichier # Supprime le chemin. n=`echo $nomf | sed -e "s/ /_/g"` # Remplace l'espace par un tiret. mv "$nomf" "$n" # Réalise le renommage. let "nombre += 1" fi done if [ "$nombre" -eq "$UN" ] # Pour une bonne grammaire. then echo "$nombre fichiers renommés." else echo "$nombre fichiers renommés." fi exit 0
encryptedpw: Charger un fichier sur un site ftp, en utilisant un mot de passe crypté en local
#!/bin/bash # Exemple "ex72.sh" modifié pour utiliser les mots de passe cryptés. # Notez que c'est toujours peu sécurisé, car le mot de passe décrypté est #+ envoyé en clair. # Utilisez quelque chose comme "ssh" si cela vous préoccupe. E_MAUVAISARGS=65 if [ -z "$1" ] then echo "Usage: `basename $0` nomfichier" exit $E_MAUVAISARGS fi NomUtilisateur=bozo # Changez suivant vos besoins. motpasse=/home/bozo/secret/fichier_avec_mot_de_passe_crypte. # Le fichier contient un mot de passe crypté. Nomfichier=`basename $1` # Supprime le chemin du fichier Serveur="XXX" # Changez le nom du serveur et du répertoire suivant Repertoire="YYY" #+ vos besoins. MotDePasse=`cruft $motpasse` # Décrypte le mot de passe. # Utilise la paquetage de cryptage de fichier "cruft" de l'auteur lui-même, #+ basé sur l'algorythme classique "onetime pad", #+ et disponible à partir de: #+ Site primaire: ftp://metalab.unc.edu /pub/Linux/utils/file #+ cruft-0.2.tar.gz [16k] ftp -n $Serveur Fin-de-Session user $NomUtilisateur $MotDePasse binary bell cd $Repertoire put $Nomfichier bye Fin-de-Session # L'option -n de "ftp" désactive la connexion automatique. # "bell" fait sonner une cloche après chaque transfert. exit 0
copy-cd: Copier un CD de données
#!/bin/bash # copy-cd.sh: copier un CD de données CDROM=/dev/cdrom # périphérique CD ROM OF=/home/bozo/projects/cdimage.iso # fichier de sortie # /xxxx/xxxxxxx/ A modifier suivant votre système. TAILLEBLOC=2048 VITESSE=2 # Utiliser une vitesse supèrieure #+ si elle est supportée. echo; echo "Insérez le CD source, mais ne le montez *pas*." echo "Appuyez sur ENTER lorsque vous êtes prêt. " read pret # Attendre une entrée, $pret n'est # pas utilisé. echo; echo "Copie du CD source vers $OF." echo "Ceci peut prendre du temps. Soyez patient." dd if=$CDROM of=$OF bs=$TAILLEBLOC # Copie brute du périphérique. echo; echo "Retirez le CD de données." echo "Insérez un CDR vierge." echo "Appuyez sur ENTER lorsque vous êtes prêt. " read pret # Attendre une entrée, $pret n'est # pas utilisé. echo "Copie de $OF vers CDR." cdrecord -v -isosize speed=$VITESSE dev=0,0 $OF # Utilise le paquetage "cdrecord" de Joerg Schilling's (voir sa doc). # http://www.fokus.gmd.de/nthp/employees/schilling/cdrecord.html echo; echo "Copie terminée de $OF vers un CDR du périphérique $CDROM." echo "Voulez-vous écraser le fichier image (o/n)? " # Probablement un fichier # immense. read reponse case "$reponse" in [oO]) rm -f $OF echo "$OF supprimé." ;; *) echo "$OF non supprimé.";; esac echo # Exercice: # Modifiez l'instruction "case" pour aussi accepter "oui" et "Oui" comme #+ entrée. exit 0
collatz: Séries de Collatz
#!/bin/bash # collatz.sh # Le célèbre "hailstone" ou la série de Collatz. # ---------------------------------------------- # 1) Obtenir un entier "de recherche" à partir de la ligne de commande. # 2) NOMBRE --- seed # 3) Afficher NOMBRE. # 4) Si NOMBRE est pair, divisez par 2, ou # 5)+ si impair, multiplier par 3 et ajouter 1. # 6) NOMBRE --- résultat # 7) Boucler à l'étape 3 (pour un nombre spécifié d'itérations). # # La théorie est que chaque séquence, quelle soit la valeur initiale, #+ se stabilisera éventuellement en répétant des cycles "4,2,1...", #+ même après avoir fluctuée à travers un grand nombre de valeurs. # # C'est une instance d'une "itération", une opération qui remplit son #+ entrée par sa sortie. # Quelque fois, le résultat est une série "chaotique". MAX_ITERATIONS=200 # Pour une grande échelle de nombre (32000), augmenter MAX_ITERATIONS. h=${1:-$$} # Nombre de recherche # Utiliser $PID comme nombre de recherche, #+ si il n'est pas spécifié en argument de la #+ ligne de commande. echo echo "C($h) --- $MAX_ITERATIONS Iterations" echo for ((i=1; i<=MAX_ITERATIONS; i++)) do echo -n "$h " # ^^^^^ # tab let "reste = h % 2" if [ "$reste" -eq 0 ] # Pair? then let "h /= 2" # Divise par 2. else let "h = h*3 + 1" # Multiplie par 3 et ajoute 1. fi COLONNES=10 # Sortie avec 10 valeurs par ligne. let "retour_ligne = i % $COLONNES" if [ "$retour_ligne" -eq 0 ] then echo fi done echo # Pour plus d'informations sur cette fonction mathématique, #+ voir "Computers, Pattern, Chaos, and Beauty", par Pickover, p. 185 ff., #+ comme listé dans la bibliographie. exit 0
days-between: Calculer le nombre de jours entre deux dates
#!/bin/bash # days-between.sh: Nombre de jours entre deux dates. # Usage: ./days-between.sh [M]M/[D]D/AAAA [M]M/[D]D/AAAA ARGS=2 # Deux arguments attendus en ligne de commande. E_PARAM_ERR=65 # Erreur de paramètres. ANNEEREF=1600 # Année de référence. SIECLE=100 JEA=365 AJUST_DIY=367 # Ajusté pour l'année bisextile + fraction. MEA=12 JEM=31 CYCLE=4 MAXRETVAL=256 # Valeur de retour positive la plus grande possible renvoyée # par une fonction. diff= # Déclaration d'une variable globale pour la différence de date. value= # Déclaration d'une variable globale pour la valeur absolue. jour= # Déclaration de globales pour jour, mois, année. mois= annee= Erreur_Param () # Mauvais paramètres en ligne de commande. { echo "Usage: `basename $0` [M]M/[D]D/YYYY [M]M/[D]D/YYYY" echo " (la date doit être supérieure au 1/3/1600)" exit $E_PARAM_ERR } Analyse_Date () # Analyse la date à partir des paramètres en ligne de { # commande. mois=${1%%/**} jm=${1%/**} # Jour et mois. jour=${dm#*/} let "annee = `basename $1`" # Pas un nom de fichier mais fonctionne de la même façon. } verifie_date () # Vérifie la validité d'une date. { [ "$jour" -gt "$JEM" ] || [ "$mois" -gt "$MEA" ] || [ "$annee" -lt "$ANNEEREF" ] && Erreur_Param # Sort du script si mauvaise(s) valeur(s). # Utilise une "liste-ou / liste-et". # # Exercice: Implémenter une vérification de date plus rigoureuse. } supprime_zero_devant () # Il est mieux de supprimer les zéros possibles { # du jour et/ou du mois car sinon Bash va les val=${1#0} # interpréter comme des valeurs octales return $val # (POSIX.2, sect 2.9.2.1). } index_jour () # Formule de Gauss: { # Nombre de jours du 3 Jan. 1600 jusqu'à la date passée # en arguments. jour=$1 mois=$2 annee=$3 let "mois = $mois - 2" if [ "$mois" -le 0 ] then let "mois += 12" let "annee -= 1" fi let "annee -= $ANNEEREF" let "indexyr = $annee / $SIECLE" let "Jours = $JEA*$annee + $annee/$CYCLE - $indexyr + $indexyr/$CYCLE + $AJUST_DIY*$mois/$MEA + $jour - $JEM" # Pour une explication en détails de cet algorithme, voir # http://home.t-online.de/home/berndt.schwerdtfeger/cal.htm if [ "$Jours" -gt "$MAXRETVAL" ] # Si supérieur à 256, then # alors devient négatif let "dindex = 0 - $Jours" # et pourra être renvoyé par la fonction. else let "dindex = $Jours" fi return $dindex } calcule_difference () # Différence entre les indices des jours. { let "diff = $1 - $2" # Variable globale. } abs () # Valeur absolue. { # Utilise une variable globale "valeur". if [ "$1" -lt 0 ] # Si négatif then # alors let "value = 0 - $1" # change de signe, else # sinon let "value = $1" # on le laisse. fi } if [ $# -ne "$ARGS" ] # Requiert deux arguments en ligne de commande. then Erreur_Param fi Analyse_Date $1 verifie_date $jour $mois $annee # Vérifie si la date est valide. supprime_zero_devant $jour # Supprime tous zéros possibles day=$? # sur le jour et/ou le mois. supprime_zero_devant $mois month=$? index_jour $jour $mois $annee date1=$? abs $date1 # S'assure que c'est positif en récupérant date1=$value # la valeur absolue. Analyse_Date $2 verifie_date $jour $mois $annee supprime_zero_devant $jour day=$? supprime_zero_devant $mois month=$? index_jour $jour $mois $annee date2=$? abs $date2 # S'assure que c'est positif. date2=$value calcule_difference $date1 $date2 abs $diff # S'assure que c'est positif. diff=$value echo $diff exit 0 # Comparez ce script avec l'implémentation de la formule de Gauss en C sur # http://buschencrew.hypermart.net/software/datedif
makedict: Créer un dictionnaire
#!/bin/bash # makedict.sh [make dictionary] # Modification du script /usr/sbin/mkdict. # Script original copyright 1993, par Alec Muffett. # # Ce script modifié est inclus dans ce document d'une manière consistante avec #+ le document "LICENSE" du paquetage "Crack" dont fait partie le script original. # Ce script manipule des fichiers texte pour produire une liste triée de mots #+ trouvés dans les fichiers. # Ceci pourrait être utile pour compiler les dictionnaires et pour des #+ recherches lexicographiques. E_MAUVAISARGS=65 if [ ! -r "$1" ] # Au moins un argument, qui doit être then #+ un fichier valide. echo "Usage: $0 fichiers-à-manipuler" exit $E_MAUVAISARGS fi # SORT="sort" # Plus nécessaire de définir des options #+ pour sort. Modification du script #+ original. cat $* | # Contenu des fichiers spécifiés vers stdout. tr A-Z a-z | # Convertion en majuscule. tr ' ' '\012' | # Nouveau: changement des espaces en #+ retours chariot. # tr -cd '\012[a-z][0-9]' | # Suppression de tout ce qui n'est pas # alphanumérique #+ (script original). tr -c '\012a-z' '\012' | # Plutôt que de supprimer #+ maintenant changement des non alpha #+ en retours chariot. sort | # Les options $SORT ne sont plus #+ nécessaires maintenant. uniq | # Suppression des mots dupliqués. grep -v '^#' | # Suppression des lignes commençant avec #+ le symbole '#'. grep -v '^$' # Suppression des lignes blanches. exit 0
soundex: Conversion phonétique
#!/bin/bash # soundex.sh: Calcule le code "soundex" pour des noms # ======================================================= # Script soundex # par # Mendel Cooper # thegrendel@theriver.com # 23 Janvier 2002 # # Placé dans le domaine public. # # Une version légèrement différente de ce script est apparu dans #+ la colonne "Shell Corner" d'Ed Schaefer en juillet 2002 #+ du magazine en ligne "Unix Review", #+ http://www.unixreview.com/documents/uni1026336632258/ # ======================================================= NBARGS=1 # A besoin du nom comme argument. E_MAUVAISARGS=70 if [ $# -ne "$NBARGS" ] then echo "Usage: `basenom $0` nom" exit $E_MAUVAISARGS fi affecte_valeur () # Affecte une valeur numérique { #+ aux lettres du nom. val1=bfpv # 'b,f,p,v' = 1 val2=cgjkqsxz # 'c,g,j,k,q,s,x,z' = 2 val3=dt # etc. val4=l val5=mn val6=r # Une utilisation particulièrement intelligente de 'tr' suit. # Essayez de comprendre ce qui se passe ici. valeur=$( echo "$1" \ | tr -d wh \ | tr $val1 1 | tr $val2 2 | tr $val3 3 \ | tr $val4 4 | tr $val5 5 | tr $val6 6 \ | tr -s 123456 \ | tr -d aeiouy ) # Affecte des valeurs aux lettres. # Supprime les numéros dupliqués, sauf s'ils sont séparés par des voyelles. # Ignore les voyelles, sauf en tant que séparateurs, donc les supprime à la fin. # Ignore 'w' et 'h', même en tant que séparateurs, donc les supprime en premier. # # La substitution de commande ci-dessus utilise plus de tubes qu'un plombier # g. } nom_en_entree="$1" echo echo "Nom = $nom_en_entree" # Change tous les caractères du nom entré en minuscules. # ------------------------------------------------ nom=$( echo $nom_en_entree | tr A-Z a-z ) # ------------------------------------------------ # Au cas où cet argument est un mixe de majuscules et de minuscules. # Préfixe des codes soundex: première lettre du nom. # -------------------------------------------- pos_caract=0 # Initialise la position du caractère. prefixe0=${nom:$pos_caract:1} prefixe=`echo $prefixe0 | tr a-z A-Z` # Met en majuscule la première lettre de soundex. let "pos_caract += 1" # Aller directement au deuxième caractère. nom1=${nom:$pos_caract} # ++++++++++++++++++++++++++ Correctif Exception +++++++++++++++++++++++++++++++++ # Maintenant, nous lançons à la fois le nom d'entrée et le nom décalé d'un #+ caractère vers la droite au travers de la fonction d'affectation de valeur. # Si nous obtenons la même valeur, cela signifie que les deux premiers #+ caractères du nom ont la même valeur et que l'une d'elles doit être annulée. # Néanmoins, nous avons aussi besoin de tester si la première lettre du nom est #+ une voyelle ou 'w' ou 'h', parce que sinon cela va poser problème. caract1=`echo $prefixe | tr A-Z a-z` # Première lettre du nom en minuscule. affecte_valeur $nom s1=$valeur affecte_valeur $nom1 s2=$valeur affecte_valeur $caract1 s3=$valeur s3=9$s3 # Si la première lettre du nom est une #+ voyelle ou 'w' ou 'h', #+ alors sa "valeur" sera nulle (non #+ initialisée). #+ Donc, positionnons-la à 9, une autre #+ valeur non utilisée, qui peut être #+ vérifiée. if [[ "$s1" -ne "$s2" || "$s3" -eq 9 ]] then suffixe=$s2 else suffixe=${s2:$pos_caract} fi # ++++++++++++++++++++++ fin Correctif Exception +++++++++++++++++++++++++++++++++ fin=000 # Utiliser au moins 3 zéro pour terminer. soun=$prefixe$suffixe$fin # Terminer avec des zéro. LONGUEURMAX=4 # Tronquer un maximum de 4 caractères soundex=${soun:0:$LONGUEURMAX} echo "Soundex = $soundex" echo # Le code soundex est une méthode d'indexage et de classification de noms #+ en les groupant avec ceux qui sonnent de la même façon. # Le code soundex pour un nom donné est la première lettre de ce nom, suivi par #+ un code calculé sur trois chiffres. # Des noms similaires devraient avoir les mêmes codes soundex. # Exemples: # Smith et Smythe ont tous les deux le soundex "S-530" # Harrison = H-625 # Hargison = H-622 # Harriman = H-655 # Ceci fonctionne assez bien en pratique mais il existe quelques anomalies. # # # Certaines agences du gouvernement U.S. utilisent soundex, comme le font les # généalogistes. # # Pour plus d'informations, voir #+ "National Archives and Records Administration home page", #+ http://www.nara.gov/genealogy/soundex/soundex.html # Exercice: # -------- # Simplifier la section "Correctif Exception" de ce script. exit 0
life: Jeu de la Vie
#!/bin/bash # life.sh: "Life in the Slow Lane" # ##################################################################### # # Ce script est la version Bash du "Jeu de la vie" de John Conway. # # "Life" est une implémentation simple d'automatisme cellulaire. # # --------------------------------------------------------------------- # # Sur un tableau rectangulaire, chaque "cellule" sera soit "vivante" # # soit "morte". On désignera une cellule vivante avec un point et une # # cellule morte avec un espace. # # Nous commençons avec un tableau composé aléatoirement de points et # #+ d'espaces. Ce sera la génération de départ, "génération 0". # # Déterminez chaque génération successive avec les règles suivantes : # # 1) Chaque cellule a huit voisins, les cellules voisines (gauche, # #+ droite, haut, bas ainsi que les quatre diagonales. # # 123 # # 4*5 # # 678 # # # # 2) Une cellule vivante avec deux ou trois voisins vivants reste # #+ vivante. # # 3) Une cellule morte avec trois cellules vivantes devient vivante # #+ (une "naissance"). # SURVIE=2 # NAISSANCE=3 # # 4) Tous les autres cas conduisent à des cellules mortes. # # ##################################################################### # fichier_de_depart=gen0 # Lit la génération de départ à partir du fichier "gen0". # Par défaut, si aucun autre fichier n'est spécifié à #+ l'appel de ce script. # if [ -n "$1" ] # Spécifie un autre fichier "génération 0". then if [ -e "$1" ] # Vérifie son existence. then fichier_de_depart="$1" fi fi VIVANT1=. MORT1=_ # Représente des cellules vivantes et "mortes" dans le fichier de départ. # Ce script utilise un tableau 10 sur 10 (pourrait être augmenté #+ mais une grande grille ralentirait de beaucoup l'exécution). LIGNES=10 COLONNES=10 GENERATIONS=10 # Nombre de générations pour le cycle. # Ajustez-le en l'augmentant si vous en avez le temps. AUCUNE_VIVANTE=80 # Code de sortie en cas de sortie prématurée, #+ si aucune cellule n'est vivante. VRAI=0 FAUX=1 VIVANTE=0 MORTE=1 avar= # Global; détient la génération actuelle. generation=0 # Initialise le compteur des générations. # ================================================================= let "cellules = $LIGNES * $COLONNES" # Nombre de cellules. declare -a initial # Tableaux contenant les "cellules". declare -a current affiche () { alive=0 # Nombre de cellules "vivantes". # Initialement à zéro. declare -a tab tab=( `echo "$1"` ) # Argument convertit en tableau. nombre_element=${#tab[*]} local i local verifligne for ((i=0; i<$nombre_element; i++)) do # Insère un saut de ligne à la fin de chaque ligne. let "verifligne = $i % LIGNES" if [ "$verifligne" -eq 0 ] then echo # Saut de ligne. echo -n " " # Indentation. fi cellule=${tab[i]} if [ "$cellule" = . ] then let "vivante += 1" fi echo -n "$cellule" | sed -e 's/_/ /g' # Affiche le tableau et modifie les tirets bas en espaces. done return } EstValide () # Teste si les coordonnées sont valides. { if [ -z "$1" -o -z "$2" ] # Manque-t'il des arguments requis ? then return $FAUX fi local ligne local limite_basse=0 # Désactive les coordonnées négatives. local limite_haute local gauche local droite let "limite_haute = $LIGNES * $COLONNES - 1" # Nombre total de cellules. if [ "$1" -lt "$limite_basse" -o "$1" -gt "$limite_haute" ] then return $FAUX # En dehors des limites. fi ligne=$2 let "gauche = $ligne * $LIGNES" # Limite gauche. let "droite = $gauche + $COLONNES - 1" # Limite droite. if [ "$1" -lt "$gauche" -o "$1" -gt "$droite" ] then return $FAUX # En dehors des limites. fi return $VRAI # Coordonnées valides. } EstVivante () # Teste si la cellule est vivante. # Prend un tableau, un numéro de cellule et un état de #+ cellule comme arguments. { ObtientNombre "$1" $2 # Récupère le nombre de cellules vivantes dans le voisinage. local voisinage=$? if [ "$voisinage" -eq "$NAISSANCE" ] # Vivante dans tous les cas. then return $VIVANTE fi if [ "$3" = "." -a "$voisinage" -eq "$SURVIE" ] then # Vivante uniquement si précédemment vivante. return $VIVANTE fi return $MORTE # Par défaut. } ObtientNombre () # Compte le nombre de cellules vivantes dans le # voisinage de la cellule passée en argument. # Deux arguments nécessaires : # $1) tableau contenant les variables # $2) numéro de cellule { local numero_cellule=$2 local tableau local haut local centre local bas local l local ligne local i local t_hau local t_cen local t_bas local total=0 local LIGNE_NHBD=3 tableau=( `echo "$1"` ) let "haut = $numero_cellule - $COLONNES - 1" # Initialise le voisinage de la #+ cellule. let "centre = $numero_cellule - 1" let "bas = $numero_cellule + $COLONNES - 1" let "l = $numero_cellule / $LIGNES" for ((i=0; i<$LIGNE_NHBD; i++)) # Parcours de gauche à droite. do let "t_hau = $haut + $i" let "t_cen = $centre + $i" let "t_bas = $bas + $i" let "ligne = $l" # Calcule la ligne centrée du voisinage. EstValide $t_cen $ligne # Position de la cellule valide ? if [ $? -eq "$VRAI" ] then if [ ${tableau[$t_cen]} = "$VIVANT1" ] # Est-elle vivante ? then # Oui ? let "total += 1" # Incrémenter le total. fi fi let "ligne = $l - 1" # Compte la ligne du haut. EstValide $t_haut $haut if [ $? -eq "$VRAI" ] then if [ ${tableau[$t_haut]} = "$VIVANT1" ] then let "total += 1" fi fi let "ligne = $l + 1" # Compte la ligne du bas. EstValide $t_bas $ligne if [ $? -eq "$VRAI" ] then if [ ${tableau[$t_bas]} = "$VIVANT1" ] then let "total += 1" fi fi done if [ ${tableau[$numero_cellule]} = "$VIVANT1" ] then let "total -= 1" # S'assurer que la valeur de la cellule testée fi #+ n'est pas elle-même comptée. return $total } prochaine_gen () # Mise à jour du tableau des générations. { local tableau local i=0 tableau=( `echo "$1"` ) # Argument passé concerti en tableau. while [ "$i" -lt "$cellules" ] do EstVivante "$1" $i ${tableau[$i]} # La cellule est-elle vivante ? if [ $? -eq "$VIVANTE" ] then # Si elle l'est, alors tableau[$i]=. #+ représente la cellule avec un point. else tableau[$i]="_" # Sinon, avec un tiret bas. fi #+ (qui sera transformé plus tard en espace). let "i += 1" done # let "generation += 1" # Incrémente le nombre de génération. # Initialise la variable à passer en tant que paramètre à la fonction # "affiche". une_var=`echo ${tableau[@]}` # Convertit un tableau en une variable de type chaîne. affiche "$une_var" # L'affiche. echo; echo echo "Génération $generation -- $vivante vivante" if [ "$alive" -eq 0 ] then echo echo "Sortie prématurée : aucune cellule encore vivante !" exit $AUCUNE_VIVANTE # Aucun intérêt à continuer fi #+ si aucune cellule n'est vivante. } # ========================================================= # main () # Charge un tableau initial avec un fichier de départ. initial=( `cat "$fichier_de_depart" | sed -e '/#/d' | tr -d '\n' |\ sed -e 's/\./\. /g' -e 's/_/_ /g'` ) # Supprime les lignes contenant le symbole de commentaires '#'. # Supprime les retours chariot et insert des espaces entre les éléments. clear # Efface l'écran. echo # Titre echo "=======================" echo " $GENERATIONS générations" echo " du" echo " \"Jeu de la Vie\"" echo "=======================" # -------- Affiche la première génération. -------- Gen0=`echo ${initial[@]}` affiche "$Gen0" # Affiche seulement. echo; echo echo "Génération $generation -- $alive vivante" # ------------------------------------------- let "generation += 1" # Incrémente le compteur de générations. echo # ------- Affiche la deuxième génération. ------- Actuelle=`echo ${initial[@]}` prochaine_gen "$Actuelle" # Mise à jour & affichage. # ------------------------------------------ let "generation += 1" # Incrémente le compteur de générations. # ------ Boucle principale pour afficher les générations conséquentes ------ while [ "$generation" -le "$GENERATIONS" ] do Actuelle="$une_var" prochaine_gen "$Actuelle" let "generation += 1" done # ============================================================== echo exit 0 # -------------------------------------------------------------- # Le tableau dans ce script a un "problème de bordures". # Les bordures haute, basse et des côtés sont limitrophes d'une absence #+ de cellules mortes. # Exercice: Modifiez le script pour avoir la grille # + de façon à ce que les côtés gauche et droit se touchent, # + comme le haut et le bas.
Fichier de données pour le Jeu de la Vie
# This is an example "generation 0" start-up file for "life.sh". # -------------------------------------------------------------- # The "gen0" file is a 10 x 10 grid using a period (.) for live cells, #+ and an underscore (_) for dead ones. We cannot simply use spaces #+ for dead cells in this file because of a peculiarity in Bash arrays. # [Exercise for the reader: explain this.] # # Lines beginning with a '#' are comments, and the script ignores them. __.__..___ ___._.____ ____.___.. _._______. ____._____ ..__...___ ____._____ ___...____ __.._..___ _..___..__

+++

Les deux scripts suivants sont de Mark Moraes de l'Université de Toronto. Voir le fichier joint "Moraes-COPYRIGHT" pour les permissions et restrictions.

behead: Supprimer les en-têtes des courriers électroniques et des nouvelles
#! /bin/sh # Supprime l'entête d'un message mail/news jusqu'à la première ligne vide. # Mark Moraes, Université de Toronto # ==> Ces commentaires sont ajoutés par l'auteur de ce document. if [ $# -eq 0 ]; then # ==> Si pas d'arguments en ligne de commande, alors fonctionne avec un # ==> fichier redirigé vers stdin. sed -e '1,/^$/d' -e '/^[ ]*$/d' # --> Supprime les lignes vides et les autres jusqu'à la première # --> commençant avec un espace blanc. else # ==> Si des arguments sont présents en ligne de commande, alors fonctionne avec # ==> des fichiers nommés. for i do sed -e '1,/^$/d' -e '/^[ ]*$/d' $i # --> De même. done fi # ==> Exercice: Ajouter la vérification d'erreurs et d'autres options. # ==> # ==> Notez que le petit script sed se répère à l'exception des arguments # ==> passés. # ==> Est-il intéressant de l'embarquer dans une fonction? Pourquoi?
ftpget: Télécharger des fichiers via ftp
#! /bin/sh # $Id: ftpget.sh,v 1.2.2.1 2004/04/05 21:37:37 admin Exp $ # Script pour réaliser une suite d'actions avec un ftp anonyme. Généralement, # convertit une liste d'arguments de la ligne de commande en entrée vers ftp. # Simple et rapide - écrit comme compagnon de ftplist # -h spécifie l'hôte distant (par défaut prep.ai.mit.edu) # -d spécifie le répertoire distant où se déplacer - vous pouvez spécifier une # séquence d'options -d - elles seront exécutées chacune leur tour. Si les # chemins sont relatifs, assurez-vous d'avoir la bonne séquence. Attention aux # chemins relatifs, il existe bien trop de liens symboliques de nos jours. # (par défaut, le répertoire distant est le répertoire au moment de la connexion) # -v active l'option verbeux de ftp et affiche toutes les réponses du serveur # ftp # -f fichierdistant[:fichierlocal] récupère le fichier distant et le renomme en # localfile # -m modele fait un mget suivant le modèle spécifié. Rappelez-vous de mettre # entre guillemets les caractères shell. # -c fait un cd local vers le répertoire spécifié # Par exemple example, # ftpget -h expo.lcs.mit.edu -d contrib -f xplaces.shar:xplaces.sh \ # -d ../pub/R3/fixes -c ~/fixes -m 'fix*' # récupèrera xplaces.shar à partir de ~ftp/contrib sur expo.lcs.mit.edu et # l'enregistrera sous xplaces.sh dans le répertoire actuel, puis obtiendra # tous les correctifs de ~ftp/pub/R3/fixes et les placera dans le répertoire # ~/fixes. # De façon évidente, la séquence des options est importante, car les commandes # équivalentes sont exécutées par ftp dans le même ordre. # # Mark Moraes (moraes@csri.toronto.edu), Feb 1, 1989 # ==> Les signes inférieur et supérieur ont été modifiés par des "parens" pour # ==> éviter des soucis avec DocBook. # # ==> Ces commentaires ont été ajoutés par l'auteur de ce document. # PATH=/local/bin:/usr/ucb:/usr/bin:/bin # export PATH # ==> Les deux lignes ci-dessus faisaient parti du script original et étaient # ==> probablement inutiles FICHIER_TEMPORAIRE=/tmp/ftp.$$ # ==> Crée un fichier temporaire, en utilisant l'identifiant du processus du # ==> script ($$) pour construire le nom du fichier. SITE=`domainname`.toronto.edu # ==> 'domainname' est similaire à 'hostname' # ==> Ceci pourrait être réécrit en ajoutant un paramètre ce qui rendrait son # ==> utilisation plus générale. usage="Usage: $0 [-h hotedistant] [-d repertoiredistant]... [-f fichierdistant:fichierlocal]... \ [-c repertoirelocal] [-m modele] [-v]" optionsftp="-i -n" verbflag= set -f # So we can use globbing in -m set x `getopt vh:d:c:m:f: $*` if [ $? != 0 ]; then echo $usage exit 65 fi shift trap 'rm -f ${FICHIER_TEMPORAIRE} ; exit' 0 1 2 3 15 echo "user anonymous ${USER-gnu}@${SITE} > ${FICHIER_TEMPORAIRE}" # ==> Ajout des guillemets (recommendé pour les echo complexes). echo binary >> ${FICHIER_TEMPORAIRE} for i in $* # ==> Analyse les arguments de la ligne de commande. do case $i in -v) verbflag=-v; echo hash >> ${FICHIER_TEMPORAIRE}; shift;; -h) hotedistant=$2; shift 2;; -d) echo cd $2 >> ${FICHIER_TEMPORAIRE}; if [ x${verbflag} != x ]; then echo pwd >> ${FICHIER_TEMPORAIRE}; fi; shift 2;; -c) echo lcd $2 >> ${FICHIER_TEMPORAIRE}; shift 2;; -m) echo mget "$2" >> ${FICHIER_TEMPORAIRE}; shift 2;; -f) f1=`expr "$2" : "\([^:]*\).*"`; f2=`expr "$2" : "[^:]*:\(.*\)"`; echo get ${f1} ${f2} >> ${FICHIER_TEMPORAIRE}; shift 2;; --) shift; break;; esac done if [ $# -ne 0 ]; then echo $usage exit 65 # ==> Modifié de l'"exit 2" pour se conformer avec le standard. fi if [ x${verbflag} != x ]; then optionsftp="${optionsftp} -v" fi if [ x${hotedistant} = x ]; then hotedistant=prep.ai.mit.edu # ==> A réécrire pour utiliser votre site ftp favori. fi echo quit >> ${FICHIER_TEMPORAIRE} # ==> Toutes les commandes sont sauvegardées dans fichier_temporaire. ftp ${optionsftp} ${hotedistant} < ${FICHIER_TEMPORAIRE} # ==> Maintenant, exécution par ftp de toutes les commandes contenues dans le # ==> fichier fichier_temporaire. rm -f ${FICHIER_TEMPORAIRE} # ==> Enfin, fichier_temporaire est supprimé (vous pouvez souhaiter le copier # ==> dans un journal). # ==> Exercices: # ==> --------- # ==> 1) Ajouter une vérification d'erreurs. # ==> 2) Ajouter des tas de trucs.

+

Antek Sawicki a contribué avec le script suivant, qui fait une utilisation très intelligente des opérateurs de substitution de paramètres discutés dans la .

password: Générer des mots de passe aléatoires de 8 caractères
#!/bin/bash # Pourrait nécessiter d'être appelé avec un #!/bin/bash2 sur les anciennes #+ machines. # # Générateur de mots de passe aléatoires pour bash 2.x #+ par Antek Sawicki tenox@tenox.tc, # qui a généreusement permis à l'auteur de ce document de l'utiliser ici. # # ==> Commentaires ajoutés par l'auteur du document ==> MATRICE="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" LONGUEUR="8" # ==> Modification possible de 'LONGUEUR' pour des mots de passe plus longs. while [ "${n:=1}" -le "$LONGUEUR" ] # ==> Rappelez-vous que := est l'opérateur de "substitution par défaut". # ==> Donc, si 'n' n'a pas été initialisé, l'initialiser à 1. do PASS="$PASS${MATRICE:$(($RANDOM%${#MATRICE})):1}" # ==> Très intelligent, pratiquement trop astucieux. # ==> Commençons par le plus intégré... # ==> ${#MATRICE} renvoie la longueur du tableau MATRICE. # ==> $RANDOM%${#MATRICE} renvoie un nombre aléatoire entre 1 et la # ==> longueur de MATRICE - 1. # ==> ${MATRICE:$(($RANDOM%${#MATRICE})):1} # ==> renvoie l'expansion de MATRICE à une position aléatoire, par # ==> longueur 1. # ==> Voir la substitution de paramètres {var:pos:len}, section 3.3.1 # ==> et les exemples suivants. # ==> PASS=... copie simplement ce résultat dans PASS (concaténation). # ==> Pour mieux visualiser ceci, décommentez la ligne suivante # ==> echo "$PASS" # ==> pour voir la construction de PASS, un caractère à la fois, # ==> à chaque itération de la boucle. let n+=1 # ==> Incrémentez 'n' pour le prochain tour. done echo "$PASS" # ==> Ou, redirigez le fichier, comme voulu. exit 0

+

James R. Van Zandt a contribué avec ce script, qui utilise les tubes nommés et, ce sont ses mots, "really exercises quoting and escaping" .

fifo: Faire des sauvegardes journalières, en utilisant des tubes nommés
#!/bin/bash # ==> Script de James R. Van Zandt, et utilisé ici avec sa permission. # ==> Commentaires ajoutés par l'auteur de ce document. ICI=`uname -n` # ==> nom d'hôte LA_BAS=bilbo echo "début de la sauvegarde distante vers $LA_BAS à `date +%r`" # ==> `date +%r` renvoie l'heure en un format sur 12 heures, par exempe # ==> "08:08:34 PM". # Assurez-vous que /pipe est réellement un tube et non pas un fichier #+ standard. rm -rf /tube mkfifo /tube # ==> Crée un fichier "tube nommé", nommé "/tube". # ==> 'su xyz' lance les commandes en tant qu'utilisateur "xyz". # ==> 'ssh' appele le shell sécurisé (client de connexion à distance). su xyz -c "ssh $LA_BAS \"cat >/home/xyz/sauve/${ICI}-jour.tar.gz\" < /tube"& cd / tar -czf - bin boot dev etc home info lib man root sbin share usr var >/tube # ==> Utilise un tube nommé, /tube, pour communiquer entre processus: # ==> 'tar/gzip' écrit dans le tube et 'ssh' lit /tube. # ==> Le résultat final est que cela sauvegarde les répertoires principaux; #+ ==> à partir de /. # ==> Quels sont les avantages d'un "tube nommé" dans cette situation, # ==> en opposition avec le "tube anonyme", avec |? # ==> Est-ce qu'un tube anonyme pourrait fonctionner ici? exit 0

+

Stephane Chazelas a contribué avec le script suivant pour démontrer que générer des nombres aléatoires ne requiert pas de tableaux.

primes: Générer des nombres aléatoires en utilisant l'opérateur modulo
#!/bin/bash # primes.sh: Génère des nombres premiers, sans utiliser des tableaux. # Script contribué par Stephane Chazelas. # Il n'utilise *pas* l'algorithme classique "Sieve of Eratosthenes", #+ mais utilise à la place la méthode plus intuitive de test de chaque nombre #+ candidat pour les facteurs (diviseurs), en utilisant l'opérateur modulo "%". LIMITE=1000 # Premiers de 2 à 1000 Premiers() { (( n = $1 + 1 )) # Va au prochain entier. shift # Prochain paramètre dans la liste. # echo "_n=$n i=$i_" if (( n == LIMITE )) then echo $* return fi for i; do # "i" est initialisé à "@", les précédentes #+ valeurs de $n. # echo "-n=$n i=$i-" (( i * i > n )) && break # Optimisation. (( n % i )) && continue # Passe les non premiers en utilisant l'opérateur #+ modulo. Premiers $n $@ # Récursion à l'intérieur de la boucle. return done Premiers $n $@ $n # Récursion à l'extérieur de la boucle. # Accumule successivement les paramètres de #+ position. # "$@" est la liste des premiers accumulés. } Premiers 1 exit 0 # Décommenter les lignes 17 et 25 pour vous aider à comprendre ce qui se passe. # Comparez la vitesse de cet algorithme de génération des nombres premiers avec # celui de "Sieve of Eratosthenes" (ex68.sh). # Exercice: Réécrivez ce script sans récursion, pour une exécution plus rapide.

+

Jordi Sanfeliu a donné sa permission pour utiliser son élégant script sur les arborescences.

tree: Afficher l'arborescence d'un répertoire
#!/bin/sh # @(#) tree 1.1 30/11/95 by Jordi Sanfeliu # email: mikaku@arrakis.es # # Version initiale : 1.0 30/11/95 # Prochaine version: 1.1 24/02/97 Maintenant avec des liens symboliques # Corrigé par : Ian Kjos, pour supporter les répertoires non dispo # email: beth13@mail.utexas.edu # # Tree est un outil pour visualiser la hiérarchie d'un répertoire # # ==> Le script 'Tree' est utilisé ici avec la permission de son auteur, Jordi Sanfeliu. # ==> Commentaires ajoutés par l'auteur de ce document. # ==> Ajout des guillemets pour les arguments. search () { for dir in `echo *` # ==> `echo *` affiche tous les fichiers du répertoire actuel sans retour à # ==> la ligne. # ==> Même effet que for dir in * # ==> mais "dir in `echo *`" ne gère pas les noms de fichiers comprenant des # ==> espaces blancs. do if [ -d "$dir" ] ; then # ==> S'il s'agit d'un répertoire (-d)... zz=0 # ==> Variable temporaire, pour garder trace du niveau de # ==> répertoire. while [ $zz != $deep ] # Conserve la trace de la boucle interne. do echo -n "| " # ==> Affiche le symbôle du connecteur vertical # ==> avec 2 espaces mais pas de retour à la ligne # ==> pour l'indentation. zz=`expr $zz + 1` # ==> Incrémente zz. done if [ -L "$dir" ] ; then # ==> Si le répertoire est un lien symbolique... echo "+---$dir" `ls -l $dir | sed 's/^.*'$dir' //'` # ==> Affiche le connecteur horizontal et affiche le nom du # ==> répertoire mais... # ==> supprime la partie date/heure. else echo "+---$dir" # ==> Affiche le symbole du connecteur # ==> horizontal et le nom du répertoire. if cd "$dir" ; then # ==> S'il peut se déplacer dans le sous-répertoire... deep=`expr $deep + 1` # ==> Incrémente la profondeur. search # avec la récursivité ;-) # ==> La fonction s'appelle elle-même. numdirs=`expr $numdirs + 1` # ==> Incrémente le compteur de # ==> répertoires. fi fi fi done cd .. # ==> Se placer un niveau au-dessus. if [ "$deep" ] ; then # ==> Si la profondeur est nulle (renvoie TRUE)... swfi=1 # ==> initialiser l'indicateur indiquant que la # ==> recherche est terminée. fi deep=`expr $deep - 1` # ==> Décrémente la profondeur. } # - Principal - if [ $# = 0 ] ; then cd `pwd` # ==> Pas d'arguments au script, alors utilise le répertoire actuel. else cd $1 # ==> Sinon, va dans le répertoire indiqué. fi echo "Répertoire initial = `pwd`" swfi=0 # ==> Indicateur de terminaison. deep=0 # ==> Profondeur de la liste. numdirs=0 zz=0 while [ "$swfi" != 1 ] # Tant que l'indicateur n'est pas initialisé do search # ==> Appelle la fonctione après avoir initialisé les variables. done echo "Nombre total de répertoires = $numdirs" exit 0 # ==> Challenge: essayez de comprendre exactement comment fonctionne ce script

+

Noah Friedman a donné sa permission pour utiliser son script contenant des fonctions sur les chaînes de caractères, qui reproduit les fonctions de manipulations de la bibliothèque C string.

string: Manipuler les chaînes de caractères comme en C
#!/bin/bash # string.bash --- émulation bash des routines de la bibliothèque string(3) # Auteur : Noah Friedman friedman@prep.ai.mit.edu # ==> Utilisé avec sa gentille permission dans ce document. # Créé le : 1992-07-01 # Dernière modification le : 1993-09-29 # Domaine public # Conversion vers la syntaxe bash v2 réalisée par Chet Ramey # Commentaire : # Code: #:docstring strcat: # Usage: strcat s1 s2 # # Strcat ajoute la valeur de la variable s2 à la variable s1. # # Exemple: # a="foo" # b="bar" # strcat a b # echo $a # => foobar # #:end docstring: ###;;;autoload ==> Chargement automatique de la fonction en commentaires. function strcat () { local s1_val s2_val s1_val=${!1} # expansion indirecte de la variable s2_val=${!2} eval "$1"=\'"${s1_val}${s2_val}"\' # ==> eval $1='${s1_val}${s2_val}' évite les problèmes, # ==> si une des variables contient un guillemet simple. } #:docstring strncat: # Usage: strncat s1 s2 $n # # Ligne strcat, mais strncat ajoute un maximum de n caractères à partir de la # valeur de la variable s2. Il en copie moins si la valeur de cette variable # est plus petite que n caractères. Echo résulte en un affichage. # # Exemple: # a=foo # b=barbaz # strncat a b 3 # echo $a # => foobar # #:end docstring: ###;;;autoload function strncat () { local s1="$1" local s2="$2" local -i n="$3" local s1_val s2_val s1_val=${!s1} # ==> expansion indirecte de variable s2_val=${!s2} if [ ${#s2_val} -gt ${n} ]; then s2_val=${s2_val:0:$n} # ==> extraction d'une sous-chaîne fi eval "$s1"=\'"${s1_val}${s2_val}"\' # ==> eval $1='${s1_val}${s2_val}' évite les problèmes # ==> si une des variables contient un guillemet simple. } #:docstring strcmp: # Usage: strcmp $s1 $s2 # # Strcmp compare ses arguments et renvoit un entier plus petit, égal ou plus # grand que zéro suivant que le chaîne s1 se classe avant, au même endroit, # après la chaîne s2. #:end docstring: ###;;;autoload function strcmp () { [ "$1" = "$2" ] && return 0 [ "${1}" '<' "${2}" ] > /dev/null && return -1 return 1 } #:docstring strncmp: # Usage: strncmp $s1 $s2 $n # # Identique à strcmp, mais fait une comparaison en examinant un maximum de n # caractères (si n est plus petit ou égal à zéro, cela revient à une égalité). #:end docstring: ###;;;autoload function strncmp () { if [ -z "${3}" -o "${3}" -le "0" ]; then return 0 fi if [ ${3} -ge ${#1} -a ${3} -ge ${#2} ]; then strcmp "$1" "$2" return $? else s1=${1:0:$3} s2=${2:0:$3} strcmp $s1 $s2 return $? fi } #:docstring strlen: # Usage: strlen s # # Strlen renvoit le nombre de caractères dans la chaîne littérale s. #:end docstring: ###;;;autoload function strlen () { eval echo "\${#${1}}" # ==> Renvoit la longueur de la valeur de la variable dont le nom est passé # ==> en argument. } #:docstring strspn: # Usage: strspn $s1 $s2 # # Strspn renvoit la longueur du segment initial maximum de la chaîne s1 qui # est entièrement composée des caractères non contenus dans la chaîne s2. #:end docstring: ###;;;autoload function strspn () { # Déconfigurer IFS permet aux espaces blancs d'être gérés comme des # caractères normaux. local IFS= local result="${1%%[!${2}]*}" echo ${#result} } #:docstring strcspn: # Usage: strcspn $s1 $s2 # # Strcspn renvoit la longueur du segment initial maximum de la chaîne s1, # consistant entièrement de caractères ne provenant pas de la chaîne s2. #:end docstring: ###;;;autoload function strcspn () { # Déconfigurer IFS permet aux espaces blances d'être gérés comme des # caractères normaux. local IFS= local result="${1%%[${2}]*}" echo ${#result} } #:docstring strstr: # Usage: strstr s1 s2 # # Strstr affiche une sous-chaîne commençant à la première occurence de la chaîne # s2 dans la chaîne s1, ou rien si s2 n'existe pas dans la chaîne. Si s2 pointe # vers une chaîne de longueur nulle, strstr affiche s1. #:end docstring: ###;;;autoload function strstr () { # si s2 pointe vers une chaîne de longueur nulle, strstr affiche s1 [ ${#2} -eq 0 ] && { echo "$1" ; return 0; } # strstr n'affiche rien si s2 n'est pas dans s1 case "$1" in *$2*) ;; *) return 1;; esac # utilise le code de correspondance de modèles pour supprimer la # correspondance et tout ce qui suit first=${1/$2*/} # ensuite supprime la première portion ne correspondant pas echo "${1##$first}" } #:docstring strtok: # Usage: strtok s1 s2 # # Strtok considère la chaîne s1 comme une séquence (nulle ou importante) de parties # de texte séparés par des blocs d'au moins un caractère à partir du caractère # séparateur de s2. Le premier appel (avec une chaîne non vide s1 spécifiée) # affiche une chaîne comprenant la première partie sur stdout. La fonction # conserve la position dans la chaîne s1 entre les différents appels pour que # les appels suivants fait avec le premier argument étant une chaîne vide # fonctionnera en suivant immédiatement la partie. De cette façon, les appels # suivants fonctionneront jusqu'à ce que plus aucune partie n'existe. La chaîne # séparateur s2 pourrait être différente d'appel en appel. Quand plus aucune # partie ne reste dans s1, une valeur vide est renvoyée. #:end docstring: ###;;;autoload function strtok () { : } #:docstring strtrunc: # Usage: strtrunc $n $s1 {$s2} {$...} # # Utilisé par beaucoup de fonctions comme strncmp pour tronquer les arguments en # vue de comparaison. # Affiche les n premiers caractères de chaque chaîne s1 s2 ... sur stdout. #:end docstring: ###;;;autoload function strtrunc () { n=$1 ; shift for z; do echo "${z:0:$n}" done } # fournit string # string.bash se termine ici. # ========================================================================== # # ==> Tout ce qui est ci-dessous a été ajouté par l'auteur du document # ==> L'utilisation suggérée de ce script est de supprimer tout ce qui se trouve # ==> en dessous et d'utiliser (comme source) ce fichier dans vos propres scripts. # strcat chaine0=one chaine1=two echo echo "Test de la fonction \"strcat\" :" echo "Chaîne originale \"chaine0\" = $chaine0" echo "\"chaine1\" = $chaine1" strcat chaine0 chaine1 echo "Nouvelle chaîne \"chaine0\" = $chaine0" echo # strlen echo echo "Test de la fonction \"strlen\" :" chaine=123456789 echo "\"chaine\" = $chaine" echo -n "Longueur de \"chaine\" = " strlen chaine echo # Exercice: # -------- # Ajoutez le code pour tester toutes les autres fonctions de chaînes ci-dessus. exit 0

+

+

Stephane Chazelas montre la programmation objet dans un script Bash.

obj-oriented: Bases de données orientées objet
#!/bin/bash # obj-oriented.sh: programmation orientée objet dans un script shell. # Script par Stephane Chazelas. person.new() # Ressemble à la déclaration d'une classe en C++. { local nom_objet=$1 nom=$2 prenom=$3 datenaissance=$4 eval "$nom_objet.set_nom() { eval \"$nom_objet.get_nom() { echo \$1 }\" }" eval "$nom_objet.set_prenom() { eval \"$nom_objet.get_prenom() { echo \$1 }\" }" eval "$nom_objet.set_datenaissance() { eval \"$nom_objet.get_datenaissance() { echo \$1 }\" eval \"$nom_objet.show_datenaissance() { echo \$(date -d \"1/1/1970 0:0:\$1 GMT\") }\" eval \"$nom_objet.get_age() { echo \$(( (\$(date +%s) - \$1) / 3600 / 24 / 365 )) }\" }" $nom_objet.set_nom $nom $nom_objet.set_prenom $prenom $nom_objet.set_datenaissance $datenaissance } echo person.new self Bozeman Bozo 101272413 # Crée une instance de "person.new" (en fait, passe les arguments à la #+ fonction). self.get_prenom # Bozo self.get_nom # Bozeman self.get_age # 28 self.get_datenaissance # 101272413 self.show_datenaissance # Sat Mar 17 20:13:33 MST 1973 echo # typeset -f # pour voir les fonctions créées (attention, cela fait défiler la page). exit 0

8. Appendice : Petit guide sur Sed et Awk

Ceci est une brève introduction aux utilitaires de traitement de texte sed et awk. Nous allons voir seulement quelques commandes basiques ici, mais cela devrait suffire pour comprendre des constructions sed et awk simples à l'intérieur de scripts shell.

sed: un éditeur de fichiers texte non interactif

awk: un langage d'examen et de traitement de motifs orienté champs avec une syntaxe C

Malgré toutes leurs différences, les deux utilitaires partagent une syntaxe d'appel similaire, utilisent tous les deux les expressions régulières, lisent tous les deux l'entrée à partir de stdin par défaut, et envoient leur sortie sur stdout. Ce sont de bons outils UNIX et ils travaillent bien ensemble. La sortie de l'un peut être envoyée via un tube vers l'autre, et leurs capacités combinées donnent aux scripts shell un peu de la puissance de Perl.

Remarque : Une différence importante entre ces utilitaires est que si les scripts shell peuvent passer des arguments facilement à sed, c'est plus compliqué avec awk. (voir Un script d'invocation autour d'un script awk et Passer une référence indirecte à awk).


Sed

Sed est un éditeur ligne non interactif. Il reçoit du texte en entrée, que ce soit à partir de stdin ou d'un fichier, réalise certaines opérations sur les lignes spécifiées de l'entrée, une ligne à la fois, puis sort le résultat vers stdout ou vers un fichier. A l'intérieur d'un script shell, sed est habituellement un des différents outils composant un tube.

Sed détermine sur quelles lignes de son entrée il va opérer à partir de l'ensemble d'adresses qui lui est passé. (68) Cette plage d'adresses est définie soit par des numéros de ligne soit par un motif à rechercher. Par exemple, 3d indique à sed qu'il doit supprimer la ligne 3 de l'entrée, et /windows/d dit à sed que vous voulez que toutes les lignes de l'entrée contenant "windows" soient supprimées.

De toutes les opérations de la boîte à outils sed, nous nous occuperons principalement des trois les plus communément utilisées. Il s'agit de printing (NdT: affichage vers stdout), deletion (NdT: suppression) et substitution (NdT: euh... substitution :).

Opérateur Nom Effet
print Affiche [la plage d'adresse spécifiée]
delete Supprime [la plage d'adresse spécifiée]
substitute Substitue motif2 à la première instance de motif1 sur une ligne
substitute Substitue motif2 par la première instance de motif1 sur une ligne, en restant dans la
transform remplace tout caractère de motif1 avec le caractère correspondant dans motif2, en restant dans la (équivalent de )
global Opère sur correspondance du motif à l'intérieur de chaque ligne d'entrée de la plage d'adresse concernée

Remarque : La substitution opère seulement sur la première instance de la correspondance d'un motif à l'intérieur de chaque ligne, sauf si l'opérateur g (global) est ajouté à la commande substitute.

A partir de la ligne de commande et dans un script shell, une opération sed peut nécessiter de mettre entre guillemets et d'utiliser certaines options.

Dans certain cas, une commande d'édition sed ne fonctionnera pas avec des guillemets simples.

Remarque : Sed utilise l'option -e pour spécifier que la chaîne suivante est une instruction ou un ensemble d'instructions. Si la chaîne ne contient qu'une seule instruction, alors cette option peut être omise.

Notation Effet
Supprime la 8è ligne de l'entrée.
Supprime toutes les lignes vides.
Supprime les lignes du début à la première ligne vide (inclue).
Affiche seulement les lignes contenant "" (avec l'option ).
Substitue "" à chaque première instance de "" trouvée dans chaque ligne d'entrée.
Substitue "" à chaque instance de "" trouvée dans chaque ligne d'entrée.
Supprime tous les espaces à la fin de toutes les lignes.
Compresse toutes les séquences consécutives de zéros en un seul zéro.
Supprime toutes les lignes contenant "" .
Supprime toutes les instances de "" , en laissant le reste de la ligne intact.

Substituer une chaîne vide (taille zéro) à une autre est équivalent à supprimer cette chaîne dans une ligne de l'entrée. Le reste de la ligne reste intact. Appliquer

s/GUI//

à la ligne

The most important parts of any application are its GUI and sound effects

donne

The most important parts of any application are its and sound effects

L'anti-slash est utilisé comme caractère de substitution pour représenter une nouvelle ligne. Dans ce cas spécial, l'expression de remplacement continue sur la ligne suivante. Cette substitution remplace les espaces débutant les lignes par un retour chariot. Le résultat final est le remplacement des indentations de paragraphes par une ligne vide entre les paragraphes.

Une plage d'adresse suivie par une ou plusieurs opérations peut nécessiter des accolades ouvrantes et fermantes, avec les retours chariot appropriés. Ceci supprime seulement la première de chaque ensemble de lignes vides consécutives. Ceci peut être utile pour espacer de manière égale un fichier texte, mais en conservant les lignes vides entre paragraphes.

Astuce : Une façon rapide de doubler les espaces dans un fichier texte est

sed G nomfichier

.

Pour des exemples illustrant l'usage de sed à l'intérieur de scripts shell, jetez un oeil sur: script d'invocation Un script d'invocation légèrement plus complexe incorrectname élimine dans le répertoire courant les fichiers dont le nom contient des caractères incorrects et des espaces blancs. rn: Un utilitaire simple pour renommer des fichiers Emuler grep dans un script utiliser column pour formatter l'affichage des répertoires behead: Supprimer les en-têtes des courriers électroniques et des nouvelles tree: Afficher l'arborescence d'un répertoire Supprimer les commentaires de programmes C Rechercher les auteurs de tous les binaires d'un répertoire Conversion de base mailformat: Formater un courrier électronique Générer des nombres aléatoires de 10 chiffres Analyse de frequence d'apparition de mot life: Jeu de la Vie Un script auto-documenté

Pour un traitement plus en profondeur de sed, vérifiez les références appropriées dans la .


Awk

Awk est un langage de manipulation de texte plein de fonctionnalités avec une syntaxe proche du C. Alors qu'il possède un ensemble impressionnant d'opérateurs et de fonctionnalités, nous n'en couvrirons que quelques-uns, les plus utiles pour l'écriture de scripts shell.

Awk casse chaque ligne d'entrée en champs. Par défaut, un champ est une chaîne de caractères consécutifs délimités par des espaces, bien qu'il existe des options pour changer le délimiteur. Awk analyse et opère sur chaque champ. Ceci rend awk idéal pour gérer des fichiers texte structurés, particulièrement des tableaux, des données organisées en ensembles cohérents, tels que des lignes et des colonnes.

Des guillemets forts (guillemets simples) et des accolades entourent les segments de code awk dans un script shell.

Nous venons juste de voir la commande awk print en action. Les seules autres fonctionnalités de awk que nous avons besoin de gérer ici sont des variables. Awk gère les variables de façon similaire aux scripts shell, quoiqu'avec un peu plus de flexibilité.

Ceci ajoute la valeur de numero_colonne au total "total" . Finalement, pour afficher "total" , il existe un bloc de commandes END, exécuté après que le script ait opéré sur toute son entrée.

Correspondant au END, il existe BEGIN, pour un bloc de code à exécuter avant que awk ne commence son travail sur son entrée.

Pour des exemples de awk à l'intérieur de scripts shell, jetez un oeil sur: Forcer une déconnexion Boucle for redirigée Supprimer les commentaires de programmes C Un script d'invocation autour d'un script awk Passer une référence indirecte à awk Utiliser export pour passer une variable à un script awk embarqué Trouver le processus associé à un PID Etat de la connexion Fileinfo: opérer sur une liste de fichiers contenue dans une variable Effacer les fichiers de façon sure Réinitialiser RANDOM Effacer un fichier par son numéro d'inode Autres moyens d'extraire des sous-chaînes Une astuce permettant de renvoyer plus d'une valeur de retour Afficher tous les utilisateurs du système

C'est tout ce que nous allons voir sur awk, mais il existe bien plus à apprendre. Voyez les références appropriées dans la .


9. Appendice : Codes de sortie ayant une signification particulière

Code de sortie Signification Exemple Commentaires
standard pour les erreurs générales let "var1 = 1/0" erreurs diverses, comme une ""
mauvaise utilisation de commandes intégrées, d'après la documentation de Bash   Rarement vue, généralement utilisation du code de sortie 1
la commande appelée ne peut s'exécuter   problème de droits ou commande non exécutable
""   problème possible avec $PATH ou erreur de frappe
argument invalide pour exit 3.14159 prend seulement des arguments de type entier compris entre 0 et 255
signal "" d'erreur fatale $PPID d'un script
$?
renvoie 137 (128 + 9)
script terminé avec Control-C   Control-C est le signal 2 d'erreur fatale, (130 = 128 + 2, voir ci-dessus)
code de sortie en dehors de la limite exit -1 prend seulement des arguments de type entier compris entre 0 et 255

D'après la table, les codes de sortie 1 - 2, 126 - 165, et 255 (69) ont une signification particulière, et devraient donc être évités pour les paramètres de sortie définis par l'utilisateur. Finir un script avec exit 127 va certainement causer une certaine confusion lors du débogage (est-ce que l'erreur est "commande introuvable" ou une erreur définie par l'utilisateur?). Néanmoins, beaucoup de scripts utilisent un exit 1 comme code de sortie générique en cas d'erreur. Le code de sortie exit 1 est utilisé dans tellement de cas d'erreur qu'il ne devrait pas ajouter d'ambiguité, mais, d'un autre côté, il ne donnera probablement pas beaucoup d'information non plus.

Il y a eu un essai de normalisation des codes de sortie(voir /usr/include/sysexits.h), mais il avait pour cible les programmeurs C et C++. Un standard similaire pour la programmation de script pourrait être approprié. L'auteur de ce document propose de restreindre les codes de sortie définis par l'utilisateur à l'intervalle 64 - 113 (en plus du code 0, en cas de succès), pour se conformer au standard C/C++. Ceci permettrait 50 codes valides, et faciliterait le débogage des scripts.

Tous les codes de sortie définis par l'utilisateur dans les exemples accompagnant ce document se conforment maintenant à ce standard, sauf dans les cas de redéfinition, comme dans Saisie avec délai.

Remarque : Lancer un $? à partir de la ligne de commande après un script shell donne des résultats cohérents avec la table ci-dessus seulement à partir de l'invite Bash ou sh. L'utilisation de cette commande dans un shell C ou tcsh peut donner d'autres valeurs dans certains cas.


10. Appendice : Une introduction détaillée sur les redirections d'entrées/sorties

écrit par Stephane Chazelas, et relu par l'auteur du document

Une commande s'attend à ce que les trois premiers descripteurs de fichier (fd) soient disponibles. Le premier, fd 0 (l'entrée standard, stdin), concerne la lecture. Les deux autres (fd 1, stdout et fd 2, stderr) concernent l'écriture.

Il existe un stdin, stdout et un stderr associatés à chaque commande.

ls 2&1

connecte temporairement le stderr de la commande ls à la même "ressource" que le stdout du shell.

Par convention, une commande lit l'entrée à partir de fd 0 (stdin), affiche sur la sortie normale, fd 1 (stdout) et sur la sortie des erreurs, fd 2 (stderr). Si un des trois fd n'est pas ouvert, vous pouvez rencontrer des problèmes:

bash$ cat /etc/passwd >&- cat: standard output: Bad file descriptor

Par exemple, lorsque xterm est lancé, il commence par s'initialiser soi-même. Avant de lancer le shell de l'utilisateur, xterm ouvre le périphérique du terminal (/dev/pts/n ou quelque chose de similaire) trois fois.

A ce moment, Bash hérite de ces trois descripteurs de fichiers et chaque commande (processus fils) lancée par Bash en hérite à leur tour, sauf quand vous redirigez la commande. La redirection signifie la réaffectation d'un des descripteurs de fichier à un autre fichier (ou tube, ou tout autre chose permise). Les descripteurs de fichiers peuvent être réaffectés (pour une commande, un groupe de commande, un sous-shell, une boucle while ou if ou case ou for...), ou globalement, pour le reste du script (en utilisant exec).

ls /dev/null

lance ls avec fd 1 connecté à /dev/null.

bash$ lsof -a -p $$ -d0,1,2 COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME bash 363 bozo 0u CHR 136,1 3 /dev/pts/1 bash 363 bozo 1u CHR 136,1 3 /dev/pts/1 bash 363 bozo 2u CHR 136,1 3 /dev/pts/1 bash$ exec 2 /dev/null bash$ lsof -a -p $$ -d0,1,2 COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME bash 371 bozo 0u CHR 136,1 3 /dev/pts/1 bash 371 bozo 1u CHR 136,1 3 /dev/pts/1 bash 371 bozo 2w CHR 1,3 120 /dev/null bash$ bash -c 'lsof -a -p $$ -d0,1,2' | cat COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME lsof 379 root 0u CHR 136,1 3 /dev/pts/1 lsof 379 root 1w FIFO 0,0 7118 pipe lsof 379 root 2u CHR 136,1 3 /dev/pts/1 bash$ echo "$(bash -c 'lsof -a -p $$ -d0,1,2' 2&1)" COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME lsof 426 root 0u CHR 136,1 3 /dev/pts/1 lsof 426 root 1w FIFO 0,0 7520 pipe lsof 426 root 2w FIFO 0,0 7520 pipe

Ceci fonctionne avec différents types de redirection.

Exercice:

Analyser le script suivant.


11. Appendice : Localisation

La localisation est une fonctionnalité non documentée de Bash.

Un script shell localisé affiche son texte dans le langage défini par la locale système. Un utilisateur Linux à Berlin, Allemagne, aura une sortie en allemand alors que son cousin à Berlin, Maryland, aura une sortie en anglais avec le même script.

Pour créer un script localisé, utilisez le modèle suivant pour écrire tous les messages pour l'utilisateur (messages d'erreur, invite, etc.).

bash$ bash -D localized.sh "Can't cd to %s." "Enter the value: "

Ceci liste tout le texte localisé. (L'option -D liste les chaînes de caractères mises entre double guillemets préfixées par un $, sans exécuter le script.)

bash$ bash --dump-po-strings localized.sh #: a:6 msgid "Can't cd to %s." msgstr "" #: a:7 msgid "Enter the value: " msgstr ""

L'option --dump-po-strings de Bash ressemble à l'option -D mais utilise le format "po" de gettext.

Maintenant, construisez un fichier langage.po pour chaque langage dans lequel le script sera traduit, en spécifiant le msgstr. Par exemple:

fr.po:

Ensuite, lancez msgfmt.

msgfmt -o localized.sh.mo fr.po

Placez le fichier résultant localized.sh.mo dans le répertoire /usr/local/share/locale/fr/LC_MESSAGES, et ajoutez les lignes suivantes au début du script:

Si un utilisateur d'un système français lance le script, il obtiendra des messages en français.

Remarque : Avec les anciennes versions de Bash ou d'autres shells, gettext avec l'option -s est obligatoire. Dans ce cas, le script devient:

Remarque :

Les variables TEXTDOMAIN et TEXTDOMAINDIR doivent être exportées dans l'environnement.

---

Cette annexe a été écrite par Stephane Chazelas.


12. Appendice : Commandes d'historique

Le shell Bash apporte des outils en ligne de commande pour éditer et manipuler l'historique des commandes d'un utilisateur. C'est principalement du confort, un moyen d'économiser des frappes de touches.

Commandes d'historique de Bash: history fc

bash$ history 1 mount /mnt/cdrom 2 cd /mnt/cdrom 3 ls ...

Variables internes associées aux commandes d'historique de Bash: $HISTCMD $HISTCONTROL $HISTIGNORE $HISTFILE $HISTFILESIZE $HISTSIZE !! !$ !# !N !-N !STRING !?STRING? ^STRING^string^

Malheureusement, les outils d'historique de Bash n'ont pas d'utilité dans un script.

bash$ ./history.sh (pas de sortie)


13. Appendice : Un exemple de fichier .bashrc

Le fichier ~/.bashrc détermine le comportement des shells interactifs. Une étude de ce fichier peut amener une meilleure compréhension de Bash.

Emmanuel Rouat a fourni le fichier .bashrc suivant, très élaboré et écrit pour un système Linux. Il accepte volontiers des commentaires de lecteur..

Etudiez le fichier avec attention, et n'hésitez pas à réutiliser certaines parties du code pour votre propre .bashrc, voire même dans vos scripts.

Exemple de fichier .bashrc
#=============================================================== # # PERSONAL $HOME/.bashrc FILE for bash-2.05 (or later) # # This file is read (normally) by interactive shells only. # Here is the place to define your aliases, functions and # other interactive features like your prompt. # # This file was designed (originally) for Solaris. # --> Modified for Linux. # This bashrc file is a bit overcrowded - remember it is just # just an example. Tailor it to your needs # #=============================================================== # --> Comments added by HOWTO author. #----------------------------------- # Source global definitions (if any) #----------------------------------- if [ -f /etc/bashrc ]; then . /etc/bashrc # --> Read /etc/bashrc, if present. fi #------------------------------------------------------------- # Automatic setting of $DISPLAY (if not set already) # This works for linux and solaris - your mileage may vary.... #------------------------------------------------------------- if [ -z ${DISPLAY:=""} ]; then DISPLAY=$(who am i) DISPLAY=${DISPLAY%%\!*} if [ -n "$DISPLAY" ]; then export DISPLAY=$DISPLAY:0.0 else export DISPLAY=":0.0" # fallback fi fi #--------------- # Some settings #--------------- set -o notify set -o noclobber set -o ignoreeof set -o nounset #set -o xtrace # useful for debuging shopt -s cdspell shopt -s cdable_vars shopt -s checkhash shopt -s checkwinsize shopt -s mailwarn shopt -s sourcepath shopt -s no_empty_cmd_completion shopt -s histappend histreedit shopt -s extglob # useful for programmable completion #----------------------- # Greeting, motd etc... #----------------------- # Define some colors first: red='\e[0;31m' RED='\e[1;31m' blue='\e[0;34m' BLUE='\e[1;34m' cyan='\e[0;36m' CYAN='\e[1;36m' NC='\e[0m' # No Color # --> Nice. Has the same effect as using "ansi.sys" in DOS. # Looks best on a black background..... echo -e "${CYAN}This is BASH ${RED}${BASH_VERSION%.*}${CYAN} - DISPLAY on ${RED}$DISPLAY${NC}\n" date if [ -x /usr/games/fortune ]; then /usr/games/fortune -s # makes our day a bit more fun.... :-) fi function _exit() # function to run upon exit of shell { echo -e "${RED}Hasta la vista, baby${NC}" } trap _exit 0 #--------------- # Shell prompt #--------------- function fastprompt() { unset PROMPT_COMMAND case $TERM in *term | rxvt ) PS1="[\h] \W > \[\033]0;[\u@\h] \w\007\]" ;; *) PS1="[\h] \W > " ;; esac } function powerprompt() { _powerprompt() { LOAD=$(uptime|sed -e "s/.*: \([^,]*\).*/\1/" -e "s/ //g") TIME=$(date +%H:%M) } PROMPT_COMMAND=_powerprompt case $TERM in *term | rxvt ) PS1="${cyan}[\$TIME \$LOAD]$NC\n[\h \#] \W > \[\033]0;[\u@\h] \w\007\]" ;; linux ) PS1="${cyan}[\$TIME - \$LOAD]$NC\n[\h \#] \w > " ;; * ) PS1="[\$TIME - \$LOAD]\n[\h \#] \w > " ;; esac } powerprompt # this is the default prompt - might be slow # If too slow, use fastprompt instead.... #=============================================================== # # ALIASES AND FUNCTIONS # # Arguably, some functions defined here are quite big # (ie 'lowercase') but my workstation has 512Meg of RAM, so ..... # If you want to make this file smaller, these functions can # be converted into scripts. # # Many functions were taken (almost) straight from the bash-2.04 # examples. # #=============================================================== #------------------- # Personnal Aliases #------------------- alias rm='rm -i' alias cp='cp -i' alias mv='mv -i' # -> Prevents accidentally clobbering files. alias h='history' alias j='jobs -l' alias r='rlogin' alias which='type -all' alias ..='cd ..' alias path='echo -e ${PATH//:/\\n}' alias print='/usr/bin/lp -o nobanner -d $LPDEST' # Assumes LPDEST is defined alias pjet='enscript -h -G -fCourier9 -d $LPDEST' # Pretty-print using enscript alias background='xv -root -quit -max -rmode 5' # put a picture in the background alias vi='vim' alias du='du -h' alias df='df -kh' # The 'ls' family (this assumes you use the GNU ls) alias ls='ls -hF --color' # add colors for filetype recognition alias lx='ls -lXB' # sort by extension alias lk='ls -lSr' # sort by size alias la='ls -Al' # show hidden files alias lr='ls -lR' # recursice ls alias lt='ls -ltr' # sort by date alias lm='ls -al |more' # pipe through 'more' alias tree='tree -Cs' # nice alternative to 'ls' # tailoring 'less' alias more='less' export PAGER=less export LESSCHARSET='latin1' export LESSOPEN='|/usr/bin/lesspipe.sh %s 2>&-' # Use this if lesspipe.sh exists export LESS='-i -N -w -z-4 -g -e -M -X -F -R -P%t?f%f \ :stdin .?pb%pb\%:?lbLine %lb:?bbByte %bb:-...' # spelling typos - highly personnal :-) alias xs='cd' alias vf='cd' alias moer='more' alias moew='more' alias kk='ll' #---------------- # a few fun ones #---------------- function xtitle () { case $TERM in *term | rxvt) echo -n -e "\033]0;$*\007" ;; *) ;; esac } # aliases... alias top='xtitle Processes on $HOST && top' alias make='xtitle Making $(basename $PWD) ; make' alias ncftp="xtitle ncFTP ; ncftp" # .. and functions function man () { xtitle The $(basename $1|tr -d .[:digit:]) manual man -a "$*" } function ll(){ ls -l "$@"| egrep "^d" ; ls -lXB "$@" 2>&-| egrep -v "^d|total "; } function xemacs() { { command xemacs -private $* 2>&- & } && disown ;} function te() # wrapper around xemacs/gnuserv { if [ "$(gnuclient -batch -eval t 2>&-)" == "t" ]; then gnuclient -q "$@"; else ( xemacs "$@" & ); fi } #----------------------------------- # File & strings related functions: #----------------------------------- function ff() { find . -name '*'$1'*' ; } # find a file function fe() { find . -name '*'$1'*' -exec $2 {} \; ; } # find a file and run $2 on it function fstr() # find a string in a set of files { if [ "$#" -gt 2 ]; then echo "Usage: fstr \"pattern\" [files] " return; fi SMSO=$(tput smso) RMSO=$(tput rmso) find . -type f -name "${2:-*}" -print | xargs grep -sin "$1" | \ sed "s/$1/$SMSO$1$RMSO/gI" } function cuttail() # cut last n lines in file, 10 by default { nlines=${2:-10} sed -n -e :a -e "1,${nlines}!{P;N;D;};N;ba" $1 } function lowercase() # move filenames to lowercase { for file ; do filename=${file##*/} case "$filename" in */*) dirname==${file%/*} ;; *) dirname=.;; esac nf=$(echo $filename | tr A-Z a-z) newname="${dirname}/${nf}" if [ "$nf" != "$filename" ]; then mv "$file" "$newname" echo "lowercase: $file --> $newname" else echo "lowercase: $file not changed." fi done } function swap() # swap 2 filenames around { local TMPFILE=tmp.$$ mv $1 $TMPFILE mv $2 $1 mv $TMPFILE $2 } #----------------------------------- # Process/system related functions: #----------------------------------- function my_ps() { ps $@ -u $USER -o pid,%cpu,%mem,bsdtime,command ; } function pp() { my_ps f | awk '!/awk/ && $0~var' var=${1:-".*"} ; } # This function is roughly the same as 'killall' on linux # but has no equivalent (that I know of) on Solaris function killps() # kill by process name { local pid pname sig="-TERM" # default signal if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then echo "Usage: killps [-SIGNAL] pattern" return; fi if [ $# = 2 ]; then sig=$1 ; fi for pid in $(my_ps| awk '!/awk/ && $0~pat { print $1 }' pat=${!#} ) ; do pname=$(my_ps | awk '$1~var { print $5 }' var=$pid ) if ask "Kill process $pid <$pname> with signal $sig?" then kill $sig $pid fi done } function my_ip() # get IP adresses { MY_IP=$(/sbin/ifconfig ppp0 | awk '/inet/ { print $2 } ' | sed -e s/addr://) MY_ISP=$(/sbin/ifconfig ppp0 | awk '/P-t-P/ { print $3 } ' | sed -e s/P-t-P://) } function ii() # get current host related info { echo -e "\nYou are logged on ${RED}$HOST" echo -e "\nAdditionnal information:$NC " ; uname -a echo -e "\n${RED}Users logged on:$NC " ; w -h echo -e "\n${RED}Current date :$NC " ; date echo -e "\n${RED}Machine stats :$NC " ; uptime echo -e "\n${RED}Memory stats :$NC " ; free my_ip 2>&- ; echo -e "\n${RED}Local IP Address :$NC" ; echo ${MY_IP:-"Not connected"} echo -e "\n${RED}ISP Address :$NC" ; echo ${MY_ISP:-"Not connected"} echo } # Misc utilities: function repeat() # repeat n times command { local i max max=$1; shift; for ((i=1; i <= max ; i++)); do # --> C-like syntax eval "$@"; done } function ask() { echo -n "$@" '[y/n] ' ; read ans case "$ans" in y*|Y*) return 0 ;; *) return 1 ;; esac } #========================================================================= # # PROGRAMMABLE COMPLETION - ONLY SINCE BASH-2.04 # (Most are taken from the bash 2.05 documentation) # You will in fact need bash-2.05 for some features # #========================================================================= if [ "${BASH_VERSION%.*}" \< "2.05" ]; then echo "You will need to upgrade to version 2.05 for programmable completion" return fi shopt -s extglob # necessary set +o nounset # otherwise some completions will fail complete -A hostname rsh rcp telnet rlogin r ftp ping disk complete -A command nohup exec eval trace gdb complete -A command command type which complete -A export printenv complete -A variable export local readonly unset complete -A enabled builtin complete -A alias alias unalias complete -A function function complete -A user su mail finger complete -A helptopic help # currently same as builtins complete -A shopt shopt complete -A stopped -P '%' bg complete -A job -P '%' fg jobs disown complete -A directory mkdir rmdir complete -A directory -o default cd complete -f -d -X '*.gz' gzip complete -f -d -X '*.bz2' bzip2 complete -f -o default -X '!*.gz' gunzip complete -f -o default -X '!*.bz2' bunzip2 complete -f -o default -X '!*.pl' perl perl5 complete -f -o default -X '!*.ps' gs ghostview ps2pdf ps2ascii complete -f -o default -X '!*.dvi' dvips dvipdf xdvi dviselect dvitype complete -f -o default -X '!*.pdf' acroread pdf2ps complete -f -o default -X '!*.+(pdf|ps)' gv complete -f -o default -X '!*.texi*' makeinfo texi2dvi texi2html texi2pdf complete -f -o default -X '!*.tex' tex latex slitex complete -f -o default -X '!*.lyx' lyx complete -f -o default -X '!*.+(jpg|gif|xpm|png|bmp)' xv gimp complete -f -o default -X '!*.mp3' mpg123 complete -f -o default -X '!*.ogg' ogg123 # This is a 'universal' completion function - it works when commands have # a so-called 'long options' mode , ie: 'ls --all' instead of 'ls -a' _universal_func () { case "$2" in -*) ;; *) return ;; esac case "$1" in \~*) eval cmd=$1 ;; *) cmd="$1" ;; esac COMPREPLY=( $("$cmd" --help | sed -e '/--/!d' -e 's/.*--\([^ ]*\).*/--\1/'| \ grep ^"$2" |sort -u) ) } complete -o default -F _universal_func ldd wget bash id info _make_targets () { local mdef makef gcmd cur prev i COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} # if prev argument is -f, return possible filename completions. # we could be a little smarter here and return matches against # `makefile Makefile *.mk', whatever exists case "$prev" in -*f) COMPREPLY=( $(compgen -f $cur ) ); return 0;; esac # if we want an option, return the possible posix options case "$cur" in -) COMPREPLY=(-e -f -i -k -n -p -q -r -S -s -t); return 0;; esac # make reads `makefile' before `Makefile' if [ -f makefile ]; then mdef=makefile elif [ -f Makefile ]; then mdef=Makefile else mdef=*.mk # local convention fi # before we scan for targets, see if a makefile name was specified # with -f for (( i=0; i < ${#COMP_WORDS[@]}; i++ )); do if [[ ${COMP_WORDS[i]} == -*f ]]; then eval makef=${COMP_WORDS[i+1]} # eval for tilde expansion break fi done [ -z "$makef" ] && makef=$mdef # if we have a partial word to complete, restrict completions to # matches of that word if [ -n "$2" ]; then gcmd='grep "^$2"' ; else gcmd=cat ; fi # if we don't want to use *.mk, we can take out the cat and use # test -f $makef and input redirection COMPREPLY=( $(cat $makef 2>/dev/null | awk 'BEGIN {FS=":"} /^[^.# ][^=]*:/ {print $1}' | tr -s ' ' '\012' | sort -u | eval $gcmd ) ) } complete -F _make_targets -X '+($*|*.[cho])' make gmake pmake _configure_func () { case "$2" in -*) ;; *) return ;; esac case "$1" in \~*) eval cmd=$1 ;; *) cmd="$1" ;; esac COMPREPLY=( $("$cmd" --help | awk '{if ($1 ~ /--.*/) print $1}' | grep ^"$2" | sort -u) ) } complete -F _configure_func configure # cvs(1) completion _cvs () { local cur prev COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} if [ $COMP_CWORD -eq 1 ] || [ "${prev:0:1}" = "-" ]; then COMPREPLY=( $( compgen -W 'add admin checkout commit diff \ export history import log rdiff release remove rtag status \ tag update' $cur )) else COMPREPLY=( $( compgen -f $cur )) fi return 0 } complete -F _cvs cvs _killall () { local cur prev COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} # get a list of processes (the first sed evaluation # takes care of swapped out processes, the second # takes care of getting the basename of the process) COMPREPLY=( $( /usr/bin/ps -u $USER -o comm | \ sed -e '1,1d' -e 's#[]\[]##g' -e 's#^.*/##'| \ awk '{if ($0 ~ /^'$cur'/) print $0}' )) return 0 } complete -F _killall killall killps # Local Variables: # mode:shell-script # sh-shell:bash # End:

14. Appendice : Convertir des fichiers Batch DOS en Scripts Shell

De nombreux programmeurs ont appris la programmation de scripts sur un PC avec DOS. Même le langage limité de fichiers batch sous DOS a permis l'écriture de scripts et d'applications assez puissantes, bien que cela nécessitait souvent des astuces assez importantes. Occasionnellement, le besoin de convertir un ancien fichier batch DOS en script shell Unix se fait encore sentir. Ce n'est généralement pas difficile car les opérateurs d'un fichier batch DOS ne sont qu'un sous-ensemble limité des équivalents en shell.

Opérateur de fichier batch Equivalent en script shell Signification
$ préfixe d'un paramètre en ligne de commande
- préfixe d'option d'une commande
/ séparateur d'un chemin de répertoire
= (égal-à) test de comparaison de chaîne de caractères
!= (non égal-à) test de comparaison de chaîne de caractères
| tube
set n'affiche pas la commande actuelle
* "" dans un nom de fichier
  redirection de fichier (écrasement)
  redirection de fichier (ajout)
  redirection de
$VAR variable d'environnement
# commentaire
! négation du test suivant
"" pour supprimer la sortie des commandes
echo echo (bien plus d'options avec Bash)
echo affiche une ligne vide
set n'affiche pas la commande(s) suivante(s)
for var in [liste]; do boucle ""
none (inutile) label
none (utiliser une fonction) saute à un autre emplacement du script
sleep pause ou attente pendant un intervalle de temps
case ou select choix ou menu
if test if
if [ -e filename ] teste si le fichier existe
if [ -z "$N" ] si le paramètre "" n'est pas présent
source ou . (opérateur point) "" un autre script
source ou . (opérateur point) "" un autre script (identique à CALL)
export assigne une valeur à une variable d'environnement
shift décalage gauche de la liste des arguments en ligne de commande
-lt ou -gt signe (d'un entier)
$? code de sortie
"" ()
périphérique imprimante (générique)
premier périphérique imprimante
premier port série

Les fichiers batch contiennent habituellement des commandes DOS. Elles doivent être remplacées par leur équivalent UNIX pour convertir un fichier batch en script shell.

Commande DOS Equivalent UNIX Effet
ln lie un fichier ou un répertoire
chmod change les permissions d'un fichier
cd change de répertoire
cd change de répertoire
clear efface l'écran
diff, comm, cmp compare des fichiers
cp copie des fichiers
Ctl-C break (signal)
Ctl-D EOF (end-of-file, fin de fichier)
rm supprime le(s) fichier(s)
rm -rf supprime le répertoire récursivement
ls -l affiche le contenu du répertoire
rm supprime le(s) file(s)
exit sort du processus courant
comm, cmp compare des fichiers
grep cherche des chaînes de caractères dans des fichiers
mkdir crée un répertoire
mkdir crée un répertoire
more affiche un fichier page par page
mv déplace
$PATH chemin vers les exécutables
mv renomme (ou déplace)
mv renomme (ou déplace)
rmdir supprime un répertoire
rmdir supprime un répertoire
sort trie un fichier
date affiche l'heure système
cat envoie le fichier vers
cp copie de fichier (étendue)

Remarque : Virtuellement, tous les opérateurs et commandes shell et UNIX ont beaucoup plus d'options et d'améliorations que leur équivalent DOS et fichier batch. Beaucoup de fichiers batch DOS reposent sur des utilitaires supplémentaires, tel que ask.com, un équivalent limité de read.

Remarque : DOS supporte un sous-ensemble très limité et incompatible de caractères d'expansion de noms de fichier, reconnaissant seulement les caractères * et ?.

Convertir un fichier batch DOS en script shell est généralement assez simple et le résultat est souvent bien meilleur que l'original.

VIEWDATA.BAT: Fichier Batch DOS
REM VIEWDATA REM INSPIRED BY AN EXAMPLE IN "DOS POWERTOOLS" REM BY PAUL SOMERSON @ECHO OFF IF !%1==! GOTO VIEWDATA REM IF NO COMMAND-LINE ARG... FIND "%1" C:\BOZO\BOOKLIST.TXT GOTO EXIT0 REM PRINT LINE WITH STRING MATCH, THEN EXIT. :VIEWDATA TYPE C:\BOZO\BOOKLIST.TXT | MORE REM SHOW ENTIRE FILE, 1 PAGE AT A TIME. :EXIT0

La conversion de ce script en est une belle amélioration.

viewdata.sh: Conversion du script shell VIEWDATA.BAT
#!/bin/bash # Conversion de VISUDONNEES.BAT en script shell. FICHIERDONNEES=/home/bozo/datafiles/book-collection.data SANSARGUMENT=1 # @ECHO OFF Commande inutile ici. if [ $# -lt "$SANSARGUMENT" ] # IF !%1==! GOTO VIEWDATA then less $FICHIERDONNEES # TYPE C:\MYDIR\BOOKLIST.TXT | MORE else grep "$1" $FICHIERDONNEES # FIND "%1" C:\MYDIR\BOOKLIST.TXT fi exit 0 # :EXIT0 # Les GOTOs, labels, smoke-and-mirrors et flimflam sont inutiles. # Le script converti est court, joli et propre, ce qu'on ne peut pas dire de # l'original.

Le site Shell Scripts on the PC de Ted Davis a un ensemble complet de tutoriels sur l'art démodé de la programmation de fichiers batch. Certaines de ses echniques ingénieuses peuvent raisonnablement être utilisées dans des scripts shell.


15. Appendice : Exercices


Analyse de scripts

Examinez le script suivant. Lancez-le, puis expliquez ce qu'il fait. Annotez le script, puis ré-écrivez-le d'une façon plus compacte et plus élégante.

---

Un lecteur a envoyé le code suivant. Il souhaitait écrire un script traçant les modifications dans le journal système, /var/log/messages. Malheureusement, le bloc de code ci-dessus se bloque et ne fait rien d'utile. Pourquoi? Corrigez-le pour qu'il fonctionne (indice: plutôt que de rediriger l'entrée standard stdin de la boucle, essayez un tube).

---

Analyser life: Jeu de la Vie, et ré-organisez-le en suivant un style simplifié et plus logique. Cherchez combien de ses variables peuvent être éliminées et essayez d'optimiser le temps d'éxécution du script.

Modifiez le script pour qu'il accepte n'importe quel fichier texte ASCII pour sa "génération" initiale. Le script lira les $ROW*$COL premiers caractères, et initialisera les occurences de voyelles comme des cellules "vivantes" . Indice: assurez-vous de remplacer les espaces dans le fichier d'entrée par des caractères 'tiret bas'.


Ecriture de scripts

Ecrivez un script pour réaliser chacune des tâches suivantes.

  • Liste des répertoires à partir du répertoire personnel de l'utilisateur
    Listez les répertoires et les sous rèpertoires à partir du répertoire personnel de l'utilisateur et sauvegardez le résultat dans un fichier. Compressez le fichier, demandez à l'utilisateur d'insérer une disquette et d'appuyer sur ENTER. Enfin, sauvegardez le fichier sur la disquette.

  • Conversion de boucles for en boucle while et en boucles until
    Convertissez les boucles for de Des boucles for simples en boucles while. Indice: stockez les données dans un tableau et parcourez les éléments du tableau.Après avoir effectué ce "gros lifting" , convertissez maintenant les boucles de l'exemple en boucles until.

  • Modification de l'espacement des lignes d'un fichier texte
    Ecrivez un script qui lit chaque ligne d'un fichier cible, puis écrit la ligne sur stdout, suivie d'une ligne vide. Ceci a pour effet de doubler l'espacement des lignes du fichier.Incluez tout le code nécessaire pour vérifier que le script obtient l'argument nécessaire en ligne de commande (un nom de fichier), et que le fichier spécifié existe.Quand le script s'exécute correctement, modifiez-le pour tripler l'espacement des lignes du fichier cible. Enfin, écrivez un script pour supprimer toutes les lignes vides du fichier cible.

  • Liste inverse
    Ecrivez un script qui s'affiche lui-même sur stdout, mais à l'envers.

  • Décompression automatique de fichiers
    A partir d'une liste de noms de fichiers donnée en entrée, cherchez sur chaque fichier cible (en analysant la sortie de la commande file) le type de compression utilisé. Puis appellez automatiquement la commande de décompression appropriée (gunzip, bunzip2, unzip, uncompress, ou autre). Si un fichier cible n'est pas compressé, affichez un message d'erreur, mais ne faites aucune autre action sur ce fichier particulier.

  • Identifiant unique
    Générez un identifiant hexadécimal à six chiffres "unique" pour votre ordinateur. N'utilisez pas la commande défectueuse hostid. Indice: md5sum /etc/passwd , puis sélectionnez les six premiers chiffres de la sortie.

  • Sauvegarde
    Archivez dans une "archive tar" (fichier *.tar.gz) tous les fichiers de votre répertoire personnel (/home/votre-nom) qui ont été modifiés dans les dernières 24 heures. Indice: utilisez find.

  • Premiers
    Afficher (sur stdout) tous les nombres premiers entre 60000 et 63000. La sortie doit être joliment formatée en colonnes (indice: utilisez printf).

  • Numéros de loterie
    Un type de loterie implique de choisir cinq numéros différents entre 1 et 50. Ecrivez un script générant cinq numéros pseudo-aléatoires dans cet intervalle sans doublons. Le script donnera la possibilité d'afficher les nombres sur stdout ou de les sauvegarder dans un fichier, avec la date et l'heure où cet ensemble de nombres a été généré.

  • Gestion de l'espace disque
    Listez, un par un, tous les fichiers faisant plus de 100Ko dans l'arborescence du répertoire /home/utilisateur. Donnez à l'utilisateur la possibilité de supprimer ou de compresser le fichier, puis continuez en affichant le suivant. Ecrivez un fichier journal avec le nom de tous les fichiers supprimés et l'heure de leur suppression.

  • Suppression sécurisée
    Ecrivez, en tant que script, une commande de suppression "sécurisée" , srm.sh. Les fichiers dont les noms sont passés en argument sur la ligne de commande de ce script ne sont pas supprimés mais compressés s'ils ne le sont pas déjà (utilisez file pour le vérifier), puis déplacés dans un répertoire /home/utilisateur/poubelle. Lors de son appel, le script vérifie s'il existe des fichiers ayant plus de 48 heures dans ce répertoire "poubelle" et les supprime.

  • Faire la monnaie
    Quel est le moyen le plus efficace de faire la monnaie sur 1,68 euros en utilisant seulement les pièces en circulation courante (jusqu'à 50 centimes)? Ce sont trois pièces de 50 centimes, une de 10, une de 5 et trois de un.A partir d'une entrée arbitraire en ligne de commande en euros et centimes ($*.??), calculez la monnaie en utilisant le plus petit nombre de pièces. Si la monnaie de votre pays n'est pas l'euro, vous pouvez utiliser votre monnaie locale à la place. Le script devra analyser l'entrèe en ligne de commande, puis la modifier en multiple de la plus petite unité monétaire (centime ou autre). Indice: jetez un oeil sur Convertire des nombres en chiffres romains.

  • Equations quadratiques
    Résolvez une équation "quadratique" de la forme Ax^2 + Bx + C = 0. Créez un script qui prend comme arguments les coefficients
  • A
  • ,
  • B
  • et
  • C
  • , et renvoie les solutions avec jusqu'à 4 chiffres après la virgule.Indice: envoyez les coefficients via un tube à bc, en utilisant la formule bien connue x = ( -B +/- sqrt( B^2 - 4AC ) ) / 2A.

  • Somme des nombres correspondants
    Trouvez la somme de tous les nombres de cinq chiffres (dans l'intervalle 10000-99999) contenant exactement deux des chiffres de l'ensemble suivant: { 4, 5, 6 }. Ils peuvent se répéter à l'intérieur du même nombre, et, si c'est le cas, ils sont comptés une fois à chaque occurence. Quelques exemples de nombres correspondants: 42057, 74638 et 89515.

  • Nombres porte-bonheur
    Un "nombre porte-bonheur" est un de ces nombres dont les chiffres pris individuellements additionnés valent 7. Par exemple, 62431 est un "nombre porte-bonheur" (6 + 2 + 4 + 3 + 1 = 16, 1 + 6 = 7). Trouvez tous les "nombres porte-bonheur" entre 1000 et 10000.

  • Classer par ordre alphabêtique une chaîne de caractères
    Classez par ordre alphabêtique (suivant l'ordre ASCII) une chaîne de caractères arbitraire lue sur la ligne de commande.

  • Analyse
    Analysez /etc/passwd, et affichez son contenu sous la forme d'un joli tableau, facile à lire.

  • Mise en forme de l'affichage d'un fichier de données
    Certaines bases de données et tableurs utilisent des fichiers de sauvegarde avec des valeurs séparées par des virgules (CSV). D'autres applications ont souvent besoin d'analyser ces fichiers.A partir d'un fichier de données au format CSV, de la forme: Reformattez les données et affichez les sur stdout avec des colonnes disposant d'un titre et de même largeur.

  • Justification
    A partir d'une entrée texte au format ASCII provenant soit de stdin soit d'un fichier, justifiez à droite chaque ligne suivant une longueur de ligne spécifiée par l'utilisateur en ajustant l'espacement entre les mots et envoyez la sortie sur stdout.

  • Liste de diffusion
    En utilisant la commande mail, écrivez un script gérant une liste de diffusion simple. Le script envoie automatiquement par courrier électronique la lettre d'informations mensuelle de la compagnie, lue à partir d'un fichier texte spécifié aux adresses de la liste de diffusion que le script lit à partir d'un autre fichier spécifié.

  • Mots de passe
    Générez des mots de passe de huit caractères pseudo-aléatoires, en utilisant les caractères contenus dans les intervalles [0-9], [A-Z], [a-z]. Chaque mot de passe doit contenir au moins deux chiffres.

  • Journal des accès aux fichiers
    Tracez tous les accès aux fichiers dans /etc sur une journée. Ce journal devra inclure le nom du fichier, le nom de l'utilisateur, et l'heure d'accès. Il doit aussi être indiqué si le fichier est modifié. Ecrivez cette information en enregistrements bien mis en forme dans un fichier journal.

  • Suppression des commentaires
    Supprimez tous les commentaires d'un script shell dont le nom est spécifié en ligne de commande. Notez que la "ligne #!" ne doit pas subir le même traitement.

  • Conversion HTML
    Convertissez un fichier texte donné en HTML. Ce script non interactif insère automatiquement toutes les balises HTML appropriées dans un fichier spécifié en argument.

  • Suppression des balises HTML
    Supprimez toutes les balises HTML d'un fichier, puis reformattez le en lignes de 60 à 75 caractères de longueur. Réajustez les espacementss de paragraphes et de blocs de façon appropriée, et convertissez les tables HTML en leur équivalent texte approximatif.

  • Conversion XML
    Convertissez un fichier XML à la fois en HTML et en texte.

  • Chasse aux spammeurs
    Ecrivez un script qui analyse un courrier électronique spam en faisant des recherches DNS à partir de l'adresse IP spécifiée dans les en-têtes pour identifier les hôtes relais ainsi que le fournisseur d'accès internet (FAI) d'origine. Le script renverra le message de spam non modifié aux FAI responsables. Bien sûr, il sera nécessaire de filtrer les adresses IP de votre propre FAI, si vous ne voulez pas vous plaindre de vous-même. Si nécessaire, utilisez les commandes d'analyse réseau appropriées.

  • Code Morse
    Convertissez un fichier texte en code Morse. Chaque caractère du fichier texte sera représenté par le code Morse correspondant, un ensemble de points et de tirets bas, séparé par un espace blanc du suivant. Par exemple "script" === "... _._. ._. .. .__. _" .

  • Dump Hexa
    Faites une sortie hexa(décimale) d'un fichier binaire donné en argument. La sortie devra être en colonnes, le premier champ indiquant l'adresse, chacun des huit champs suivants un nombre hexa de quatre octets, et le dernier champ l'équivalent ASCII des huit champs précédents.

  • Emulation d'un registre à décalage
    En s'inspirant de Emuler une pile, écrivez un script qui émule un registre à décalage de 64 bits par un tableau. Implémentez des fonctions pour charger le registre, décaler à gauche et décaler à droite. Enfin, écrivez une fonction qui interprète le contenu du registre comme huit caractères ASCII sur 8 bits.

  • Déterminant
    Résolvez un déterminant 4 x 4.

  • Mots cachés
    Ecrivez un générateur de puzzle de "recherche de mots" , un script qui cache dix mots d'entrée dans une matrice de 10 x 10 lettres choisies au hasard. Les mots peuvent être cachés en horizontal, en vertical et en diagonale.

  • Anagramme
    Faites un anagramme des quatre lettres d'entrée. Par exemple, les anagrammes de word sont: do or rod row word. Vous pouvez utiliser /usr/share/dict/linux.words comme liste de référence.

  • Index 'brouillard'
    L' "index brouillard" d'un passage de texte permet d'estimer sa difficulté de lecture, par un nombre correspondant grossièrement à un niveau d'école. Par exemple, un passage d'index 12 devrait être compréhensible par tout personne ayant plus de 12 ans d'école.La version 'Gunning' de cet index utilise l'algorithme suivant.Choisissez une section de texte d'au moins 100 mots de longueur. Comptez le nombre de phrases (une portion d'une phrase tronquée par les limites de la section de texte compte pour un). Trouvez le nombre moyen de mots par phrase.MOY_MOT_PHRASE = TOTAL_MOTS / PHRASES Comptez le nombre de mots "difficiles" dans le segment -- ceux contenant au moins trois syllabes. Divisez cette quantité par le nombre total de mots pour obtenir la proportion de mots difficiles.PRO_MOTS_DIFFIC = MOTS_LONGS / TOTAL_MOTS L'index 'Gunning' est la somme des deux quantités ci-dessus, multiplié par 0,4, arrondie à l'entier le plus proche.G_FOG_INDEX = int ( 0.4 * ( MOY_MOT_PHRASE + PRO_MOTS_DIFFIC ) ) L'étape 4 est de loin la partie la plus difficile de cet exercice. Il existe différents algorithmes pour estimer le nombre de syllabes dans un mot. Un moyen empirique consiste à considérer le nombre de lettres dans un mot et le mélange voyelles - consonnes.Une stricte interprétation de l'index 'Gunning' ne compte pas les mots composés et les noms propres comme des mots "difficiles" , mais cela compliquerait considérablement le script.

  • Calculer PI en utilisant l'aiguille de Buffon
    Le mathématicien français du 18è siècle Buffon a conçu une expérimentation nouvelle. Lâchez de manière répétée une aiguille de longueur "n" sur un sol en bois composé de planches longues et étroites. Les trous séparant les planches de largeur égale sont à une distance fixe "d" les uns des autres. Gardez une trace du nombre de lâchés et du nombre de fois où l'aiguille fait une intersection avec un trou sur le sol. Le ratio de ces deux nombres se trouve être un multiple fractionnel de PI. Dans l'esprit de l'Calculer PI, écrivez un script qui lance une simulation de Monte Carlo de l'aiguille de Buffon. Pour simplifier le problème, initialisez la longueur de l'aiguille à la distance entre les trous, n = d.Indice: il existe en fait deux variables critiques: la distance du centre de l'aiguille au trou le plus proche, et l'angle formé par l'aiguille et le trou. Vous pouvez utiliser bc pour réaliser les calculs.

  • Cryptage Playfair
    Implémentez le cryptage Playfair (Wheatstone) dans un script.Le cryptage Playfair crypte le texte par substitution de chaque "diagramme" de deux lettres (groupe). Traditionnellement, on utiliserait un carré de cinq lettres sur cinq composant un alphabet pour le cryptage et le décriptage.Ce script aura trois sections principalesGénération du "carré de clé" , basé sur un mot-clé entré par l'utilisateur. Cryptage d'un message "texte" . Décryptage du texte crypté. Ce script utilisera de façon poussée les tableaux et les fonctions.

--

Merci de ne pas envoyer à l'auteur vos solutions pour ces exercices. Il existe de bien meilleures façons de l'impressionner avec votre intelligence, comme par exemple l'envoi de corrections de bogues et des suggestions pour améliorer ce livre.


16. Appendice : Droits d'utilisation

IMPORTANT:Le texte ci-dessous est la version française de la mise en garde de ce document. Seule la version originale de cette mise en garde, présentée dans la section suivante, fait foi.

Le "Guide avancé d'écriture des scripts Bash" est sous le copyright, (c) 2000, de Mendel Cooper. Ce document peut seulement être distribué suivant les termes et conditions précisés dans la "Open Publication License" (version 1.0 ou ultérieure), http://www.opencontent.org/openpub/. Les options suivantes de la licence s'appliquent aussi.

En résumé, vous pouvez distribuer librement ce livre dans sa forme électronique non modifiée. Vous devez obtenir la permission de l'auteur pour distribuer une version modifiée ou un travail qui en découle. Le but de cette restriction est de préserver l'intégrité artistique de ce document et de prévenir les "évolutions parallèles" .

Ce sont des termes très libéraux, et il ne doivent pas empêcher une diffusion ou utilisation légitime de ce livre. L'auteur encourage particulièrement l'utilisation de ce livre dans un but éducatif.

Les droits d'impression commerciale de ce livre sont disponibles. Merci de contacter l'auteur si vous êtes intéressé.

L'auteur a créé ce livre dans l'esprit du LDP Manifesto.

---

Hyun Jin Cha a réalisé une traduction koréenne de la version 1.0.11 de ce livre. Des traductions espagnole, portuguaise, française, allemande et chinoise sont en cours. Si vous souhaitez traduire ce document dans une autre langue, n'hésitez pas à le faire, suivant les termes indiqués ci-dessus. L'auteur souhaite être averti de tels efforts.


17. Appendice : Copyright

IMPORTANT:Le texte ci-dessous est la mise en garde de ce document. Ce texte fait foi.

The "Advanced Bash-Scripting Guide" is copyright, (c) 2000, by Mendel Cooper. This document may only be distributed subject to the terms and conditions set forth in the Open Publication License (version 1.0 or later), http://www.opencontent.org/openpub/. The following license options also apply.

Essentially, you may freely distribute this book in unaltered electronic form. You must obtain the author's permission to distribute a substantially modified version or derivative work. The purpose of this restriction is to preserve the artistic integrity of this document and to prevent "forking" .

These are very liberal terms, and they should not hinder any legitimate distribution or use of this book. The author especially encourages the use of this book for instructional purposes.

The commercial print rights to this book are available. Please contact the author if interested.

The author produced this book in a manner consistent with the spirit of the LDP Manifesto.

---

Hyun Jin Cha has done a Korean translation of version 1.0.11 of this book. Spanish, Portuguese, French, German, and Chinese translations are underway. If you wish to translate this document into another language, please feel free to do so, subject to the terms stated above. The author wishes to be notified of such efforts.


(1)Ils sont connus sous le nom de commandes intégrées, c'est-à-dire des fonctionnalités internes au shell.
(2)Beaucoup de fonctionnalités de ksh88, et même quelques unes de la version mise à jour ksh93, ont été intégré à Bash.
(3)Par convention, les scripts shell écrits par l'utilisateur, compatibles avec le shell Bourne, prennent l'extension .sh à leur nom. Les scripts système, tels que ceux trouvés dans /etc/rc.d, ne suivent pas cette ligne de conduite.
(4)Certains systèmes UNIX (ceux basés sur 4.2BSD) prennent un nombre magique sur quatre octets, réclamant un espace après le !, #! /bin/sh.
(5) La ligne #! d'un script shell est la première chose que l'interpréteur de commande (sh ou bash) voit. Comme cette ligne commence avec un #, il sera correctement interprété en tant que commentaire lorsque l'interpréteur de commandes exécutera finalement le script. La ligne a déjà été utilisé pour appeler l'interpréteur de commandes. En fait, si le script inclut une ligne #! supplémentaire, alors bash l'interprètera comme un commentaire. #!/bin/bash echo "Partie 1 du script." a=1 #!/bin/bash # Ceci ne lance *pas* un nouveau script. echo "Partie 2 du script." echo $a # Valeur de $a est toujours 1.
(6) Ceci permet des astuces très mignonnes. #!/bin/rm # Script se supprimant lui-même. # Rien de plus ne semble se produire lorsque vous lancez ceci... si on enlève #+ le fait que le fichier disparait. QUOIQUECESOIT=65 echo "Cette ligne ne s'affichera jamais." exit $QUOIQUECESOIT # Importe peu. Le script ne se terminera pas ici. De la même manière, essayer de lancer un fichier README avec un #!/bin/more après l'avoir rendu exécutable. Le résultat est un fichier de documentation s'affichant lui-même.
(7)Portable Operating System Interface, an attempt to standardize UNIX-like OSes (NdT: interface de systèmes d'exploitation portables, un essai pour standardiser les UNIX).
(8)Attention : appeler un script Bash avec sh nom_script désactive les extensions spécifiques à Bash, et donc le script peut ne pas fonctionner.
(9)Pour pouvoir être lancé, un script a besoin du droit de lecture en plus de celui d'exécution, car le shell a besoin de le lire.
(10)Pourquoi ne pas simplement appeler le script avec nom_script ? Si le répertoire où vous vous trouvez ($PWD) est déjà celui où se trouve nom_script, pourquoi cela ne fonctionne-t'il pas? Cela échoue parce que, pour des raisons de sécurité, le répertoire courant, ., n'est pas inclus dans le $PATH de l'utilisateur. Il est donc nécessaire d'appeler le script de façon explicite dans le répertoire courant avec ./nom_script.
(11)Le shell fait l'expansion des accolades. La commande elle-même agit sur le résultat de cette expansion.
(12)Exception : un bloc de code entre accolades dans un tube peut être lancé comme sous-shell. ls | { read ligne1; read ligne2; } # Erreur. Le bloc de code entre accolades tourne comme un sous-shell, # donc la sortie de "ls" ne peut être passée aux variables de ce bloc. echo "La première ligne est $ligne1; la seconde ligne est $ligne2" # Ne fonctionnera pas. # Merci, S.C.
(13)Le processus appelant le script affecte le paramètre $0. Par convention, ce paramètre est le nom du script. Voir la page man d'execv.
(14)Ici, la séparation de mots signifie que la chaîne de caractères est divisée en un certain nombre d'arguments séparés, un par mot.
(15)Faites attention que les binaires suid peuvent apporter des failles de sécurité et que l'option suid n'a pas d'effet sur les script shell.
(16)Sur les systèmes UNIX modernes, ce droit n'est plus utilisé sur les fichiers, mais seulement sur les répertoires.
(17)Comme S.C. l'a indiqué, dans un test composé, mettre la variable chaîne de caractères entre quotes pourrait ne pas suffire. [ -n "$string" -o "$a" = "$b" ] peut causer une erreur avec certaines versions de Bash si $string est vide. La façon la plus sûre est d'ajouter un caractère supplémentaire aux variables potentiellement vides, [ "x$string" != x -o "x$a" = "x$b" ] (les x sont annulés).
(18)Le pid du script en cours est $$, bien sûr.
(19)Les mots argument et paramètre sont souvent utilisés sans distinction. Dans le contexte de ce document, ils ont exactement la même signification, celle d'une variable passée à un script ou à une fonction.
(20)Ceci s'applique soit aux arguments en ligne de commande soit aux paramètres passés à une fonction.
(21) Si $parametre est nul dans un script non interactif, il se terminera avec un code de retour 127 (le code d'erreur de Bash pour commande introuvable).
(22)Ce sont des commandes intégrées du shell, alors que les autres commandes de boucle, telles que while et case, sont des mots clés.
(23)Une exception à ceci est la commande time, listée dans la documentation Bash officielle en tant que mot clé.
(24)Une option est un argument agissant comme un indicateur, changeant les comportements du script de façon binaire. L'argument associé avec une option particulière indique le comportement que l'option active ou désactive.
(25) Le source C pour un certain nombre de commandes intégrées chargeables est disponible typiquement dans le répertoire /usr/share/doc/bash-?.??/functions. Notez que l'option -f d'enable n'est pas transportable sur tous les systèmes.
(26)Le même effet qu'autoload peut être réalisé avec typeset -fu.
(27)Il s'agit de fichiers dont le nom commence par un point, par exemple ~/.Xdefaults. De tels noms de fichiers ne sont pas affichés lors d'un ls, et ne risquent donc pas d'être effacés accidententellement par une commande rm -rf *. Ces fichiers sont utilisés habituellement en tant que fichiers de configuration situés dans le répertoire principal d'un utilisateur.
(28)Ce n'est vrai que pour la version GNU de tr, pas pour les versions se trouvant dans les systèmes UNIX commerciaux.
(29) Un tar czvf archive_name.tar.gz * incluera les fichiers commençant par un point pour les répertoires compris dans le répertoire courant. C'est une fonctionnalité non documentée de GNU tar.
(30)C'est un système de chiffrement symétrique de bloc, employé pour crypter des fichiers sur un seul système ou sur un réseau local, par opposition à la classe de chiffrement publique, dont pgp est un exemple bien connu.
(31) Un démon est un processus en tâche de fond non attaché à une session terminal. Les démons réalisent des services désignés soit à des moments précis soit en étant enclenchés par certains événements. Le mot démon signifie fantôme en grec, et il y a certainement quelque chose de mystérieux, pratiquement surnaturel, sur la façon dont les démons UNIX travaillent silencieusement derrière la scène, réalisant leurs différentes tâches.
(32)Ces scripts sont inspirés de ceux trouvés dans la distribution debian
(33)La file d'impression est l'ensemble des documents en attente d'impression.
(34)Pour une excellente vue d'ensemble du sujet, lire l'article de Andy Vaught Introduction to Named Pipes ( Introduction aux tubes nommés ) dans le numéro de septembre 1997 du of Linux Journal.
(35)EBCDIC (prononcer ebb-sid-ic) est l'acronyme de Extended Binary Coded Decimal Interchange Code. C'est un vieux format de données d'IBM qui n'a plus cours aujourd'hui. Une utilisation étrange de l'option conv=ebcdic est l'encodage simple (mais pas très sécurisé) de fichiers textes. cat $file | dd conv=swab,ebcdic > $file_encrypted # Encode (baragouin). # on peut ajouter l'option swab pour obscurcir un peu plus cat $file_encrypted | dd conv=swab,ascii > $file_plaintext # Decode.
(36)Une macro est une constante symbôlique qui peut être substituée par une simple chaine de caractères ou par une operation sur une série d'arguements.
(37)C'est le cas pour les machines Linux ou Unix disposant d'un système de gestion de quotas disque.
(38)La commande userdel échouera si l'utilisateur en cours de suppression est connecté à ce moment.
(39)Pour plus de détails sur la gravure de CDR, voir l'article d'Alex Withers, Créer des CDs, dans le numéro d'octobre 1999 du Linux Journal.
(40)L'option -c de mke2fs demande aussi une vérification des blocs défectueux.
(41)Les opérateurs de systèmes Linux simple utilisateur préfèrent généralement quelque chose de plus simple pour leur sauvegarde, comme tar.
(42)NAND est l'opérateur logique not-and. Son effet est similaire à la soustraction.
(43)Dans le cadre des substitutions de commande, une commande peut être une commande système externe, une commande intégrée du shell voire même une fonction d'un script.
(44)Un descripteur de fichier est simplement un numéro que le système d'exploitation affecte à un fichier ouvert pour garder sa trace. Considérez cela comme une version simplifiée d'un pointeur de fichier. C'est analogue à un handle vers un fichier en C.
(45)Utiliser le descripteur de fichier 5 pourrait causer des problèmes. Lorsque Bash crée un processus fils, par exemple avec exec, le fils hérite de fd 5 (voir le courrier électronique archivé de Chet Ramey, SUBJECT: RE: File descriptor 5 is held open, NdT: Le descripteur de fichier est laissé ouvert). Il est plus raisonnable de laisser ce descripteur tranquille.
(46)La plus simple forme d'expression rationnelle est un caractère conservant sa signification littérale, ne contenant pas de méta caractères.
(47)Comme sed, awk et grep travaillent ligne par ligne, il n'y aura habituellement pas de retour à la ligne à chercher. Dans les cas où il existerait un retour à la ligne dans une expression à plusieurs lignes, le point correspondra au retour à la ligne. #!/bin/bash sed -e 'N;s/.*/[&]/' EOF # Document en ligne ligne1 ligne2 EOF # SORTIE: # [ligne1 # ligne2] echo awk '{ $0=$1 "\n" $2; if (/ligne.1/) {print}}' EOF ligne 1 ligne 2 EOF # SORTIE: # ligne # 1 # Merci, S.C. exit 0
(48) L'expansion de noms de fichiers peut faire des correspondances avec les fichiers commençant par un point, mais seulement si le modèle inclut spécifiquement le point. ~/[.]bashrc # N'étendra pas en ~/.bashrc ~/?bashrc # Là non plus. # Les caractères joker et autres méta caractères ne s'étendront # pas en un point lors d'un remplacement. ~/.[b]ashrc # Sera étendu en ~./bashrc ~/.ba?hrc # Ici ausi. ~/.bashr* # De même. # Activer l'option "dotglob" désactive ceci. # Merci, S.C.
(49)Ceci a le même effet qu'un tube nommé (fichier temporaire), et, en fait, les tubes nommés étaient autrefois utilisés dans les substitutions de processus.
(50)Les références de variables indirectes (voir ) apportent une espèce de mécanisme peu pratique pour passer des pointeurs aux fonctions. #!/bin/bash ITERATIONS=3 # Combien de fois obtenir une entrée. icompteur=1 ma_lecture () { # Appeler: ma_lecture nomvariable, # Affiche la précédente valeur entre crochets comme valeur par défaut, # et demande une nouvelle valeur. local var_locale echo -n "Entrez une valeur " eval 'echo -n "[$'$1'] "' # Précédente valeurref-pa. read var_locale [ -n "$var_locale" ] && eval $1=\$var_locale # "liste-ET": si "var_locale" alors l'initialisez à "$1". } echo while [ "$icompteur" -le "$ITERATIONS" ] do ma_lecture var echo "Entrée #$icompteur = $var" let "icompteur += 1" echo done # Merci à Stephane Chazelas pour nous avoir apporté cet exemple instructif. exit 0
(51)La commande return est une commande intégrée Bash.
(52) Herbert Mayer définit la récursion comme ...l'expression d'un algorithme utilisant une version plus simple de ce même algorithme... Une fonction récursive s'appelle elle-même.
(53)Trop de niveaux de récursion pourrait arrêter brutalement un script avec une erreur de segmentation. #!/bin/bash fonction_recursive () { (( $1 < $2 )) && f $(( $1 + 1 )) $2; # Aussi longtemps que le premier paramètres est plus petit que le second, #+ incrémente le premier et fait une récursion. } fonction_recursive 1 50000 # Récursion sur 50.000 niveaux! # Erreur de segmentation, bien sûr. # Une récursion d'une telle profondeur peut même arrêter un programme C avec une erreur de #+ de segmentation, suite à l' utilisation de toute la mémoire allouée à la #+ pile. # Merci, S.C. exit 0 # Ce script ne finira par normalement.
(54)Néanmoins, les alias semblent étendre les paramètres de position.
(55)Ceci ne s'applique pas à csh, tcsh, et d'autres shells non liés ou descendant du classique shell Bourne (sh).
(56) Les entrées dans /dev fournissent des points de montage pour les périphériques physiques et virtuels. Ces entrées utilisent très peu d'espace disque. Quelques périphériques, tels que /dev/null, /dev/zero, et /dev/urandom sont virtuels. Ce ne sont pas des périphériques physiques et ils existent seulement au niveau logiciel.
(57)Un périphérique bloc lit et/ou écrit des données par morceaux, ou blocs en constraste avec un périphérique caractère, qui accède aux données caractère par caractère. Des exemples de périphérique bloc sont un disque dur et un lecteur CD ROM. Un exemple de périphérique caractère est un clavier.
(58)Certaines commandes système, telles que procinfo, free, vmstat, lsdev, et uptime le font aussi.
(59)Le débogueur Bash de Rocky Bernstein comble légèrement ce manque.
(60)Par convention, signal 0 est affecté à exit.
(61)Ajouter le droit suid sur le script lui-même n'a aucun effet.
(62)Dans ce contexte, les nombres magiques ont une signification entièrement différente que les nombres magiques utilisés pour désigner les types de fichier.
(63)ANSI est, bien sûr, l'acronyme pour American National Standards Institute.
(64)Voir l'article de Marius van Oers, Unix Shell Scripting Malware, et aussi la référence Denning dans la bibliographie.
(65)Chet Ramey a promit des tableaux associatifs (une fonctionnalité Perl) dans une future version de Bash.
(66)C'est la technique très connue du flagellez-le à mort.
(67)Ceux qui le peuvent le font. Ceux qui ne le peuvent pas... prenez un MCSE.
(68)Si aucune plage d'adresses n'est spécifiée, il s'agit par défaut de toutes les lignes.
(69)Des valeurs de sortie en dehors de limites peuvent donner des codes de sortie imprévus. Par exemple, exit 3809 donne un code de sortie 225.

Merci de contacter l'auteur de ce document avant toute reproduction