Vous êtes sur la page 1sur 662

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

bash
Le livre de recettes

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

CARL ALBING, JP VOSSEN ET CAMERON NEWHAM

bash
Le livre de recettes

Traduction de FRANOIS CERBELLE et HERV SOULARD

ditions OREILLY 18 rue Sguier 75006 Paris france@oreilly.com http://www.oreilly.fr/

[05/03/08]

bash Le livre de recettes

oykoT iepiaT lopotsabeS nikP siraP mahnraF engoloC egdirbmaC


Elodie FRITSCH <elodie.fritsch@total.com>

Ldition originale de ce livre a t publie aux tats-Unis par OReilly Media Inc. sous le titre bash Cookbook, ISBN 0-596-52678-4. OReilly Media Inc., 2007

Couverture conue par Karen MONTGOMERY et Marcia FRIEDMAN

dition franaise : Dominique BURAUD

Les programmes figurant dans ce livre ont pour but dillustrer les sujets traits. Il nest donn aucune garantie quant leur fonctionnement une fois compils, assembls ou interprts dans le cadre dune utilisation professionnelle ou commerciale.

DITIONS OREILLY, Paris, 2007 ISBN 10 : 2-35402-083-X ISBN 13 : 978-2-35402-083-5 Version papier : http://www.oreilly.fr/catalogue/2841774473

Toute reprsentation ou reproduction, intgrale ou partielle, faite sans le consentement de lauteur, de ses ayants droit, ou ayants cause, est illicite (loi du 11 mars 1957, alina 1er de larticle 40). Cette reprsentation ou reproduction, par quelque procd que ce soit, constituerait une contrefaon sanctionne par les articles 425 et suivants du Code pnal. La loi du 11 mars 1957 autorise uniquement, aux termes des alinas 2 et 3 de larticle 41, les copies ou reproductions strictement rserves lusage priv du copiste et non destines une utilisation collective dune part et, dautre part, les analyses et les courtes citations dans un but dexemple et dillustration.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Table des matires

Prface ................................................................................................. xv 1. Dbuter avec bash .......................................................................... 1


1.1. 1.2. 1.3. 1.4. 1.5. 1.6. 1.7. 1.8. 1.9. 1.10. 1.11. 1.12. 1.13. 1.14. 1.15. 1.16. Comprendre linvite de commandes ..................................................... 4 Afficher son emplacement ...................................................................... 5 Chercher et excuter des commandes .................................................... 6 Obtenir des informations sur des fichiers .............................................. 8 Afficher tous les fichiers cachs ............................................................ 10 Protger la ligne de commande ............................................................ 12 Utiliser ou remplacer des commandes ................................................ 14 Dterminer si le shell est en mode interactif ...................................... 15 Faire de bash le shell par dfaut ........................................................... 16 Obtenir bash pour Linux ...................................................................... 18 Obtenir bash pour xBSD ....................................................................... 21 Obtenir bash pour Mac OS X ................................................................ 22 Obtenir bash pour Unix ........................................................................ 23 Obtenir bash pour Windows ................................................................ 24 Obtenir bash sans linstaller .................................................................. 25 Documentation de bash ........................................................................ 26

2. Sortie standard ............................................................................. 31


2.1. 2.2. 2.3. 2.4. 2.5. 2.6. crire la sortie sur le terminal ou une fentre .................................... crire la sortie en conservant les espaces ............................................. Mettre en forme la sortie ...................................................................... crire la sortie sans le saut de ligne ...................................................... Enregistrer la sortie dune commande ................................................. Enregistrer la sortie vers dautres fichiers ............................................ 32 33 34 35 36 37

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

vi 2.7. 2.8. 2.9. 2.10. 2.11. 2.12. 2.13. 2.14. 2.15. 2.16. 2.17. 2.18. 2.19. 2.20. 2.21. 2.22.

Table des matires Enregistrer la sortie de la commande ls ............................................... Envoyer la sortie et les erreurs vers des fichiers diffrents ................. Envoyer la sortie et les erreurs vers le mme fichier .......................... Ajouter la sortie un fichier existant .................................................. Utiliser seulement le dbut ou la fin dun fichier ............................... Sauter len-tte dun fichier .................................................................. Oublier la sortie ..................................................................................... Enregistrer ou runir la sortie de plusieurs commandes ................... Relier une sortie une entre .............................................................. Enregistrer une sortie redirige vers une entre ................................. Connecter des programmes en utilisant la sortie comme argument Placer plusieurs redirections sur la mme ligne ................................. Enregistrer la sortie lorsque la redirection semble inoprante ......... Permuter STDERR et STDOUT ........................................................... Empcher lcrasement accidentel des fichiers ................................... craser un fichier la demande ........................................................... 38 39 40 41 42 43 43 44 46 47 49 50 51 53 54 56

3. Entre standard ............................................................................ 59


3.1. 3.2. 3.3. 3.4. 3.5. 3.6. 3.7. 3.8. Lire les donnes dentre depuis un fichier ......................................... Conserver les donnes avec le script .................................................... Empcher un comportement trange dans un here document ........ Indenter un here document ................................................................. Lire lentre de lutilisateur .................................................................. Attendre une rponse Oui ou Non ...................................................... Choisir dans une liste doptions ........................................................... Demander un mot de passe .................................................................. 59 60 61 63 64 65 68 69

4. Excuter des commandes ............................................................. 71


4.1. 4.2. 4.3. 4.4. 4.5. 4.6. 4.7. 4.8. 4.9. 4.10. Lancer nimporte quel excutable ........................................................ Connatre le rsultat de lexcution dune commande ..................... Excuter plusieurs commandes la suite ............................................ Excuter plusieurs commandes la fois .............................................. Dterminer le succs dune commande ............................................... Utiliser moins dinstructions if ............................................................. Lancer de longues tches sans surveillance ......................................... Afficher des messages en cas derreur .................................................. Excuter des commandes places dans une variable .......................... Excuter tous les scripts dun rpertoire .............................................. 71 73 75 76 77 78 79 80 81 82

5. Variables du shell ......................................................................... 85


5.1. Documenter un script ........................................................................... 87 5.2. Incorporer la documentation dans les scripts ..................................... 88 5.3. Amliorer la lisibilit des scripts .......................................................... 90

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Table des matires 5.4. 5.5. 5.6. 5.7. 5.8. 5.9. 5.10. 5.11. 5.12. 5.13. 5.14. 5.15. 5.16. 5.17. 5.18. 5.19.

vii

Sparer les noms de variables du texte environnant .......................... 92 Exporter des variables ........................................................................... 92 Afficher les valeurs de toutes les variables ........................................... 94 Utiliser des paramtres dans un script ................................................. 95 Parcourir les arguments dun script ..................................................... 96 Accepter les paramtres contenant des espaces .................................. 97 Accepter des listes de paramtres contenant des espaces ................... 99 Compter les arguments ....................................................................... 101 Extraire certains arguments ................................................................ 103 Obtenir des valeurs par dfaut ........................................................... 104 Fixer des valeurs par dfaut ................................................................ 105 Utiliser null comme valeur par dfaut valide ................................... 106 Indiquer une valeur par dfaut variable ............................................ 107 Afficher un message derreur pour les paramtres non dfinis ....... 108 Modifier certaines parties dune chane ............................................ 109 Utiliser les tableaux ............................................................................. 111

6. Logique et arithmtique ............................................................. 113


6.1. 6.2. 6.3. 6.4. 6.5. 6.6. 6.7. 6.8. 6.9. 6.10. 6.11. 6.12. 6.13. 6.14. 6.15. 6.16. 6.17. 6.18. 6.19. Utiliser larithmtique dans un script ................................................ Conditionner lexcution du code ..................................................... Tester les caractristiques des fichiers ................................................ Tester plusieurs caractristiques ......................................................... Tester les caractristiques des chanes ................................................ Tester lgalit ...................................................................................... Tester avec des correspondances de motifs ........................................ Tester avec des expressions rgulires ................................................ Modifier le comportement avec des redirections ............................. Boucler avec while ............................................................................... Boucler avec read ................................................................................. Boucler avec un compteur .................................................................. Boucler avec des valeurs en virgule flottante .................................... Raliser des branchements multiples ................................................ Analyser les arguments de la ligne de commande ............................ Crer des menus simples ..................................................................... Modifier linvite des menus simples .................................................. Crer une calculatrice NPI simple ..................................................... Crer une calculatrice en ligne de commande .................................. 113 116 119 122 123 124 126 127 130 131 133 135 136 137 139 142 143 144 147

7. Outils shell intermdiaires I ...................................................... 149


7.1. 7.2. 7.3. 7.4. Rechercher une chane dans des fichiers ........................................... Garder uniquement les noms de fichiers .......................................... Obtenir une rponse vrai/faux partir dune recherche .................. Rechercher une chane en ignorant la casse ...................................... 150 151 152 154

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

viii 7.5. 7.6. 7.7. 7.8. 7.9. 7.10. 7.11. 7.12. 7.13. 7.14. 7.15. 7.16.

Table des matires Effectuer une recherche dans un tube ............................................... Rduire les rsultats de la recherche .................................................. Utiliser des motifs plus complexes dans la recherche ....................... Rechercher un numro de scu .......................................................... Rechercher dans les fichiers compresss ............................................ Garder une partie de la sortie ............................................................. Conserver une partie dune ligne de sortie ....................................... Inverser les mots de chaque ligne ...................................................... Additionner une liste de nombres ..................................................... Compter des chanes ........................................................................... Afficher les donnes sous forme dhistogramme .............................. Afficher un paragraphe de texte aprs une phrase trouve ............. 154 156 157 158 159 160 161 162 163 164 166 168

8. Outils shell intermdiaires II ..................................................... 171


8.1. 8.2. 8.3. 8.4. 8.5. 8.6. 8.7. 8.8. 8.9. 8.10. 8.11. 8.12. 8.13. 8.14. 8.15. Trier votre affichage ............................................................................ Trier les nombres ................................................................................. Trier des adresses IP ............................................................................. Couper des parties de la sortie ............................................................ Retirer les lignes identiques ................................................................ Compresser les fichiers ........................................................................ Dcompresser des fichiers ................................................................... Vrifier les rpertoires contenus dans une archive tar ..................... Substituer des caractres ..................................................................... Changer la casse des caractres ........................................................... Convertir les fichiers DOS au format Linux/Unix ............................ Supprimer les guillemets .................................................................... Compter les lignes, les mots ou les caractres dans un fichier ........ Reformater des paragraphes ............................................................... Aller plus loin avec less ....................................................................... 171 172 173 176 177 178 180 182 183 184 185 186 187 188 189

9. Rechercher des fichiers avec find, locate et slocate .................. 191


9.1. 9.2. 9.3. 9.4. 9.5. 9.6. 9.7. 9.8. 9.9. 9.10. 9.11. Retrouver tous vos fichiers MP3 ........................................................ Traiter les noms de fichiers contenant des caractres tranges ....... Acclrer le traitement des fichiers trouvs ...................................... Suivre les liens symboliques ............................................................... Retrouver des fichiers sans tenir compte de la casse ........................ Retrouver des fichiers daprs une date ............................................. Retrouver des fichiers daprs leur type ............................................. Retrouver des fichiers daprs leur taille ........................................... Retrouver des fichiers daprs leur contenu ...................................... Retrouver rapidement des fichiers ou du contenu ........................... Retrouver un fichier partir dune liste demplacements possibles ................................................................... 191 193 194 195 195 196 197 198 199 200 202

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Table des matires

ix

10. Autres fonctionnalits pour les scripts ...................................... 207


10.1. 10.2. 10.3. 10.4. 10.5. 10.6. 10.7. 10.8. Convertir un script en dmon ............................................................ Rutiliser du code ................................................................................ Utiliser des fichiers de configuration dans un script ........................ Dfinir des fonctions ........................................................................... Utiliser des fonctions : paramtres et valeur de retour .................... Intercepter les signaux ........................................................................ Redfinir des commandes avec des alias ............................................ Passer outre les alias ou les fonctions ................................................ 207 208 210 211 213 215 219 221

11. Dates et heures ........................................................................... 223


11.1. 11.2. 11.3. 11.4. 11.5. 11.6. 11.7. 11.8. Formater les dates en vue de leur affichage ...................................... Fournir une date par dfaut ............................................................... Calculer des plages de dates ................................................................ Convertir des dates et des heures en secondes depuis lorigine ....... Convertir des secondes depuis lorigine en dates et heures ............. Dterminer hier ou demain en Perl .................................................. Calculer avec des dates et des heures ................................................. Grer les fuseaux horaires, les horaires dt et les annes bissextiles ............................................................................ 11.9. Utiliser date et cron pour excuter un script au nme jour ............ 224 225 227 230 231 232 233 235 235

12. Tches utilisateur sous forme de scripts shell ........................... 239


12.1. 12.2. 12.3. 12.4. 12.5. Afficher une ligne de tirets ................................................................. Prsenter des photos dans un album ................................................. Charger votre lecteur MP3 ................................................................. Graver un CD ....................................................................................... Comparer deux documents ................................................................ 239 241 247 251 254

13. Analyses et tches similaires ..................................................... 257


13.1. 13.2. 13.3. 13.4. 13.5. 13.6. 13.7. 13.8. 13.9. 13.10. 13.11. 13.12. 13.13. Analyser les arguments dun script .................................................... Afficher ses propres messages derreur lors de lanalyse .................. Analyser du contenu HTML ............................................................... Placer la sortie dans un tableau .......................................................... Analyser la sortie avec une fonction .................................................. Analyser du texte avec read ................................................................ Analyser avec read dans un tableau ................................................... Dterminer le bon accord ................................................................... Analyser une chane caractre par caractre ..................................... Nettoyer une arborescence SVN ........................................................ Configurer une base de donnes MySQL .......................................... Extraire certains champs des donnes ............................................... Actualiser certains champs dans des fichiers de donnes ................. 257 260 262 264 265 266 267 268 269 270 271 273 276

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

x 13.14. 13.15. 13.16. 13.17. 13.18. 13.19.

Table des matires Supprimer les espaces .......................................................................... Compacter les espaces ......................................................................... Traiter des enregistrements de longueur fixe .................................... Traiter des fichiers sans sauts de ligne ................................................ Convertir un fichier de donnes au format CSV .............................. Analyser un fichier CSV ...................................................................... 277 281 283 285 287 288

14. Scripts scuriss .......................................................................... 291


14.1. 14.2. 14.3. 14.4. 14.5. 14.6. 14.7. 14.8. 14.9. 14.10. 14.11. 14.12. 14.13. 14.14. 14.15. 14.16. 14.17. 14.18. 14.19. 14.20. 14.21. 14.22. 14.23. viter les problmes de scurit classiques ........................................ viter lusurpation de linterprteur .................................................. Dfinir une variable $PATH sre ...................................................... Effacer tous les alias ............................................................................. Effacer les commandes mmorises ................................................... Interdire les fichiers core ..................................................................... Dfinir une variable $IFS sre ........................................................... Dfinir un umask sr .......................................................................... Trouver les rpertoires modifiables mentionns dans $PATH ....... Ajouter le rpertoire de travail dans $PATH .................................... Utiliser des fichiers temporaires scuriss ......................................... Valider lentre .................................................................................... Fixer les autorisations .......................................................................... Afficher les mots de passe dans la liste des processus ....................... crire des scripts setuid ou setgid ....................................................... Restreindre les utilisateurs invits ...................................................... Utiliser un environnement chroot ..................................................... Excuter un script sans avoir les privilges de root .......................... Utiliser sudo de manire plus sre ..................................................... Utiliser des mots de passe dans un script ........................................... Utiliser SSH sans mot de passe ........................................................... Restreindre les commandes SSH ........................................................ Dconnecter les sessions inactives ...................................................... 293 294 294 296 297 298 298 299 300 303 304 308 310 311 312 314 316 317 318 319 321 329 331

15. Scripts labors ........................................................................... 333


15.1. 15.2. 15.3. 15.4. 15.5. 15.6. 15.7. 15.8. 15.9. 15.10. Trouver bash de manire portable ..................................................... Dfinir une variable $PATH de type POSIX ..................................... Dvelopper des scripts shell portables ............................................... Tester des scripts sous VMware .......................................................... crire des boucles portables ................................................................ crire une commande echo portable ................................................. Dcouper lentre si ncessaire ........................................................... Afficher la sortie en hexadcimal ....................................................... Utiliser la redirection du rseau de bash ........................................... Dterminer mon adresse ..................................................................... 334 335 337 339 340 342 345 346 348 349

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Table des matires 15.11. 15.12. 15.13. 15.14. 15.15. 15.16. Obtenir lentre depuis une autre machine ...................................... Rediriger la sortie pour toute la dure dun script ........................... Contourner les erreurs liste darguments trop longue .............. Journaliser vers syslog depuis un script ............................................. Envoyer un message lectronique depuis un script .......................... Automatiser un processus plusieurs phases ...................................

xi 354 356 357 359 360 363

16. Configurer bash .......................................................................... 367


16.1. 16.2. 16.3. 16.4. 16.5. 16.6. 16.7. 16.8. 16.9. 16.10. 16.11. 16.12. 16.13. 16.14. 16.15. 16.16. 16.17. 16.18. 16.19. 16.20. Options de dmarrage de bash ........................................................... Personnaliser linvite ........................................................................... Modifier dfinitivement $PATH ........................................................ Modifier temporairement $PATH ..................................................... Dfinir $CDPATH ............................................................................... Raccourcir ou modifier des noms de commandes ............................ Adapter le comportement et lenvironnement du shell .................. Ajuster le comportement de readline en utilisant .inputrc ............. Crer son rpertoire priv dutilitaires .............................................. Utiliser les invites secondaires : $PS2, $PS3 et $PS4 ......................... Synchroniser lhistorique du shell entre des sessions ....................... Fixer les options de lhistorique du shell ........................................... Concevoir une meilleure commande cd ........................................... Crer un nouveau rpertoire et y aller avec une seule commande Aller au fond des choses ...................................................................... tendre bash avec des commandes internes chargeables ................. Amliorer la compltion programmable .......................................... Utiliser correctement les fichiers dinitialisation .............................. Crer des fichiers dinitialisation autonomes et portables ............... Commencer une configuration personnalise .................................. 368 368 376 377 383 385 386 387 389 390 392 393 396 398 399 400 406 411 414 416

17. Maintenance et tches administratives .................................... 429


17.1. 17.2. 17.3. 17.4. 17.5. 17.6. 17.7. 17.8. 17.9. 17.10. 17.11. 17.12. Renommer plusieurs fichiers .............................................................. Utiliser GNU Texinfo et Info sous Linux .......................................... Dzipper plusieurs archives ZIP ......................................................... Restaurer des sessions dconnectes avec screen .............................. Partager une unique session bash ...................................................... Enregistrer une session complte ou un traitement par lots ........... Effacer lcran lorsque vous vous dconnectez ................................. Capturer les mta-informations des fichiers pour une restauration Indexer de nombreux fichiers ............................................................ Utiliser diff et patch ............................................................................. Compter les diffrences dans des fichiers .......................................... Effacer ou renommer des fichiers dont le nom comporte des caractres spciaux ........................................................................ 429 431 432 433 435 437 438 439 440 441 446 448

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

xii 17.13. 17.14. 17.15. 17.16. 17.17. 17.18. 17.19. 17.20. 17.21. 17.22. 17.23. 17.24.

Table des matires Insrer des en-ttes dans un fichier .................................................... diter un fichier sans le dplacer ....................................................... Utiliser sudo avec un groupe de commandes ................................... Trouver les lignes prsentes dans un fichier mais pas dans un autre ........................................................................ Conserver les N objets les plus rcents .............................................. Filtrer la sortie de ps sans afficher le processus grep ........................ Dterminer si un processus sexcute ................................................ Ajouter un prfixe ou un suffixe laffichage ................................... Numroter les lignes ........................................................................... crire des squences ............................................................................ muler la commande DOS pause ...................................................... Formater les nombres ......................................................................... 449 452 454 456 460 463 464 465 467 469 471 472

18. Rduire la saisie ......................................................................... 475


18.1. 18.2. 18.3. 18.4. 18.5. 18.6. 18.7. Naviguer rapidement entre des rpertoires quelconques ................ Rpter la dernire commande .......................................................... Excuter une commande similaire .................................................... Effectuer des substitutions sur plusieurs mots .................................. Rutiliser des arguments ..................................................................... Terminer les noms automatiquement ............................................... Assurer la saisie .................................................................................... 475 477 478 479 480 481 482

19. Bourdes du dbutant .................................................................. 485


19.1. 19.2. 19.3. 19.4. 19.5. 19.6. 19.7. 19.8. 19.9. 19.10. 19.11. 19.12. 19.13. 19.14. 19.15. Oublier les autorisations dexcution ................................................ Rsoudre les erreurs Aucun fichier ou rpertoire de ce type .... Oublier que le rpertoire de travail nest pas dans $PATH ............. Nommer un script test .................................................................. Sattendre pouvoir modifier les variables exportes ...................... Oublier des guillemets lors des affectations ...................................... Oublier que la correspondance de motifs trie par ordre alphabtique ........................................................................ Oublier que les tubes crent des sous-shells ...................................... Rinitialiser le terminal ...................................................................... Supprimer des fichiers avec une variable vide .................................. Constater un comportement trange de printf ................................. Vrifier la syntaxe dun script bash .................................................... Dboguer des scripts ............................................................................ viter les erreurs commande non trouve avec les fonctions ... Confondre caractres gnriques du shell et expressions rgulires .......................................................................... 485 486 488 489 490 491 493 493 496 497 497 499 500 502 503

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Table des matires

xiii

A. Listes de rfrence ...................................................................... 505 B. Exemples fournis avec bash ....................................................... 559 C. Analyse de la ligne de commande .............................................. 569 D. Gestion de versions ..................................................................... 575 E. Compiler bash ............................................................................. 597 Index .................................................................................................. 605

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Prface

Tout systme dexploitation moderne dispose dun interprteur de commandes (un shell), voire mme de plusieurs. Certains shells sont orients ligne de commande, comme celui tudi dans ce livre, tandis que dautres offrent une interface graphique, comme lExplorateur de Windows ou le Finder du Mac. Certaines personnes utiliseront linterprteur de commande uniquement pour lancer leur application prfre et ny retourneront qu la fermeture de leur session. Cependant, les interactions entre lutilisateur et le shell sont gnralement plus frquentes et plus labores. Mieux vous connatrez votre shell, plus vous serez rapide et efficace. Que vous soyez administrateur systme, programmeur ou simple utilisateur, un script shell pourra, dans certaines occasions, vous faire gagner du temps ou faciliter la rptition dune tche importante. Mme la dfinition dun simple alias, qui modifie ou raccourcit le nom dune commande souvent utilise, peut avoir un effet substantiel. Nous allons nous intresser, entre autres, tous ces aspects. Comme cest le cas avec tout langage de programmation gnral, il existe plusieurs manires deffectuer une tche. Parfois, il nexiste quune seule bonne manire, mais, le plus souvent, vous avez le choix entre deux ou trois approches quivalentes. Celle que vous choisissez dpend de votre style personnel, de votre crativit et de votre connaissance des diffrentes commandes et techniques. Cela sapplique aussi bien nous, en tant quauteurs, qu vous, en tant que lecteur. Dans la plupart des exemples, nous proposons une seule mthode et la mettons en uvre. Parfois, nous optons pour une mthode particulire et expliquons pourquoi nous pensons quil sagit de la meilleure. Nous prsenterons, loccasion, plusieurs solutions quivalentes afin que vous puissiez choisir celle qui correspond le mieux vos besoins et votre environnement. Quelquefois, vous devrez choisir entre un code efficace trs astucieux et un code plus lisible. Nous nous tournons toujours vers le code le plus lisible. En effet, lexprience nous a appris que la lisibilit du code astucieux crit aujourdhui nest plus la mme 6 ou 18 mois et 10 projets plus tard. Vous risquez alors de passer beaucoup de temps vous interroger sur le fonctionnement de votre code. Faites-nous confiance : crivez du code clair et bien document. Vous vous en fliciterez plus tard.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

xvi

Prface

Notre public
Ce livre est destin aux utilisateurs de systmes Unix ou Linux (et Mac compris), ainsi quaux administrateurs qui peuvent se trouver chaque jour devant plusieurs systmes diffrents. Il vous aidera crer des scripts qui vous permettront den faire plus, en moins de temps, plus facilement et de manire plus cohrente que jamais. Les utilisateurs dbutants apprcieront les sections qui concernent lautomatisation des tches rptitives, les substitutions simples et la personnalisation de leur environnement afin quil soit plus agrable et peut-tre plus conforme leurs habitudes. Les utilisateurs expriments et les administrateurs trouveront de nouvelles solutions et des approches diffrentes des tches courantes. Les utilisateurs avancs seront intresss par tout un ensemble de techniques utilisables sur-le-champ, sans devoir se souvenir de tous les dtails syntaxiques. Cet ouvrage sadresse particulirement : aux nophytes dUnix ou de Linux qui ont peu de connaissances du shell, mais qui souhaitent dpasser la seule utilisation de la souris ; aux utilisateurs dUnix ou de Linux expriments et aux administrateurs systme qui recherchent des rponses rapides leurs questions concernant lcriture de scripts shell ; aux programmeurs travaillant dans un environnement Unix ou Linux (ou mme Windows) qui veulent amliorer leur productivit ; aux administrateurs systme qui dbutent sous Unix ou Linux ou qui viennent dun environnement Windows et qui doivent se former rapidement ; aux utilisateurs de Windows et les administrateurs systme expriments qui souhaitent disposer dun environnement puissant pour lexcution de scripts.

Ce livre ne sattardera pas longtemps sur les bases de lcriture de scripts shell. Pour cela, consultez Le shell bash de Cameron Newham (ditions OReilly) et Introduction aux scripts shell de Nelson H.F. Beebe et Arnold Robbins (ditions OReilly). Notre objectif est dapporter des solutions aux problmes classiques, en insistant sur la pratique et non sur la thorie. Nous esprons que ce livre vous fera gagner du temps lorsque vous rechercherez une solution ou essaierez de vous souvenir dune syntaxe. En ralit, ce sont les raisons de la rdaction de cet ouvrage. Nous voulions un livre qui propose des ides et permette de passer directement aux exemples pratiques oprationnels en cas de besoin. Ainsi, nous navons pas mmoriser les diffrences subtiles entre le shell, Perl, C, etc. Nous supposons que vous avez accs un systme Unix ou Linux (ou bien, reportezvous la recette 1.15, page 25, ou la recette 15.4, page 339) et que vous savez comment ouvrir une session, saisir des commandes basiques et utiliser un diteur de texte. Pour la majorit des exemples, vous naurez pas besoin des droits du super-utilisateur (root). En revanche, ils seront indispensables pour quelques-uns, notamment ceux qui concernent linstallation de bash.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Prface

xvii

Contenu de ce livre
Le sujet de cet ouvrage est bash, le GNU Bourne Again Shell. Il fait partie de la famille des shells Bourne, dont les autres membres sont le shell Bourne originel, sh, le shell Korn, ksh, ainsi que sa version pdksh (Public Domain Korn Shell). Bien quils ne soient pas au cur de ce livre, tout comme dash et zsh, les scripts prsents fonctionneront assez bien avec ces autres interprteurs de commandes. Vous pouvez lire cet ouvrage du dbut la fin ou louvrir et slectionner ce qui retient votre attention. Cela dit, nous esprons surtout que ds que vous aurez une question sur la manire deffectuer une tche ou besoin dun conseil, vous pourrez trouver facilement la rponse adapte (ou proche) et conomiser du temps et des efforts. La philosophie Unix tend construire des outils simples qui rpondent parfaitement des problmes prcis, puis les combiner selon les besoins. Lassociation des outils se fait gnralement au travers dun script shell car ces commandes, appeles tubes, peuvent tre longues et difficiles mmoriser ou saisir. Lorsque ce sera ncessaire, nous emploierons ces outils dans le contexte dun script shell pour runir les diffrents lments qui permettent datteindre lobjectif vis. Ce livre a t crit avec OpenOffice.org Writer sur la machine Linux ou Windows disponible un moment donn et en utilisant Subversion (voir lannexe D, Gestion de versions). Grce au format ODF (Open Document Format), de nombreux aspects de lcriture de ce livre, comme les rfrences croises et lextraction de code, ont t simplifis (voir la recette 13.17, page 285).

Logiciel GNU
bash, ainsi que dautres outils mentionns dans ce livre, font partie du projet GNU (http://www.gnu.org/). GNU est un acronyme rcursif qui signifie en anglais GNUs Not Unix (GNU nest pas Unix). Dmarr en 1984, ce projet a pour objectif de dvelopper un systme dexploitation de type Unix libre. Sans trop entrer dans les dtails, ce que lon nomme couramment Linux est, en ralit, un noyau avec un ensemble minimal de diffrents logiciels. Les outils GNU se placent autour de ce cur et bons nombres dautres logiciels peuvent tre inclus selon les distributions. Cependant, le noyau Linux lui-mme nest pas un logiciel GNU. Le projet GNU dfend lide que Linux devrait en ralit se nommer GNU/Linux . Cest galement lavis dautres acteurs et certaines distributions, notamment Debian, emploie cette dnomination. Par consquent, on peut considrer que lobjectif du projet GNU a t atteint, mme si le rsultat nest pas exclusivement GNU. De nombreux logiciels importants, en particulier bash, sont issus du projet GNU et il existe des versions GNU de pratiquement tous les outils mentionns dans ce livre. Bien que les outils fournis par ce projet soient plus riches en fonctionnalits et, en gnral, plus faciles utiliser, ils sont galement parfois lgrement diffrents. Nous reviendrons sur ce sujet la recette 15.3, page 337, mme si les fournisseurs dUnix commerciaux, entre les annes 1980 et 1990, sont largement responsables de ces diffrences. Tous ces aspects de GNU, Unix et Linux ont dj t traits en dtail (dans des livres aussi pais que celui-ci), mais nous pensons que ces remarques taient ncessaires. Pour plus dinformations sur le sujet, consultez le site http://www.gnu.org.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

xviii

Prface

Note sur les exemples de code


Les lments dexcution dun script shell sont gnralement prsents de la manire suivante :
$ ls a.out $ cong.txt def.conf fichier.txt autre.txt zebra.liste

Le premier caractre est souvent un symbole dollar ($) pour indiquer que la commande a t saisie linvite du shell bash. (Noubliez pas quelle peut tre diffrente et que vous pouvez la modifier, comme lexplique la recette 16.2, page 368.) Linvite est affiche par linterprteur de commandes. Cest vous de saisir le reste de la ligne. De manire similaire, la dernire ligne de ces exemples est souvent une invite ( nouveau un $) afin de montrer que lexcution de la commande est termine et que le contrle est revenu au shell. Le symbole dise (#) pose plus de problmes. Dans de nombreux fichiers Unix ou Linux, y compris les scripts du shell bash, ce symbole dnote le dbut dun commentaire et nos exemples lemploient ainsi. Mais, dans linvite de bash ( la place de $), # signifie que vous avez ouvert une session en tant que root. Puisquun seul de nos exemples excute ses commandes en tant que root, vous ne devriez pas tre trop perturb, mais il est important que vous le sachiez. Lorsque la chane dinvite est absente dun exemple, nous montrons en ralit le contenu dun script shell. Pour quelques exemples longs, nous numrotons les lignes du script, mais ces numros ne font pas partie du script lui-mme. Parfois, nous montrons un exemple sous forme dun journal de session ou dune suite de commandes. Nous pouvons galement utiliser la commande cat avec un ou plusieurs fichiers afin de vous rvler le contenu du script et/ou des fichiers de donnes utiliss dans lexemple ou dans le rsultat dune opration.
$ cat fichiers_donnees static en-tete ligne1 static en-tete ligne2 1 toto 2 titi 3 tata

De nombreux scripts et fonctions plus labors sont galement disponibles en tlchargement. Pour plus de dtails, reportez-vous la section Votre avis, page xxi. Nous avons dcid dutiliser #!/usr/bin/env bash avec ces exemples car cette dclaration est plus portable que #!/bin/bash, que vous pourrez rencontrer sur Linux ou sur un Mac. Pour plus dinformations, consultez la recette 15.1, page 334. Vous pourrez galement remarquer la ligne suivante dans certains exemples de code :
# bash Le livre de recettes : nom_extrait

Cela signifie que le code, en version amricaine, est disponible en tlchargement sur le site mis en place par les auteurs pour ce livre (http://www.bashcookbook.com). Sa version francise se trouve sur le site http://www.oreilly.fr/catalogue/2841774473. Le tlchargement (.tgz ou .zip) est document, mais vous trouverez le code dans les fichiers au format ./chXX/nom_extrait, o chXX correspond au chapitre et nom_extrait au nom du fichier.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Prface

xix

Utilisation inefficace de cat


Certains utilisateurs Unix aiment reprer les points dinefficacit dans le code des autres. En gnral, cette critique est apprcie lorsquelle est donne de manire constructive. Le cas le plus courant est probablement lutilisation inefficace de cat , signal lorsque quelquun excute cat fichier | grep toto la place de grep toto fichier. Dans ce cas, cat est inutile et implique un surcot puisque la commande sexcute dans un sous-shell. Un autre cas frquent est cat fichier | tr '[A-Z]' '[a-z]' la place de tr '[A-Z]' '[a-z]' < fichier. Parfois, lemploi de cat peut mme provoquer le dysfonctionnement dun script (voir la recette 19.8, page 493). Mais, une utilisation superflue de cat sert parfois un objectif. Elle peut reprsenter un emplacement dans un tube que dautres commandes remplaceront par la suite (peuttre mme cat -n). Dautre part, en plaant le fichier sur le ct gauche dune commande, votre attention soriente plus facilement vers cette partie du code. Ce ne sera pas le cas si le fichier se trouve derrire un symbole < lextrmit droite de la page. Mme si nous approuvons lefficacit, ainsi que la ncessit de la rechercher, elle nest plus aussi essentielle quelle a pu ltre. Cependant, nous ne militons pas en faveur de la ngligence ou de lexplosion de code. Nous disons simplement que la rapidit des processeurs nest pas vraiment en dclin. Si vous aimez utiliser cat, nhsitez donc pas.

Note propos de Perl


Nous vitons volontairement lemploi de Perl dans nos solutions, autant que possible, mme si, dans certains cas, il serait le bienvenu. Perl fait dj lobjet dune description dtaille dans dautres ouvrages, bien plus que nous ne pourrions le faire ici. Dautre part, Perl savre gnralement plus long, avec un surcot plus important, que nos solutions. Il existe galement une mince frontire entre lcriture de scripts shell et de scripts Perl ; le sujet de ce livre est lcriture de scripts shell. Les scripts shell servent combiner des programmes Unix, tandis que Perl inclut de nombreuses fonctionnalits des programmes Unix externes dans le langage lui-mme. Il est ainsi plus efficace et, dune certaine manire, plus portable. Cependant, il est galement diffrent et complique lexcution efficace des programmes externes dont vous avez besoin. Le choix de loutil employer est souvent plus guid par ses connaissances de loutil que par toute autre raison. En revanche, lobjectif est toujours de raliser le travail ; le choix de loutil est secondaire. Nous vous montrerons plusieurs manires de raliser certaines oprations avec bash et des outils connexes. Lorsque votre but sera de mener bien votre travail, vous devrez choisir les outils utiliser.

Autres ressources
Perl Cookbook, 3e dition, Nathan Torkington et Tom Christiansen (OReilly Media) ; Programmation en Perl, 3e dition, Larry Wall et autres (ditions OReilly) ; De lart de programmer en Perl, Damian Conway (ditions OReilly) ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

xx

Prface
Matrise des expressions rgulires, 2e dition, Jeffrey E. F. Friedl (ditions OReilly) ; Le shell Bash, 3e dition, Cameron Newham (ditions OReilly) ; Introduction aux scripts shell, Nelson H.F. Beebe et Arnold Robbins (ditions OReilly).

Conventions typographiques
Les conventions typographiques sont les suivantes :
Menu

Indique des intituls, des options et des boutons de menus, ainsi que des touches du clavier, comme Alt et Ctrl. Italique Dsigne des termes nouveaux, des URL, des adresses lectroniques, des noms de fichiers, des extensions de fichiers, des noms de chemins, des rpertoires et des utilitaires Unix. Chasse fixe Cette police est utilise pour les commandes, les options, les variables, les attributs, les fonctions, les types, les classes, les espaces de noms, les mthodes, les modules, les proprits, les paramtres, les valeurs, les objets, les vnements, les gestionnaires dvnements, les balises XML, les balises HTML, les macros, le contenu des fichiers et la sortie des commandes. Chasse fixe gras Signifie lutilisateur de saisir littralement des commandes ou du texte. Chasse fixe italique Montre que du texte doit tre remplac par des valeurs saisies par lutilisateur.
Cette icne signifie un conseil, une suggestion ou une note gnrale.

Ce pige vous met en garde.

Utiliser les exemples de code


Ce livre a comme objectif de vous aider. En gnral, vous pourrez utiliser les exemples de code sans restriction dans vos pages web et vos conceptions. Vous naurez pas besoin de contacter OReilly pour une autorisation, moins que vous ne vouliez reproduire des portions significatives de code. La conception dun programme reprenant plusieurs extraits de code de cet ouvrage ne requiert aucune autorisation. Par contre, la vente et la distribution dun CD-ROM dexemples provenant des ouvrages OReilly en ncessitent une. Rpondre une question en citant le livre et les exemples de code ne requi[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Prface

xxi

rent pas de permission. Par contre intgrer une quantit significative dexemples de code extraits de ce livre, dans la documentation de vos produits en ncessite une. Nous apprcions, sans limposer, lattribution de lorigine de ce code. Une attribution comprend gnralement le titre, lauteur, lditeur et le numro ISBN. Par exemple, bash Le livre de recettes, de Carl Albing, JP Vossen et Cameron Newham. Copyright 2007 ditions OReilly, 2-84177-447-3 . Si vous pensez que lutilisation que vous avez faite de ce code sort des limites dune utilisation raisonnable ou du cadre de lautorisation ci-dessus, nhsitez pas nous contacter ladresse editorial@oreilly.fr.

Votre avis
Adressez vos commentaires et questions sur ce livre lditeur : ditions OReilly 18 rue Sguier 75006 Paris Une page web existe pour ce livre, o nous donnons les exemples, une liste derrata et quelques informations supplmentaires, ladresse : http://www.oreilly.fr/catalogue/2841774473 Vous trouverez galement des informations sur cet ouvrage, des exemples de code, des errata, des liens, la documentation de bash et bien dautres complments sur le site cr par les auteurs (en anglais) : http://www.bashcookbook.com Pour donner vos commentaires ou poser des questions techniques sur ce livre, envoyez un courriel : editorial@oreilly.com Pour plus dinformations sur les ouvrages, confrences, logiciels, les centres de ressources et le rseau OReilly, rendez-vous sur le site web OReilly ladresse : http://www.oreilly.com et http://www.oreilly.fr

Remerciements
Merci la GNU Software Foundation et Brian Fox pour avoir crit bash. Merci galement Chet Ramey, qui assure le dveloppement de bash depuis la version 1.14 au milieu des annes 90. Nous lui sommes reconnaissants davoir rpondu nos questions et davoir relu une version prliminaire de ce livre.

Relecteurs
Un grand merci nos relecteurs : Yves Eynard, Chet Ramey, William Shotts, Ryan Waldron et Michael Wang. Leurs commentaires, leurs suggestions et, dans certains cas, leurs solutions alternatives nous ont t indispensables. Ils ont galement repr nos erreurs et, de manire gnrale, amlior cet ouvrage. Les erreurs que vous pourriez trouver nous sont dues.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

xxii

Prface

OReilly
Nous voulons remercier toute lquipe de OReilly, notamment Mike Loukides, Derek Di Matteo et Laurel Ruma.

Des auteurs
Carl
Lcriture dun livre nest jamais un effort totalement solitaire. Merci JP et Cameron davoir travaill avec moi sur ce projet. Nos talents complmentaires et nos disponibilits respectives ont permis dcrire un livre plus abouti. Je voudrais galement remercier JP pour nous avoir fourni une certaine infrastructure matrielle. Merci Mike pour avoir accept ma proposition de ce livre sur bash, de mavoir mis en contact avec JP et Cameron qui avaient un projet similaire, de nous avoir pouss lorsque nous tions bloqus et de nous avoir guids lorsque nous devenions fous. Son assistance permanente et ses conseils techniques ont t trs apprcis. Ma femme et mes enfants mont patiemment soutenu tout au long de ce projet, en mencourageant, en me motivant et en me laissant du temps et de la place pour travailler. Je les remercie du fond du cur. Outre la rdaction de ce livre, un travail de recherche et de prparation a t ncessaire. Je suis particulirement redevable Dr. Ralph Bjork qui ma initi Unix, bien avant que quiconque en ait entendu parler. Sa perspicacit, sa prvoyance et ses conseils mont t bnfiques bien plus longtemps que je ne laurais imagin. Je ddie ce livre mes parents, Hank et Betty, qui mont apport toutes les meilleures choses quils avaient moffrir, comme leur prsence, la foi chrtienne, lamour, une excellente ducation, une place dans la vie et tout ce que lon souhaite pour ses propres enfants. Je ne pourrais jamais les remercier assez.

JP
Merci Cameron pour avoir crit Le shell bash. Son livre ma normment appris et a t ma premire rfrence lorsque jai dbut ce projet. Merci Carl pour tout son travail, sans lequel il aurait fallu quatre fois plus de temps pour un rsultat infrieur. Je voudrais remercier Mike davoir accept ce projet, de lavoir aid avancer et davoir propos Carl de nous rejoindre. Merci galement Carl et Mike pour leur patience quant mes problmes existentiels et de planning. Je ddie ce livre Papa, qui en aurait t trs content. Il ma toujours dit que les deux dcisions les plus importantes sont ce que lon fait et avec qui lon se marie. En y rflchissant, je pense avoir plutt bien russi. Cet ouvrage est donc galement ddi Karen, pour son incroyable soutien, patience et comprhension pendant ce processus plus long que prvu et sans qui les ordinateurs ne seraient pas aussi amusants. Enfin, merci Kate et Sam, qui ont tellement contribu rsoudre mes problmes dorganisation.

Cameron
Jaimerais remercier JP et Carl pour leur incroyable travail, sans lequel ce livre nexisterait probablement pas. Merci galement JP pour avoir mis lide dun tel livre sur bash ; je suis certain quil le regrette parfois, face aux longues heures passes devant le clavier, mais quil est fier dy avoir particip. Enfin, je voudrais nouveau remercier Adam.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1
Dbuter avec bash
Quest-ce quun shell et pourquoi faudrait-il sy intresser ? Tout systme dexploitation rcent (postrieur 1970) propose une forme dinterface utilisateur, cest--dire un mcanisme permettant dindiquer les commandes excuter. Dans les premiers systmes dexploitation, cette interface de commande tait intgre et il nexistait quune seule manire de converser avec lordinateur. Par ailleurs, linterface ne permettait dexcuter que des commandes, car ctait alors lunique rle de lordinateur. Le systme dexploitation Unix a promu la sparation du shell (llment du systme qui permet de saisir des commandes) de tous les autres composants : le systme dentre/sortie, lordonnanceur, la gestion de la mmoire et tous les autres aspects pris en charge par le systme dexploitation (dont la plupart des utilisateurs ne veulent pas entendre parler). Linterprteur de commandes ntait quun programme parmi tant dautres. Son travail tait dexcuter dautres programmes pour le compte des utilisateurs. Cependant, cette sparation a t le dbut dune rvolution. Le shell ntait quun autre programme qui sexcutait sur Unix et, si vous naimiez pas celui livr en standard, vous pouviez crire le vtre. Cest ainsi qu la fin des dix premires annes dexistence dUnix, au moins deux shells taient en concurrence : le shell Bourne, sh (un descendant du shell originel de Thomson), et le shell C, csh. La deuxime dcennie dUnix vue lapparition dautres variantes : le shell Korn (ksh) et la premire version de bash. la fin de la troisime dcennie, il existait probablement une dizaine de shells diffrents. Une fois devant votre systme, vous demandez-vous vais-je utiliser csh, bash ou ksh aujourdhui ? Cest peu probable. Vous utilisez certainement le shell standard livr avec votre systme Linux (ou BSD, Mac OS X, Solaris, HP/UX). Mais, en sortant linterprteur de commandes du systme dexploitation lui-mme, les dveloppeurs (comme Brian Fox, le crateur de bash, et Chet Ramey, actuellement responsable de bash) ont plus de facilit crire de meilleurs shells. Vous pouvez crer un nouveau shell sans toucher au systme dexploitation. Il est ainsi beaucoup plus facile de faire accepter un nouveau shell, puisque les fournisseurs de systmes dexploitation nont pas lintgrer leur systme. Il suffit de prparer le shell afin quil puisse tre install sur un systme comme nimporte quel autre programme.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Chapitre 1 Dbuter avec bash

Mais, pourquoi faire autant dhistoires pour un programme qui prend simplement des commandes et les excute ? Effectivement, si le shell ne vous permettait que de saisir des commandes, il ne serait gure intressant. Cependant, deux facteurs ont t prdominants dans lvolution du shell Unix : la facilit dutilisation et la programmation. Il en a rsult un shell moderne qui offre de nombreuses fonctionnalits en plus daccepter des commandes. Les shells modernes sont trs pratiques. Par exemple, ils se souviennent des commandes saisies et vous permettent de les rutiliser et de les modifier. Ils vous permettent galement de dfinir vos propres abrviations de commandes, des raccourcis et dautres fonctionnalits. Pour un utilisateur expriment, la saisie de commandes (cest--dire avec les raccourcis, les alias et la compltion) est beaucoup plus efficace et rapide que le dplacement dicnes au sein dune interface graphique. Outre leur simple commodit, les shells sont programmables. Certaines suites de commandes sont invoques trs souvent. Ds que vous effectuez une opration plus dune fois, vous devez vous demander puis-je crire un programme qui fasse cela ma place ? La rponse est oui. Un shell est aussi un langage de programmation spcifiquement conu pour oprer avec les commandes du systme de votre ordinateur. Par consquent, si vous souhaitez gnrer un millier de fichiers MP3 partir de fichiers WAV, vous pouvez crire un programme shell (ou un script shell) qui le fera. Si vous souhaitez compresser tous les fichiers de journalisation de votre systme, vous pouvez crire un script shell qui ralisera ce travail. Ds quune tche devient rptitive, vous devez essayer de lautomatiser en crivant un script shell. Il existe plusieurs langages de scripts puissants, comme Perl, Python et Ruby, mais linterprteur de commandes dUnix (quelle que soit la variante du shell que vous utilisez) est un bon point de dpart. En effet, vous savez dj saisir des commandes, alors pourquoi compliquer les choses ?

Intrt de bash
Si ce livre sarticule autour de bash et non dun autre shell, cest que bash est largement disponible. Il nest pas le plus rcent, sans doute pas le plus fantaisiste ni le plus puissant (quoi quil ne doit pas en tre loin) et nest pas le seul tre distribu comme logiciel Open Source, mais il est omniprsent. Les raisons en sont historiques. Les premiers shells taient plutt de bons outils de programmation, mais ils taient peu pratiques pour les utilisateurs. Le shell C a amlior la facilit dutilisation, comme la possibilit de rpter une commande dj saisie, mais faisait un pitre langage de programmation. Le shell Korn, sorti ensuite (au dbut des annes 80), a amlior la facilit dutilisation et le langage de programmation et semblait promis un grand avenir. Malheureusement, ksh ntait pas initialement un logiciel Open Source ; il tait un produit propritaire, donc difficile fournir avec un systme dexploitation gratuit comme Linux. Sa licence a t modifie en 2000, puis nouveau en 2005. la fin des annes 80, la communaut Unix a dcid que la standardisation tait une bonne chose et les groupes de travail POSIX (sous lgide de lIEEE) ont t forms. POSIX a normalis les bibliothques et les utilitaires Unix, en particulier le shell. Linterprteur de commandes standard tait principalement bas sur la version de 1988 du shell Korn, avec certaines caractristiques du shell C et quelques innovations. bash a d-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Dbuter avec bash

marr au sein du projet GNU, dont le but tait de crer un systme POSIX complet et donc un shell POSIX. bash offrait les possibilits indispensables aux programmeurs shell, ainsi que la commodit souhaite par les utilisateurs de la ligne de commande. lorigine, il a t conu comme une alternative au shell Korn, mais avec limportance prise par le mouvement du logiciel libre et la large adoption de Linux, bash a rapidement clips ksh. Cest ainsi que bash est devenu linterprteur de commandes par dfaut de toutes les distributions Linux que nous connaissons (il existe une centaine de distributions Linux et il est probable que quelques-unes choisissent un autre shell par dfaut), ainsi que sur Mac OS X. Il est galement disponible pour tous les autres systmes dexploitation Unix, notamment BSD et Solaris. Dans les rares cas o bash nest pas livr avec le systme, son installation reste simple. Il est mme disponible pour Windows (via Cygwin). bash est la fois un langage de programmation puissant et une bonne interface utilisateur. Vous naurez mme pas sacrifier des raccourcis clavier pour obtenir des fonctions de programmation labores. En vous formant bash, vous ne pouvez pas vous tromper. Les shells par dfaut les plus rpandus sont lancien shell Bourne et bash, dont la compatibilit avec le premier est excellente. Lun de ces interprteurs de commandes est sans aucun doute prsent sur tout systme dexploitation Unix, ou assimil, moderne. De plus, comme nous lavons prcis, si bash est absent de votre machine, rien ne vous empche de linstaller. Cependant, il existe dautres shells. Dans lesprit du logiciel libre, les auteurs et les responsables de tous ces shells partagent leurs ides. Si vous consultez les rapports de modification de bash, vous constaterez que de nombreuses caractristiques ont t ajoutes ou ajustes afin dassurer une compatibilit avec un autre shell. Cependant, la plupart des utilisateurs sen moquent. Ils prennent ce qui existe et sen contentent. Par consquent, si cela vous intresse, vous pouvez tudier dautres shells. Il existe de nombreuses alternatives dignes dintrt et vous pourriez en prfrer certaines, mme si elles ne seront probablement pas aussi rpandues que bash.

Le shell bash
bash est un shell, cest--dire un interprteur de commandes. Son principal objectif, comme celui de nimporte quel shell, est de vous permettre dinteragir avec le systme dexploitation de lordinateur afin daccomplir vos tches. En gnral, cela implique le lancement de programmes. Le shell prend donc les commandes saisies, dtermine les programmes qui doivent tre excuts et les lance pour vous. Certaines tches demandent lexcution dune suite dactions rcurrente ou trs complexe, voire les deux. La programmation shell, ou lcriture de scripts shell, vous permet dautomatiser ces tches, pour plus de facilit dutilisation, de fiabilit et de reproductibilit. Si bash est nouveau pour vous, nous commencerons par certains fondamentaux. Si vous avez dj utilis Unix ou Linux, vous avez probablement rencontr bash, mais peut-tre sans le savoir. bash est essentiellement un langage dexcution de commandes et celles que vous avez dj pu saisir, comme ls, cd, grep ou cat, sont, en un sens, des commandes bash. Parmi ces commandes, certaines sont intgres bash lui-mme, tandis que dautres sont des programmes spars. Pour le moment, cette diffrence nest pas importante.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

Chapitre 1 Dbuter avec bash

Nous terminerons ce chapitre en expliquant comment obtenir bash. La plupart des systmes sont livrs avec une version de bash installe, mais ce nest pas le cas de tous. Mme si votre systme vient avec bash, il est toujours bon de savoir comment lobtenir et linstaller. De nouvelles versions, offrant de nouvelles fonctionnalits, sortent de temps autre. Si vous utilisez dj bash et en avez une certaine habitude, vous pouvez aller directement au chapitre 2. Vous ntes pas oblig de lire ce livre du dbut la fin. Si vous consultez les recettes des chapitres centraux, vous verrez de quoi bash est rellement capable. Mais, commenons par le dbut.

1.1. Comprendre linvite de commandes


Problme
Vous voulez savoir ce que signifient tous ces symboles de ponctuation.

Solution
Tous les interprteurs de commandes possdent une forme dinvite qui vous signale que le shell est prt accepter vos ordres. Laspect de linvite dpend de nombreux facteurs, en particulier du type et de la version du systme dexploitation, du type et de la version du shell, de la distribution et de sa configuration. Dans la famille des shells Bourne, le symbole $ la fin de linvite signifie gnralement que vous avez ouvert une session en tant quutilisateur normal, tandis que le symbole # signifie que vous tes le super-utilisateur (root). Le compte root est ladministrateur du systme et quivaut au compte Systme de Windows (plus puissant que le compte Administrateur) ou au compte Superviseur de Netware. root est tout-puissant et peut faire tout ce quil veut sur un systme Unix ou Linux classique. Les invites par dfaut affichent souvent le chemin du rpertoire courant, mme si elles peuvent labrger. Le symbole ~ signifie que le rpertoire de travail est votre rpertoire personnel. Certaines invites par dfaut peuvent galement afficher votre nom dutilisateur et le nom de la machine sur laquelle vous tes connect. Si, pour le moment, cela vous semble idiot, vous changerez davis lorsque vous serez connect cinq machines sous des noms potentiellement diffrents. Voici une invite Linux classique, pour lutilisateur jp sur la machine adams, actuellement dans son rpertoire personnel. Le symbole $ final indique quil sagit dun utilisateur normal, non de root.
jp@adams:~$

En passant dans le rpertoire /tmp, voici ce que devient linvite. Vous remarquerez que ~, qui signifie en ralit /home/jp, a t remplac par /tmp.
jp@adams:/tmp$

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.2. Afficher son emplacement

Discussion
Linvite du shell est un lment prdominant de la ligne de commande et ses possibilits de personnalisation sont nombreuses. Pour le moment, vous devez simplement savoir comment linterprter. Bien entendu, votre invite par dfaut peut tre diffrente, mais vous devez dj tre en mesure de la comprendre, au moins partiellement. Certains systmes Unix ou Linux offrent accs la puissance de root par le biais de commandes comme su et sudo. Parfois, root nest pas rellement tout-puissant, par exemple lorsque le systme intgre une politique des accs (MAC mandatory access control), comme SELinux de la NSA.

Voir aussi
la recette 1.2, Afficher son emplacement, page 5 ; la recette 14.19, Utiliser sudo de manire plus sre, page 318 ; la recette 16.2, Personnaliser linvite, page 368 ; la recette 17.15, Utiliser sudo avec un groupe de commandes, page 454.

1.2. Afficher son emplacement


Problme
Vous ntes pas certain de votre rpertoire de travail et linvite par dfaut nest daucune aide.

Solution
Utilisez la commande interne pwd ou dfinissez une invite plus utile (voir la recette 16.2, page 368). Par exemple :
bash-2.03$ pwd /tmp bash-2.03$ export PS1='[\u@\h \w]$ ' [jp@solaris8 /tmp]$

Discussion
pwd signifie print working directory (afficher le rpertoire de travail) et accepte deux arguments. -L, loption par dfaut, affiche votre chemin logique. -P affiche votre emplacement physique, qui peut tre diffrent du chemin logique si vous avez suivi un lien symbolique.
bash-2.03$ pwd /tmp/rep2

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6
bash-2.03$ pwd -L /tmp/rep2 bash-2.03$ pwd -P /tmp/rep1

Chapitre 1 Dbuter avec bash

Voir aussi
la recette 16.2, Personnaliser linvite, page 368.

1.3. Chercher et excuter des commandes


Problme
Vous voulez trouver et excuter une commande prcise sous bash.

Solution
Essayez les commandes type, which, apropos, locate, slocate, find et ls.

Discussion
bash gre une liste des rpertoires dans lesquels il doit rechercher les commandes. Cette liste est place dans la variable denvironnement $PATH. La commande type interne bash cherche dans votre environnement (y compris les alias, les mots-cls, les fonctions, les commandes internes et les fichiers dans $PATH) les commandes excutables correspondant ses arguments et affiche le type et lemplacement de celles trouves. Parmi tous ces arguments, loption -a permet dafficher toutes les correspondances et pas seulement la premire. La commande which est similaire, mais sa recherche se fait uniquement dans la variable $PATH (et les alias de csh). Elle peut tre diffrente dun systme lautre (il sagit souvent dun script csh sur BSD et dun binaire sur Linux), mais elle accepte gnralement loption -a, tout comme type. Vous pouvez vous en servir lorsque vous connaissez le nom de la commande et souhaitez connatre son emplacement prcis, ou bien pour savoir si elle existe sur lordinateur. Par exemple :
$ type which which is hashed (/usr/bin/which) $ type ls ls is aliased to `ls -F -h' $ type -a ls ls is aliased to `ls -F -h' ls is /bin/ls $ which which /usr/bin/which

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.3. Chercher et excuter des commandes

Pratiquement toutes les commandes disposent dune aide sur leur utilisation. En gnral, il existe une documentation en ligne sous forme de pages de manuel (manpages). Pour les consulter, utilisez la commande man. Par exemple, man ls affiche la documentation de la commande ls. De nombreux programmes offrent galement une aide intgre laquelle on accde laide dun argument, comme -h ou --help. Certains programmes, en particulier sur dautres systmes dexploitation, affichent une aide lorsque vous ne passez aucun argument. Cest galement le cas de quelques commandes Unix, mais elles sont rares. La raison provient de lutilisation des commandes Unix dans les tubes. Nous y reviendrons plus loin. Mais, que pouvez-vous faire si vous ne connaissez pas ou si vous avez oubli le nom de la commande dont vous avez besoin ? apropos recherche dans les intituls et les descriptions des pages de manuel les expressions rgulires passes en argument. Elle savre incroyablement utile lorsque vous avez oubli le nom dune commande. Elle quivaut man -k.
$ apropos music cms (4) - Creative Music System device driver $ man -k music cms (4) - Creative Music System device driver

locate et slocate consultent les bases de donnes du systme (gnralement compiles et actualises par une tche cron) pour trouver, quasi instantanment, des fichiers ou des commandes. Lemplacement des bases de donnes, les informations indexes et la frquence des mises jour peuvent varier dun systme lautre. Pour plus de dtails, consultez les pages de manuel de votre systme. Outre les noms de fichiers et les chemins, slocate stocke des informations dautorisation afin de ne pas prsenter lutilisateur des programmes auxquels il na pas accs. Sur la plupart des systmes Linux, locate est un lien symbolique vers slocate. Sur dautres systmes, ces programmes peuvent tre distincts ou slocate peut ne pas exister.
$ locate apropos /usr/biSOpropos /usr/share/man/de/man1/apropos.1.gz /usr/share/man/es/man1/apropos.1.gz /usr/share/man/it/man1/apropos.1.gz /usr/share/man/ja/man1/apropos.1.gz /usr/share/man/man1/apropos.1.gz

Pour plus dinformations sur la commande find, consultez le chapitre 9. Enfin, vous pouvez galement essayer la commande ls. Noubliez pas que si la commande recherche se trouve dans votre rpertoire de travail, vous devez la prfixer par ./ puisque, pour des raisons de scurit, le rpertoire courant ne se trouve gnralement pas dans $PATH (voir les recettes 14.3, page 294, et 14.10, page 303).

Voir aussi
help type ; man which ; man apropos ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

8
man locate ; man slocate ; man find ; man ls ;

Chapitre 1 Dbuter avec bash

le chapitre 9, Rechercher des fichiers avec find, locate et slocate, page 191 ; la recette 4.1, Lancer nimporte quel excutable, page 71 ; la recette 14.10, Ajouter le rpertoire de travail dans $PATH, page 303.

1.4. Obtenir des informations sur des fichiers


Problme
Vous avez besoin dinformations complmentaires sur un fichier, comme sa nature, son propritaire, ses droits dexcution, le nombre de liens physiques quil possde ou la date de dernier accs ou de dernire modification.

Solution
Utilisez les commandes ls, stat, file ou find.
$ touch /tmp/fichier

$ ls /tmp/fichier /tmp/fichier $ ls -l /tmp/fichier -rw-r--r-- 1 jp jp 0 2007-06-19 15:03 /tmp/fichier

$ stat /tmp/fichier File: "/tmp/fichier" Size: 0 Blocks: 0 IO Block: 4096 fichier rgulier Device: 303h/771d Inode: 2310201 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 501/ jp) Gid: ( 501/ jp) Access: 2007-06-19 14:23:10.000000000 +0200 Modify: 2007-06-19 14:23:10.000000000 +0200 Change: 2007-06-19 14:23:10.000000000 +0200

$ file /tmp/fichier /tmp/fichier: empty $ file -b /tmp/fichier empty

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.4. Obtenir des informations sur des fichiers


$ echo '#!/bin/bash -' > /tmp/fichier

$ file /tmp/fichier /tmp/fichier: Bourne-Again shell script text executable $ file -b /tmp/fichier Bourne-Again shell script text executable

Le chapitre 9 reviendra en dtail sur la commande find.

Discussion
La commande ls affiche uniquement les noms de fichiers, tandis que ls -l fournit des dtails supplmentaires sur chaque fichier. ls accepte de nombreuses options ; consultez sa page de manuel sur votre systme pour les connatre. Voici quelques-unes des options intressantes : -a Ne pas masquer les fichiers commenant par . (point). -F Afficher le type du fichier en ajoutant lun des symboles de type suivant : /*@%=|. -l Prsenter une liste dtaille. -L Rvler les informations sur le fichier li et non sur le lien symbolique lui-mme. -Q Encadrer les noms de fichiers avec des guillemets (extension GNU, non reconnue par tous les systmes). -r Inverser lordre de tri. -R Parcourir les sous-rpertoires. -S Trier daprs la taille de fichier. -1 Utiliser le format court, mais avec un fichier par ligne. Avec loption -F, une barre oblique (/) dsigne un rpertoire, un astrisque (*) indique que le fichier est excutable, un symbole arobase (@) dsigne un lien symbolique, un signe gal (=) dsigne une socket et un tube (|) les FIFO (first-in, first-out). stat, file et find acceptent de nombreux paramtres qui rglent le format de la sortie ; consultez les pages de manuel de votre systme pour les connatre. Par exemple, les options suivantes produisent une sortie similaire celle de ls -l :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

10

Chapitre 1 Dbuter avec bash


$ ls -l /tmp/fichier -rw-r--r-- 1 jp jp 0 2007-06-19 15:03 /tmp/fichier $ stat -c'%A %h %U %G %s %y %n' /tmp/fichier -rw-r--r-- 1 jp jp 14 2007-06-19 14:26:32.000000000 +0200 /tmp/fichier $ find /tmp/ -name fichier -printf '%m %n %u %g %t %p' 644 1 jp jp Tue Jun 19 14:26:32 2007 /tmp/fichier

Ces outils ne sont pas disponibles sur tous les systmes dexploitation, ni dans certaines versions. Par exemple, la commande stat nexiste pas, par dfaut, sur Solaris. Vous devez savoir que les rpertoires ne sont rien dautres que des fichiers traits de manire particulire par le systme dexploitation. Les commandes prcdentes fonctionnent donc galement sur les rpertoires, mme si vous devez parfois les modifier pour obtenir ce que vous souhaitez. Par exemple, utilisez ls -d pour afficher les informations concernant un rpertoire, la place de ls seule qui affiche le contenu dun rpertoire.

Voir aussi
man ls ; man stat ; man file ; man find ; le chapitre 9, Rechercher des fichiers avec find, locate et slocate, page 191.

1.5. Afficher tous les fichiers cachs


Problme
Vous voulez voir uniquement les fichiers cachs (avec un point) dun rpertoire afin de modifier un fichier dont vous avez oubli le nom ou pour supprimer des fichiers obsoltes. ls -a affiche tous les fichiers, y compris ceux normalement cachs, mais elle est trop prolixe et ls -a .* ne vous convient pas.

Solution
Utilisez ls -d combine vos autres critres.
ls -d .* ls -d .b* ls -d .[!.]*

Vous pouvez galement adapter la commande de manire retirer . et .. de la liste.


$ grep -l 'PATH' ~/.[!.]* /home/jp/.bash_history /home/jp/.bash_profile

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.5. Afficher tous les fichiers cachs

11

Discussion
En raison de la manire dont le shell traite les caractres gnriques, la combinaison .* ne se comporte pas forcment comme vous le supposez. Lexpansion des noms de fichiers, ou globalisation (globbing), considre toute chane contenant les caractres *, ? ou [ comme un motif et la remplace par une liste alphabtique des noms de fichiers qui correspondent ce motif. * correspond nimporte quelle chane, y compris la chane vide, tandis que ? quivaut un seul caractre. Les caractres placs entre [ et ] dfinissent une liste ou une plage de caractres, chacun devant correspondre. Il existe galement dautres oprateurs de correspondance de motifs, mais nous ne les prsenterons pas dans ce chapitre (voir les sections Caractres pour la correspondance de motifs, page 546, et Oprateurs pour la correspondance de motifs tendue extglob, page 547). Ainsi, *.txt signifie tout fichier se terminant par .txt, tandis que *txt reprsente tout fichier se terminant par txt (sans point). f?o correspond foo ou fao, mais pas fooo. Vous pourriez donc penser que .* correspond tout fichier qui commence par un point. Malheureusement, lexpansion de .* inclut . et .., qui sont donc tous deux affichs. Au lieu dobtenir uniquement les fichiers commenant par un point dans le rpertoire de travail, vous obtenez galement ces fichiers, tous les fichiers et rpertoires du rpertoire courant (.), tous les fichiers et rpertoires du rpertoire parent (..), ainsi que les noms et le contenu de tous les sous-rpertoires du rpertoire de travail qui commencent par un point. Cest, pour le moins, assez perturbant. Vous pouvez essayer la mme commande ls avec et sans loption -d, puis echo .*. Cette commande echo affiche simplement le rsultat de lexpansion de .* par le shell. Essayez galement echo .[!.]*. .[!.]* est un motif dans lequel [ ] prcise la liste des caractres employs dans la correspondance, mais le symbole ! du dbut inverse le rle de la liste. Nous recherchons donc tout ce qui commence par un point, suivi de tout caractre autre quun point, suivi dun nombre quelconque de caractres. Vous pouvez galement employer ^ pour inverser une classe de caractres, mais ! fait partie des spcifications de POSIX et est donc plus portable.
.[!.]* ne trouvera pas un fichier nomm ..toto. Vous pouvez ajouter quelque chose comme .??* pour trouver les correspondances avec tout ce qui commence par un point et qui contient au moins trois caractres. Mais, ls -d .[!.]* .??* affiche alors deux fois tout ce qui correspond aux deux motifs. Vous pouvez galement utiliser .??* seul, mais les fichiers comme .a sont alors oublis. La solution dpend de vos besoins et de votre environnement. Il nexiste pas de rponse adapte tous les cas.
$ ls -a . .. $ ls -d .[!.]* .a ..toto .a .fichier_point_normal fichier_normal

.fichier_point_normal

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

12

Chapitre 1 Dbuter avec bash

$ ls -d .??* ..toto

.fichier_point_normal

..toto .a fichier_point_normal $ ls -d .[!.]* .??* | sort -u ..toto .a .fichier_point_normal

.fichier_point_normal

echo * peut remplacer ls si cette commande est corrompue ou indisponible. Cela fonctionne car le shell transforme * en tout le contenu du rpertoire courant, cest--dire une liste similaire celle obtenue avec ls.

Voir aussi
man ls ; http://www.gnu.org/software/coreutils/faq/#ls-_002da-_002a-does-not-list-dot-files ; la section 2.11 de http://www.faqs.org/faqs/unix-faq/faq/part2 ; la section Caractres pour la correspondance de motifs, page 546 ; la section Oprateurs pour la correspondance de motifs tendue extglob, page 547.

1.6. Protger la ligne de commande


Problme
Vous souhaitez disposer dune rgle gnrale pour protger la ligne de commande.

Solution
Entourez une chane par des apostrophes, sauf si elle contient des lments que le shell doit interprter.

Discussion
Le texte non protg, tout comme celui plac entre guillemets, est assujetti lexpansion et la substitution par le shell. Prenons les exemples suivants :
$ echo Un caf vaut $5?! Un caf vaut ?! $ echo "Un caf vaut $5?!" bash: !": event not found

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.6. Protger la ligne de commande


$ echo 'Un caf vaut $5?!' Un caf vaut $5?!

13

Dans le premier exemple, $5 est trait comme une variable, mais, puisquelle nexiste pas, elle est fixe une valeur nulle. Cest galement le cas dans le deuxime exemple, mais nous nallons mme pas jusque-l car !" est trait comme une substitution de lhistorique, qui choue car elle ne correspond rien. Le troisime exemple fonctionne comme voulu. Pour combiner des expansions du shell et des chanes littrales, vous pouvez vous servir de caractre dchappement du shell (\) ou modifier la protection. Le point dexclamation est un cas particulier car la barre oblique inverse qui prcde nest pas retire. Vous pouvez contourner ce problme laide dapostrophes ou en ajoutant une espace la fin.
$ echo 'Un caf vaut $5 pour' "$USER" '?!' Un caf vaut $5 pour jp ?! $ echo "Un caf vaut \$5 pour $USER?\!" Un caf vaut $5 pour jp?\! $ echo "Un caf vaut \$5 pour $USER?! " Un caf vaut $5 pour jp?!

Par ailleurs, vous ne pouvez pas placer une apostrophe lintrieur dune chane entre apostrophes, mme en utilisant une barre oblique inverse, puisque rien, pas mme la barre oblique inverse, nest interprt lintrieur des chanes entre apostrophes. Cependant, vous pouvez contourner ce problme en utilisant des guillemets avec chappement ou en chappant une seule apostrophe lextrieur des apostrophes englobantes.
# # $ > Nous obtenons une invite de poursuite puisque les apostrophes ne sont pas quilibres. echo '$USER nachtera pas son caf $5.' ^C

# MAUVAIS. $ echo "$USER nachtera pas son caf $5." jp nachtera pas son caf . # OK. $ echo "$USER nachtera pas son caf \$5." jp nachtera pas son caf $5. # OK. $ echo 'Je n'\'achterai pas mon caf $5.' Je nachterai pas mon caf $5.

Voir aussi
le chapitre 5, Variables du shell, page 85, pour tous les dtails sur les variables du shell et la syntaxe $VAR ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14

Chapitre 1 Dbuter avec bash


le chapitre 18, Rduire la saisie, page 475, pour plus dinformations sur ! et lhistorique des commandes.

1.7. Utiliser ou remplacer des commandes


Problme
Vous voulez remplacer une commande interne par votre propre fonction ou une commande externe et vous devez savoir prcisment la commande excute par votre script (par exemple, le programme externe /bin/echo ou la commande interne echo). Si vous avez cr une nouvelle commande, elle peut tre en conf lit avec une commande interne ou une externe existante.

Solution
Utilisez type et which pour savoir si une commande existe et si elle est interne ou externe.
# type cd cd is a shell builtin # type awk awk is /biSOwk # which cd /usr/bin/which: no cd in (/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin/X11: /usr/X11R6/bin:/root/bin) # which awk /bin/awk

Discussion
Une commande interne fait partie intgrante du shell, tandis quune commande externe est un fichier spar lanc par le shell. Ce fichier peut tre un binaire ou un script shell, mais il est important de comprendre la diffrence. Premirement, lorsque vous utilisez une certaine version dun shell, les commandes internes sont toujours disponibles, contrairement aux programmes externes qui peuvent ou non tre installs sur le systme. Deuximement, si vous donnez lun de vos programmes le nom dune commande interne, vous risquez dtre surpris par le rsultat puisque la commande interne reste prioritaire (voir la recette 19.4, page 489). Vous pouvez utiliser enable pour activer et dsactiver des commandes internes, mais nous dconseillons cette solution sauf si vous tes absolument certain de bien comprendre ce que vous faite. enable -a affiche toutes les commandes internes et leur tat dactivation. Avec les commandes internes, les options -h ou --help, qui permettent dobtenir des informations sur lutilisation dune commande, ne sont gnralement pas disponibles

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.8. Dterminer si le shell est en mode interactif

15

et, si une page de manuel existe, elle fait souvent rfrence celle de bash. Dans ce cas, la commande interne help peut vous tre utile. Elle affiche une aide sur les commandes internes du shell.
# help help help: help [-s] [pattern ...] Display helpful information about builtin commands. If PATTERN is specified, gives detailed help on all commands matching PATTERN, otherwise a list of the builtins is printed. The -s option restricts the output for each builtin command matching PATTERN to a short usage synopsis.

Lorsque vous redfinissez une commande interne, utilisez builtin pour viter les boucles. Par exemple :
cd () { builtin cd "$@" echo "$OLDPWD --> $PWD" }

Pour obliger le shell invoquer une commande externe la place dune fonction ou dune commande interne, qui sont prioritaires, utilisez enable -n, qui dsactive les commandes internes du shell, ou command, qui ignore les fonctions du shell. Par exemple, pour invoquer le programme test dsign par la variable $PATH la place de la version interne du shell, saisissez enable -n test, puis excutez test. Pour invoquer la commande ls native la place dune fonction ls que vous auriez pu crer, utilisez command ls.

Voir aussi
man which ; help help ; help builtin ; help command ; help enable ; help type ; la recette 19.4, Nommer un script test , page 489 ; la section Variables internes, page 510.

1.8. Dterminer si le shell est en mode interactif


Problme
Vous voulez excuter un certain code uniquement si le shell se trouve (ou ne se trouve pas) en mode interactif.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

16

Chapitre 1 Dbuter avec bash

Solution
Utilisez linstruction case suivante :
#!/usr/bin/env bash # bash Le livre de recettes : interactive case "$-" in *i*) # Code pour le shell interactif. ;; *) # Code pour le shell non interactif. ;; esac

Discussion
La variable $- contient la liste de toutes les options en cours du shell. Si celui-ci fonctionne en mode interactif, elle contiendra i. Vous pourriez galement rencontrer du code similaire au suivant (il fonctionne parfaitement, mais la solution prcdente est conseille) :
if [ "$PS1" ]; then echo Ce shell est interactif else echo Ce shell n\est pas interactif fi

Voir aussi
help case ; help set ; la recette 6.14, Raliser des branchements multiples, page 137, pour plus dinformations sur linstruction case.

1.9. Faire de bash le shell par dfaut


Problme
Vous utilisez un systme BSD, Solaris ou une autre variante dUnix et bash nest pas le shell pas dfaut. Vous ne voulez plus lancer bash explicitement, mais en faire votre shell par dfaut

Solution
Tout dabord, vrifiez que bash est install. Si lexcution de bash --version affiche une description, alors ce shell est install sur votre systme :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.9. Faire de bash le shell par dfaut


$ bash --version GNU bash, version 3.00.16(1)-release (i386-pc-solaris2.10) Copyright (C) 2004 Free Software Foundation, Inc.

17

Si vous nobtenez aucun numro de version, il manque peut-tre un rpertoire dans votre chemin. Sur certains systmes, chsh -l ou cat /etc/shells affiche une liste des shells valides. Sinon, demandez votre administrateur lemplacement de bash ou sil peut tre install. Sous Linux, chsh -l prsente une liste des shells valides, mais, sous BSD, cette commande ouvre un diteur qui permet de modifier la configuration. Loption -l nest pas reconnue dans la version de chsh pour Mac OS X, mais lexcution de chsh ouvre un diteur pour modifier la configuration et chpass -s shell change de shell. Si bash est install, utilisez la commande chsh -s pour changer de shell par dfaut. Par exemple, chsh -s /bin/bash. Si cette commande choue, essayez chsh, passwd -e, passwd -l chpass ou usermod -s /usr/bin/bash. Si vous ne parvenez toujours pas changer de shell, demandez votre administrateur systme, qui devra peut-tre revoir le fichier /etc/passwd. Sur la plupart des systmes, il contient des lignes au format suivant :
cam:pK1Z9BCJbzCrBNrkjRUdUiTtFOh/:501:100:Cameron Newham:/home/cam:/bin/bash cc:kfDKDjfkeDJKJySFgJFWErrElpe/:502:100:Cheshire Cat:/home/cc:/bin/bash

En tant quutilisateur root, vous pouvez modifier le dernier champ des lignes du fichier des mots de passe afin de prciser le chemin complet du shell choisi. Si votre systme dispose dune commande vipw, vous devez lutiliser pour prserver la cohrence du fichier des mots de passe.
Certains systmes refuseront tout shell douverture de session qui nest pas mentionn dans /etc/shells. Si bash nest pas prsent dans ce fichier, vous devrez demander votre administrateur systme de ly ajouter.

Discussion
Certains systmes dexploitation, principalement les Unix BSD, placent bash dans la partition /usr. Vous devez alors bien rflchir avant de changer le shell de root. Si le systme rencontre des problmes au dmarrage et si vous devez revoir sa configuration avant que la partition /usr soit monte, vous vous trouvez dans une situation dlicate : il ny a aucun shell pour root. Cest pourquoi il est prfrable de ne pas changer le shell par dfaut de root. En revanche, il ny a aucune raison de ne pas affecter bash comme shell par dfaut pour les comptes classiques. Noubliez pas quil est fortement dconseill dutiliser le compte root, sauf en cas dabsolue ncessit. Servez-vous de votre compte dutilisateur normal lorsque cest possible. Grce aux commandes de type sudo, vous aurez rarement besoin dun shell root. Si toutes vos tentatives ont chou, vous pouvez probablement remplacer votre shell douverture de session existant par bash en utilisant exec, mais les mes sensibles doivent sabstenir. Consultez la section A7) How can I make bash my login shell? dans la FAQ de bash (http://tiswww.case.edu/php/chet/bash/FAQ).

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

18

Chapitre 1 Dbuter avec bash

Voir aussi
man chsh ; man passwd ; man chpass ; /etc/shells ; la section A7) How can I make bash my login shell? dans la FAQ de bash lURL http://tiswww.case.edu/php/chet/bash/FAQ ; la recette 14.13, Fixer les autorisations, page 310 ; la recette 14.19, Utiliser sudo de manire plus sre, page 318.

1.10. Obtenir bash pour Linux


Problme
Vous souhaitez obtenir bash pour votre systme Linux ou vous voulez tre certain davoir la dernire version.

Solution
bash est fourni avec pratiquement toutes les distributions rcentes de Linux. Pour tre certain de disposer de la dernire version compatible avec votre distribution, utilisez les outils de gestion de paquets quelle offre. Pour mettre niveau ou installer des applications, vous devez tre root ou disposer de son mot de passe. Pour certaines distributions Linux, la version de bash par dfaut est la version 2.x, avec la version 3.x disponible sous bash3. Prenez soin de vrifier ce point. Le tableau 1-1 rcapitule les versions par dfaut dbut 2007. Les distributions mettent niveau leurs dpts assez souvent et les versions peuvent avoir chang. Par exemple, la dernire version de Debian est passe bash version 3.1. Tableau 1-1. Versions par dfaut des distributions Linux
Distribution Debian Sargea Debian Etcha Fedora Core 1 Fedora Core 2 Fedora Core 3 2.x dans linstallation initiale 2.05b Sans objet (SO) bash-2.05b31.i386.rpm bash-2.05b38.i386.rpm SO 2.x dans les mises jour 3.1dfsg-8 (testing & unstable) SO bash-2.05b34.i386.rpm SO SO 3.x dans linstallation initiale 3.0-12(1)-release 3.1.17(1)-release SO SO bash-3.017.i386.rpm 3.x dans les mises jour 3.00.16(1)release SO SO SO bash-3.018.i386.rpm

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.10. Obtenir bash pour Linux


Tableau 1-1. Versions par dfaut des distributions Linux (suite)
Distribution Fedora Core 4 Fedora Core 5 Fedora Core 6 Knoppix 3.9 & 4.0.2 Mandrake 9.2b Mandrake 10.1c Mandrake 10.2d Mandriva 2006.0e Mandriva 2007.0f OpenSUSE 10.0 OpenSUSE 10.1 OpenSUSE 10.2 SLED 10 RC3 RHEL 3.6, CentOS 3.6 RHEL 4.4, CentOS 4.4 MEPIS 3.3.1 Ubuntu 5.10g Ubuntu 6.06g Ubuntu 6.10
gh

19

2.x dans linstallation initiale SO SO SO SO bash-2.05b14mdk.i586.rpm bash-2.05b22mdk.i586.rpm SO SO SO

2.x dans les mises jour SO SO SO SO SO SO SO SO SO

3.x dans linstallation initiale bash-3.031.i386.rpm bash-3.16.2.i386.rpm bash-3.116.1.i386.rpm 3.0-15 SO SO bash-3.02mdk.i586.rpm bash-3.06mdk.i586.rpm bash-3.17mdv2007.0.i586 .rpm 3.00.16(1)release 3.1.16(1)-release bash-3.155.i586.rpm 3.1.17(1)-release SO 3.00.15(1)release 3.0-14 3.0.16(1) 3.1.17(1)-release 3.1.17(1)-release

3.x dans les mises jour SO bash-3.19.fc5.1.i386.rpm SO SO SO SO SO SO SO

SO SO SO SO bash-2.05b.0(1) SO SO SO SO SO

SO SO SO SO SO SO SO SO SO SO

3.0.17(1)-release SO SO SO SO SO SO SO SO SO

a. Debian Etch : voir aussi les paquets bash-builtins, bash-doc, bash-minimal et bash-static. b. Mandrake 9.2 : voir aussi bash-completion-20030821-3mdk.noarch.rpm, bash-doc-2.05b-14mdk. i586.rpm, bash1-1.14.7-31mdk.i586.rpm. c. Mandrake 10.1 : voir aussi bash-completion-20040711-1mdk.noarch.rpm, bash-doc-2.05b22mdk.i586.rpm, bash1-1.14.7-31mdk.i586.rpm. d. Mandrake 10.2 : voir aussi bash-completion-20050121-2mdk.noarch.rpm, bash-doc-3.0-2mdk. i586.rpm.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

20
e.

Chapitre 1 Dbuter avec bash


Mandriva 2006.0 : voir aussi bash-completion-20050721-1mdk.noarch.rpm, bash-doc-3.0-6mdk. i586.rpm. f. Mandriva 2007.0 : voir aussi bash-completion-20060301-5mdv2007.0.noarch.rpm, bash-doc-3. 1-7mdv2007.0.i586.rpm. g. Ubuntu : voir aussi les paquets bash-builtins, bash-doc, bash-static et abs-guide. h. Ubuntu 6.10 cre le lien symbolique dash vers /bin/sh au lieu de bash comme dans les versions prcdentes et la plupart des autres distributions Linux (https://wiki.ubuntu.com/ DashAsBinSh).

Pour Debian Sarge et les systmes drivs de Debian, comme Knoppix, Ubuntu et MEPIS, vrifiez que le fichier /etc/apt/sources.list dsigne un miroir Debian jour. Ensuite, utilisez les outils graphiques Synaptic, kpackage, gnome-apt ou Ajout/Suppression de programmes, loutil aptitude en mode terminal ou la ligne de commande :
apt-get update && apt-get install bash bash3 bash-builtins bash-doc bash3doc

Pour les distributions Red Hat, y compris Fedora Core (FC) et Red Hat Enterprise Linux (RHEL), utilisez loutil graphique Ajout/Suppression dapplications (si cet outil nest pas accessible depuis le menu, saisissez redhat-config-packages & sur la ligne de commande de RHEL3 ou, pour RHEL4, system-config-packages &). Pour un outil en ligne de commande uniquement, entrez :
up2date install bash

Pour Fedora Core et CentOS, vous pouvez suivre les directives donnes pour RHEL ou la ligne de commande :
yum update bash

Pour SUSE, lancez la version graphique ou en mode terminal de YaST. Vous pouvez galement employer loutil RPM en ligne de commande. Pour Mandriva/Mandrake, utilisez loutil graphique Rpmdrake ou la ligne de commande :
urpmi bash

Discussion
Puisquelles voluent trs rapidement, il nous est impossible de dcrire toutes les distributions Linux, mme les principales. Heureusement, cette volution concerne essentiellement la facilit dutilisation. vous ne devriez donc pas rencontrer trop de difficults installer un logiciel sur votre distribution. Si vous utilisez Knoppix, Ubuntu ou un autre Live CD, les mises jour et les installations choueront gnralement car le support est en lecture seule. Une fois installes sur le disque dur, les versions de ces distributions peuvent tre mises niveau. La commande apt-get update && apt-get install bash bash3 bash-builtins bashdoc bash3-doc prcdente gnrera des erreurs sur les systmes qui noffrent pas de paquet bash3. Vous pouvez les ignorer sans problme.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.11. Obtenir bash pour xBSD

21

Voir aussi
http://wiki.linuxquestions.org/wiki/Installing_Software ; CentOS : http://www.centos.org/docs/4/html/rhel-sag-en-4/pt-pkg-management.html ; Debian : http://www.debian.org/doc/, voir les manuels apt-HOWTO et Documentation dselect pour dbutants ; http://www.debianuniverse.com/readonline/chapter/06 ; Fedora Core : http://fedora.redhat.com/docs/yum/ ; Red Hat Enterprise Linux : https://www.redhat.com/docs/manuals/enterprise/RHEL-3Manual/sysadmin-guide/pt-pkg-management.html ; https://www.redhat.com/docs/manuals/enterprise/RHEL-4-Manual/sysadmin-guide/pt-pkgmanagement.html ; Mandriva : http://www.mandriva.com/fr/community/users/documentation ; http://doc.mandrivalinux.com/MandrakeLinux/101/fr/Starter.html/software-management. html ; http://doc.mandrivalinux.com/MandrakeLinux/101/fr/Starter.html/ch19s05.html ; MEPIS : manuel du systme http://www.mepis-france.org/doc/ManuelMepisFr.pdf ; OpenSuSE : http://fr.opensuse.org/Documentation ; http://www.opensuse.org/User_Documentation ; http://forge.novell.com/modules/xfmod/project/?yast ; Ubuntu : http://www.ubuntulinux.org/support/documentation/helpcenter_view et http:// ubuntu.fr/aide/ (non officiel) ; la recette 1.9, Faire de bash le shell par dfaut, page 16.

1.11. Obtenir bash pour xBSD


Problme
Vous souhaitez obtenir bash pour votre systme FreeBSD, NetBSD ou OpenBSD ou vous voulez tre certain davoir la dernire version.

Solution
Pour savoir si bash est install, consultez le fichier /etc/shells. Pour installer ou mettre jour bash, utilisez la commande pkg_add. Si vous matrisez parfaitement votre systme BSD, servez-vous des outils de gestion des portages logiciels, mais nous nexaminerons pas cette solution ici. FreeBSD :
pkg_add -vr bash

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

22

Chapitre 1 Dbuter avec bash

Pour NetBSD, visitez la page Logiciels pour NetBSD (http://www.netbsd.org/fr/Documentation/software/) et recherchez le dernier paquet bash correspondant votre version et architecture, puis excutez une commande similaire la suivante :
pkg_add -vu ftp://ftp.netbsd.org/pub/NetBSD/packages/pkgsrc-2005Q3/NetBSD2.0/i386/All/bash-3.0pl16nb3.tgz

Pour OpenBSD, servez-vous de la commande pkg_add -vr. Vous pouvez galement adapter le chemin FTP votre version et architecture. Dautre part, une version compile statiquement est probablement disponible, par exemple ftp://ftp.openbsd.org/pub/ OpenBSD/3.8/packages/i386/bash-3.0.16p1-static.tgz.
pkg_add -vr ftp://ftp.openbsd.org/pub/OpenBSD/3.8/packages/i386/bash3.0.16p1.tgz

Discussion
FreeBSD et OpenBSD placent bash dans /usr/local/bin/bash, tandis que NetBSD utilise /usr/pkg/bin/bash. PC-BSD 1.2, un systme dexploitation Unix bas sur FreeBSD solide comme un roc , fournit bash 3.1.17(0) dans /usr/local/bin/bash, bien que le shell par dfaut reste csh.

Voir aussi
la recette 1.9, Faire de bash le shell par dfaut, page 16 ; la recette 15.4, Tester des scripts sous VMware, page 339.

1.12. Obtenir bash pour Mac OS X


Problme
Vous souhaitez obtenir bash pour votre Mac ou vous voulez tre certain davoir la dernire version.

Solution
Selon la page de Chet Ramey ddie bash (http://tiswww.tis.case.edu/~chet/bash/bashtop. html), Mac OS 10.2 (Jaguar) et les versions ultrieures sont livres avec bash sous le nom /bin/sh. 10.4 (Tiger) propose la version 2.05b.0(1)-release (powerpc-apple-darwin8.0). Des paquets OS X prcompils de bash-2.05 sont galement disponibles sur plusieurs sites web, par exemple HMUG. La version de bash pour Darwin (sur lequel repose Mac OS X) est disponible sur Fink ou DarwinPorts.

Discussion
Vous pouvez galement compiler une version plus rcente de bash partir des sources, mais ce processus est dconseill aux dbutants.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.13. Obtenir bash pour Unix

23

Voir aussi
http://tiswww.tis.case.edu/~chet/bash/bashtop.html ; http://www.hmug.org/pub/MacOS_X/BSD/Applications/Shells/bash/ ; http://fink.sourceforge.net/pdb/package.php/bash ; http://darwinports.opendarwin.org/ports.php?by=name&substr=bash.

1.13. Obtenir bash pour Unix


Problme
Vous souhaitez obtenir bash pour votre systme Unix ou vous voulez tre certain davoir la dernire version.

Solution
Sil nest pas dj install ou sil ne se trouve pas parmi les programmes de votre systme dexploitation, visitez la page de Chet Ramey ddie bash et consultez la section des tlchargements des versions binaires. Vous pouvez galement le compiler partir des fichiers sources (voir lannexe E, Compiler bash).

Discussion
Voici ce que prcise la page de Chet Ramey ddie bash (http://tiswww.tis.case.edu/~chet/ bash/bashtop.html) :
Les utilisateurs de Solaris 2.x, de Solaris 7 et de Solaris 8 peuvent obtenir une version compile de bash-3.0 sur le site Sunfreeware. Sun fournit bash-2.03 avec les distributions Solaris 8, bash-2.05 en tant que programme reconnu par Solaris 9 et bash-3.0 en tant que programme pris en charge avec Solaris 10 (directement sur le CD de Solaris 10). Les utilisateurs dAIX peuvent obtenir les versions compiles des anciennes versions de bash auprs du Groupe Bull, ainsi que les fichiers sources et binaires des versions actuelles pour diffrentes variantes dAIX auprs de lUCLA. IBM propose bash-3.0 pour AIX 5L dans la bote outils AIX pour les applications [GNU/]Linux. Le format utilis est RPM, que vous pouvez galement trouver au mme endroit. Les utilisateurs de SGI peuvent obtenir une version stable de bash-2.05b partir de la page SGI Freeware. Les utilisateurs de HP-UX peuvent obtenir les versions compiles et sources de bash3.0 partir du site The Porting and Archive Centre for HP-UX. Les utilisateurs de Tru64 Unix peuvent obtenir les versions compiles et sources de bash-2.05b partir du site HP/Compaq Tru64 Unix Open Source Software Collection.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

24

Chapitre 1 Dbuter avec bash

Voir aussi
http://tiswww.tis.case.edu/~chet/bash/bashtop.html ; http://www.sun.com/software/solaris/freeware/ ; http://aixpdslib.seas.ucla.edu/packages/bash.html ; http://www.ibm.com/servers/aix/products/aixos/linux/index.html ; http://freeware.sgi.com/index-by-alpha.html ; http://hpux.cs.utah.edu/ ; http://hpux.connect.org.uk/hppd/hpux/Shells/ ; http://hpux.connect.org.uk/hppd/hpux/Shells/bash-3.00.16/ ; http://h30097.www3.hp.com/demos/ossc/html/bash.htm ; la recette 1.9, Faire de bash le shell par dfaut, page 16 ; lannexe E, Compiler bash, page 597.

1.14. Obtenir bash pour Windows


Problme
Vous souhaitez obtenir bash pour votre systme Windows ou vous voulez tre certain davoir la dernire version.

Solution
Utilisez Cygwin. Tlchargez http://www.cygwin.com/setup.exe et excutez-le. Rpondez aux questions et choisissez les paquetages installer, en particulier bash, qui se trouve dans la catgorie Shells (il est slectionn par dfaut). Au moment de lcriture de ces lignes, bash-3.2.1715 est disponible. Une fois Cygwin install, vous devrez le configurer. Pour cela, consultez le guide de lutilisateur http://cygwin.com/cygwin-ug-net/.

Discussion
Extrait du site web de Cygwin :
Ce quest Cygwin Cygwin est un environnement de type Linux pour Windows. Il est constitu de deux parties : Une DLL (cygwin1.dll), qui joue le rle de couche dmulation des API de Linux et qui en fournit les fonctionnalits principales. Un ensemble doutils, qui apportent lapparence de Linux.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.15. Obtenir bash sans linstaller


La DLL Cygwin fonctionne avec toutes les versions finales 32 bits x86 de Windows (non les versions bta ou release candidate ), depuis Windows 95 lexception de Windows CE. Ce que nest pas Cygwin Cygwin ne peut excuter des applications Linux natives sous Windows. Vous devez recompiler lapplication partir des fichiers sources pour la faire fonctionner sous Windows. Cygwin ne peut offrir aux applications Windows natives les caractristiques dUnix (par exemple les signaux ou les ptys). Une fois encore, vous devez compiler vos applications partir des fichiers sources si vous voulez exploiter les possibilits de Cygwin.

25

Cygwin est un vritable environnement de type Unix sexcutant au-dessus de Windows. Cest un outil merveilleux, mais il peut tre parfois disproportionn. Le site http:// unxutils.sourceforge.net/ propose les versions Windows natives de certains utilitaires GNU (sans bash). Les Windows Services pour UNIX (http://www.microsoft.com/windowsserversystem/sfu/ default.mspx) pourraient galement vous intresser, mais leur dveloppement nest plus vraiment assur et leur prise en charge va au moins jusquen 2011 (http://www.eweek. com/article2/0,1895,1855274,00.asp). http://jpsoft.com/ offre des shells en ligne de commande et graphiques, dont linterface est plus cohrente avec DOS/Windows. Aucun des auteurs nest associ cette entreprise, mais lun deux est un utilisateur satisfait de ces produits.

Voir aussi
http://www.cygwin.com/ ; http://unxutils.sourceforge.net/ ; http://www.microsoft.com/windowsserversystem/sfu/default.mspx ; http://jpsoft.com/ ; http://www.eweek.com/article2/0,1895,1855274,00.asp.

1.15. Obtenir bash sans linstaller


Problme
Vous souhaitez tester un shell ou un script shell sur un certain systme sans avoir le temps ou les ressources de linstaller ou de lacheter.

Solution
Demandez un compte shell gratuit (ou presque) chez HP Polar Home ou dautres four, nisseurs.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

26

Chapitre 1 Dbuter avec bash

Discussion
Le programme Test drive de HP propose des comptes shell gratuit pour plusieurs systmes dexploitation sur diffrents matriels HP Pour plus dinformations, consultez . le site http://www.testdrive.hp.com/. Polar Home offre de nombreux services gratuits et des comptes shell presque gratuits. Voici un extrait de leur site web :
polarhome.com est un organisme ducatif non commercial dont lobjectif est de promouvoir les systmes dexploitation avec shell et les services Internet, en offrant des comptes shell, des services de messagerie, ainsi que dautres services en ligne, sur tous les systmes disponibles (actuellement Linux, OpenVMS, Solaris, AIX, QNX, IRIX, HP-UX, Tru64, FreeBSD, OpenBSD, NetBSD et OPENSTEP). [...] Note : ce site est en dveloppement permanent et sappuie sur des connexions lentes ou des petits serveurs qui ne sont plus utiliss. Par consquent, en tant quutilisateur/visiteur dun site non commercial, vous ne devez pas avoir de trop grandes attentes. Mme si polarhome.com fait le maximum pour offrir des services de niveau professionnel, les utilisateurs ne doivent pas esprer plus quil nest possible. polarhome.com est un site rparti, mais 90 % de ses ressources se trouvent Stockholm, en Sude.

Voir aussi
la liste de comptes shell gratuits : http://www.ductape.net/~mitja/freeunix.shtml ; http://www.testdrive.hp.com/os/ ; http://www.testdrive.hp.com/faq/ ; http://www.polarhome.com/.

1.16. Documentation de bash


Problme
Vous souhaitez en apprendre plus sur bash, mais vous ne savez pas par o commencer.

Solution
Vous tes en train de lire ce livre, ce qui est dj un bon point de dpart ! Les autres ouvrages des ditions OReilly qui traitent de bash et de lcriture de scripts sont : Le shell bash de Cameron Newham et Introduction aux scripts shell de Nelson H.F. Beebe et Arnold Robbins. Malheureusement, la documentation officielle de bash na jamais vraiment t disponible en ligne, jusqu aujourdhui. Vous deviez prcdemment tlcharger plusieurs archives, trouver tous les fichiers qui contenaient des informations, puis dchiffrer les noms des fichiers afin dobtenir ce que vous souhaitiez. Nous avons fait tout ce travail

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.16. Documentation de bash

27

votre place et lavons plac sur notre site web consacr ce livre (http://www.bashcookbook.com/). Vous y trouverez lensemble de la documentation de rfrence officielle de bash. Nhsitez pas le visiter et le faire connatre.

Documentation officielle
Le fichier contenant la FAQ officielle de bash se trouve ftp://ftp.cwru.edu/pub/bash/FAQ. Consultez notamment la section qui concerne la documentation de bash, H2) What kind of bash documentation is there? . Le guide de rfrence officiel est galement fortement conseill (voir ci-aprs). La page web de Chet Ramey (le responsable actuel de bash) ddie bash (appele bashtop) contient une quantit impressionnante dinformations trs utiles (http://tiswww. tis.case.edu/~chet/bash/bashtop.html). Chet assure galement la mise jour des documents suivants : README Fichier dcrivant bash : http://tiswww.tis.case.edu/chet/bash/README. NEWS Fichier donnant la liste des changements importants entre la version actuelle et la version prcdente : http://tiswww.tis.case.edu/chet/bash/NEWS. CHANGES Historique complet des modifications apportes bash : http://tiswww.tis.case.edu/ chet/bash/CHANGES. INSTALL Instructions dinstallation : http://tiswww.tis.case.edu/chet/bash/INSTALL. NOTES Notes de configuration et de fonctionnement propres aux diffrentes platesformes : http://tiswww.tis.case.edu/chet/bash/NOTES. COMPAT Problmes de compatibilit entre bash3 et bash1 : http://tiswww.tis.case.edu/~chet/ bash/COMPAT. Les dernires versions du code source et de la documentation de bash sont toujours disponibles ladresse http://ftp.gnu.org/gnu/bash/. Nous vous conseillons fortement de tlcharger les sources et la documentation, mme si vous utilisez des versions binaires prcompiles. Voici une courte liste de la documentation. Lannexe B fournit un index des exemples inclus et du code source. Consultez le rpertoire ./doc disponible dans larchive des sources, par exemple http://ftp.gnu.org/gnu/ bash/bash-3.1.tar.gz, bash-3.1/doc : .FAQ Liste des questions les plus frquentes concernant bash, avec les rponses. .INTRO Courte introduction bash. article.ms Article sur bash que Chet a crit pour The Linux Journal.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

28
bash.1 Page de manuel de bash. bashbug.1 Page de manuel de bashbug.

Chapitre 1 Dbuter avec bash

builtins.1 Page de manuel qui documente les commandes internes sorties de bash.1. bashref.texi Manuel de rfrence de bash. bashref.info Manuel de rfrence de bash, une fois trait par makeinfo. rbash.1 Page de manuel du shell bash restreint. readline.3 Page de manuel de readline. Les fichiers .ps sont des versions PostScript des documents prcdents. Les fichiers .html sont des versions HTML de la page de manuel et du manuel de rfrence. Les fichiers .0 sont des pages de manuel mises en forme. Les fichiers .txt sont des versions ASCII, obtenues avec groff -Tascii. Voici ce que vous trouverez dans larchive de la documentation, par exemple http://ftp. gnu.org/gnu/bash/bash-doc-3.1.tar.gz, bash-doc-3.1 : .bash.0 Page de manuel de bash mise en forme (galement disponible aux formats PDF, ps et HTML). bashbug.0 Page de manuel de bashbug mise en forme. bashref The Bash Reference Guide (galement disponible aux formats PDF, ps, HTML et dvi). builtins.0 Page de manuel de built-ins mise en forme. .rbash.0 Page de manuel du shell bash restreint mise en forme.

Autre documentation
Bash pour le dbutant ladresse http://www.traduc.org/docs/guides/vf/Bash-BeginnersGuide/. Guide avanc dcriture des scripts Bash ladresse http://abs.traduc.org/. Writing Shell scripts.php. Scripts ladresse http://www.linuxcommand.org/writing_shell_

BASH Programming Introduction HOW-TO ladresse http://www.tldp.org/HOWTO/ Bash-Prog-Intro-HOWTO.html.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

1.16. Documentation de bash


29

The Bash Prompt HOWTO ladresse http://www.tldp.org/HOWTO/Bash-PromptHOWTO/index.html. Plutt ancien, mais encore trs utile : UNIX shell differences and how to change your shell at http://www.faqs.org/faqs/unix-faq/shell/shell-differences/. [Apples] Shell Scripting Primer ladresse http://developer.apple.com/documentation/ OpenSource/Conceptual/ShellScripting/.

Voir aussi
lannexe B, Exemples fournis avec bash, page 559.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2
Sortie standard
Sil ne produit aucune sortie, un logiciel est totalement inutile. Mais les entres/sorties ont souvent t un point dlicat de linformatique. Si vous faites partie des vieux briscards, vous vous souvenez sans doute de lpoque o lexcution dun programme impliquait un important travail de configuration de ses entres/sorties. Certains problmes ont disparu. Par exemple, il est dsormais inutile de demander des oprateurs de placer des bandes dans un lecteur, tout au moins sur les systmes bureautiques que nous avons pu rencontrer. Cependant, quelques problmes perdurent. Lun deux concerne la diversit des sorties possibles. Laffichage de donnes sur lcran est diffrent de leur enregistrement dans un fichier, enfin cela semble diffrent. De mme, crire lenregistrement de donnes dans un fichier semble diffrent de leur criture sur une bande, sur une mmoire flash ou sur dautres types de dispositifs. Et si vous souhaitez que la sortie dun programme aille directement dans un autre programme ? Les dveloppeurs doivent-ils crire du code pour grer toutes sortes de priphrique de sortie, mme ceux qui nont pas encore t invents ? Les utilisateurs doivent-ils savoir comment connecter les programmes quils emploient diffrentes sortes de priphriques ? Sans trop y rflchir, cela ne semble pas vraiment la bonne solution. Lune des ides sous-jacentes Unix est de tout considrer comme un fichier (une suite ordonne doctets). Le systme dexploitation est responsable de cette mise en uvre. Peu importe que vous criviez dans un fichier sur un disque, sur le terminal, sur un lecteur de bande, sur une cl mmoire, etc. Votre programme doit juste savoir comment crire dans un fichier et le systme dexploitation soccupe du reste. Cette approche simplifie normment le problme. La question suivante est donc quest-ce quun fichier ? . Comment un programme sait-il crire dans un fichier qui reprsente la fentre dun terminal, un fichier sur le disque ou toute autre sorte de fichiers ? Cest trs simple, il suffit de sen remettre au shell. Lorsque vous excutez un programme, vous devez encore associer ses fichiers de sortie et dentre (nous verrons comment au chapitre suivant). Cet aspect na pas disparu, mais le shell la rendu trs facile. Voici une commande excessivement simple :
$ faireQuelqueChose < fichierEntree > fichierSortie

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

32

Chapitre 2 Sortie standard

Elle lit les donnes en entre depuis fichierEntree et envoie sa sortie vers fichierSortie. Si vous omettez > fichierSortie, la sortie saffiche sur la fentre du terminal. Si vous omettez < fichierEntree, le programme lit son entre depuis le clavier. Le programme ne sait absolument pas o va sa sortie, ni do provient son entre. Vous pouvez rediriger la sortie comme bon vous semble (y compris vers un autre programme) grce aux possibilits offertes par bash. Mais ce nest que le dbut. Dans ce chapitre, nous prsentons diffrentes manires de gnrer une sortie et les mthodes du shell pour lenvoyer vers diffrents destinations.

2.1. crire la sortie sur le terminal ou une fentre


Problme
Vous souhaitez une sortie simple partir de vos commandes du shell.

Solution
Utilisez la commande interne echo. Tous les paramtres placs sur la ligne de commande sont affichs lcran. Par exemple :
echo Veuillez patienter.

affiche
Veuillez patienter.

Vous pouvez tester cette simple session en entrant la commande linvite de bash (le caractre $) :
$ echo Veuillez patienter. Veuillez patienter. $

Discussion
echo est lune des commandes bash les plus simples. Elle affiche lcran les arguments donns sur la ligne de commande. Cependant, quelques remarques sont ncessaires. Tout dabord, le shell analyse les arguments de la ligne de commande decho, comme pour nimporte quelle autre ligne de commande. Autrement dit, il applique toutes les substitutions, correspondances de caractres gnriques et autres interprtations avant de passer les arguments echo. Ensuite, puisque les arguments sont analyss, les espaces qui les sparent sont ignores :
$ echo ces mots sont ces mots sont vraiment spars $ vraiment spars

En gnral, lindulgence du shell vis--vis des espaces qui sparent les arguments est plutt la bienvenue. Dans ce cas, avec echo, elle savre assez dconcertante.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.2. crire la sortie en conservant les espaces

33

Voir aussi
help echo ; help printf ; la recette 2.3, Mettre en forme la sortie, page 34 ; la recette 15.6, crire une commande echo portable, page 342 ; la recette 19.1, Oublier les autorisations dexcution, page 485 ; la section Options et squences dchappement de echo, page 539 ; la section printf, page 540.

2.2. crire la sortie en conservant les espaces


Problme
Vous souhaitez que la sortie conserve les espaces saisies.

Solution
Placez la chane entre des apostrophes ou des guillemets. Ainsi, pour lexemple prcdent, les espaces sont affiches :
$ echo "ces ces mots $ mots sont sont vraiment spars" vraiment spars

Ou encore :
$ echo 'ces ces mots $ mots sont sont vraiment spars' vraiment spars

Discussion
Puisque les mots sont placs entre des apostrophes ou des guillemets, ils reprsentent un seul argument pour la commande echo. Cet argument est une chane et le shell ninterfre pas forcment avec son contenu. Si vous utilisez des apostrophes (''), le shell nexamine pas le contenu de la chane. Si vous la placez entre des guillemets ("), certaines substitutions ont lieu (variable, expansion du tilde et substitution de commandes), mais, dans cet exemple, le shell na rien modifier. En cas de doute, utilisez des apostrophes.

Voir aussi
help echo ; help printf ; le chapitre 5, Variables du shell, page 85, pour plus dinformations sur la substitution ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

34
la recette 2.3, Mettre en forme la sortie, page 34 ;

Chapitre 2 Sortie standard

la recette 15.6, crire une commande echo portable, page 342 ; la recette 19.1, Oublier les autorisations dexcution, page 485 ; la section Options et squences dchappement de echo, page 539.

2.3. Mettre en forme la sortie


Problme
Vous souhaitez avoir une meilleure matrise de la mise en forme et du placement de la sortie.

Solution
Utilisez la commande interne printf. Par exemple :
$ printf '%s = %d\n' Lignes $LINES Lignes = 24 $

ou :
$ printf '%-10.10s = %4.2f\n' 'GigaHertz' 1.92735 GigaHertz = 1.93 $

Discussion
La commande interne printf fonctionne comme son quivalent de la bibliothque C. Le premier argument est une chane de format. Les arguments suivants sont mis en forme conformment aux indications de format (%). Les nombres placs entre le symbole % et le type de format (s ou f dans notre exemple) apportent des dtails de mise en forme supplmentaires. Pour le format en virgule f lottante (f), la premire valeur (4 dans le modificateur 4.2) fixe la largeur du champ entier. La deuxime (2) indique le nombre de chiffres placs aprs la virgule. Notez que la rponse est arrondie. Dans le cas dune chane, la premire valeur prcise la taille maximum du champ, tandis que la seconde indique sa taille minimum. La chane sera tronque ou comble par des espaces, selon les besoins. Lorsque les modificateurs de taille maximum et minimum sont gaux, la longueur de la chane est exactement celle indique. Le symbole - ajout au modificateur signifie que la chane doit tre aligne gauche ( lintrieur de la largeur du champ). Sans ce symbole, la chane est aligne droite :
$ printf '%10.10s = %4.2f\n' 'GigaHertz' 1.92735 GigaHertz = 1.93 $

La chane en argument peut tre place ou non entre guillemets. Utilisez-les si vous souhaitez conserver lespacement indiqu ou si vous devez annuler la signification particu-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.4. crire la sortie sans le saut de ligne

35

lire de certains caractres de la chane (ce nest pas le cas dans notre exemple). Il est prfrable de prendre lhabitude de placer entre guillemets les chanes passes printf, car il est trs facile de les oublier.

Voir aussi
help printf ; http://www.opengroup.org/onlinepubs/009695399/functions/printf.html ; Le shell bash de Cameron Newham (ditions OReilly), page 171, ou toute documentation de rfrence sur la fonction printf du langage C ; la recette 15.6, crire une commande echo portable, page 342 ; la recette 19.11, Constater un comportement trange de printf, page 497 ; la section printf, page 540.

2.4. crire la sortie sans le saut de ligne


Problme
Vous souhaitez que le saut de ligne par dfaut gnr par echo soit retir de la sortie.

Solution
Avec printf, il suffit denlever le caractre \n dans la chane de format. Pour echo, utilisez loption -n.
$ printf "%s %s" invite suivante invite suivante$

Ou :
$ echo -n invite invite$

Discussion
Puisque la chane de format (le premier argument) de printf ninclut aucun caractre de saut de ligne, le caractre dinvite ($) apparat immdiatement droite de la chane affiche. Cette caractristique est utile dans les scripts shell, lorsque laffichage de la sortie est ralis sur plusieurs instructions, avant de terminer la ligne, ou bien lorsque vous voulez afficher une invite lutilisateur avant de lire les donnes en entre. Avec la commande echo, il existe deux manires dliminer le saut de ligne. Premirement, loption -n supprime le saut de ligne final. La commande echo dispose galement de plusieurs squences dchappement ayant une signification particulire, similaires celles des chanes du langage C (par exemple, \n pour le saut de ligne). Pour les utiliser, vous devez invoquer echo avec loption -e. Lune des squences dchappement est \c. Elle naffiche pas un caractre, mais empche la gnration du saut de ligne final. Voici donc une deuxime solution :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

36
$ echo -e 'salut\c' salut$

Chapitre 2 Sortie standard

La commande printf est puissante, offre une grande souplesse de mise en forme et implique peu de surcot. En effet, il sagit dune commande interne, contrairement dautres shells ou danciennes versions de bash, dans lesquels printf est un programme excutable distinct. Pour toutes ces raisons, nous lutiliserons dans plusieurs exemples de ce livre.

Voir aussi
help echo ; help printf ; http://www.opengroup.org/onlinepubs/009695399/functions/printf.html ; le chapitre 3, Entre standard, page 59, notamment la recette 3.5, Lire lentre de lutilisateur, page 64 ; la recette 2.3, Mettre en forme la sortie, page 34 ; la recette 15.6, crire une commande echo portable, page 342 ; la recette 19.11, Constater un comportement trange de printf, page 497 ; la section Options et squences dchappement de echo, page 539 ; la section printf, page 540.

2.5. Enregistrer la sortie dune commande


Problme
Vous souhaitez conserver la sortie gnre par une commande en la plaant dans un fichier.

Solution
Utilisez le symbole > pour indiquer au shell de rediriger la sortie vers un fichier. Par exemple :
$ echo il faut le remplir il faut le remplir $ echo il faut le remplir > fichier.txt $

Vrifions le contenu de fichier.txt :


$ cat fichier.txt il faut le remplir $

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.6. Enregistrer la sortie vers dautres fichiers

37

Discussion
La premire ligne de lexemple montre une commande echo dont les trois arguments sont affichs. La deuxime ligne de code utilise > pour rediriger cette sortie vers le fichier nomm fichier.txt. Cest pourquoi, aucune sortie napparat aprs la commande echo. La seconde partie de lexemple invoque la commande cat pour afficher le contenu du fichier. Vous pouvez constater quil contient la sortie de la commande echo. La commande cat tire son nom du mot concatnation. Elle concatne la sortie des fichiers indiqus sur la ligne de commande, comme dans cat fichier1 fichierdeux autrefichier encoresdesfichiers. Le contenu de ces fichiers est envoy, lun aprs lautre, sur la fentre de terminal. Si un fichier volumineux a t coup en deux, il peut nouveau tre reconstitu (cest--dire concatn) en envoyant la sortie vers un troisime fichier :
$ cat premiere.moitie deuxieme.moitie > fichier.entier

Notre commande cat fichier.txt nest donc quun cas trivial de concatnation dun seul fichier, avec le rsultat affich lcran. Mme si cat offre dautres fonctionnalits, elle est principalement utilise pour afficher le contenu dun fichier lcran.

Voir aussi
man cat ; la recette 17.21, Numroter les lignes, page 467.

2.6. Enregistrer la sortie vers dautres fichiers


Problme
Vous souhaitez enregistrer la sortie vers dautres emplacements que le rpertoire courant.

Solution
Prcisez un nom de chemin lors de la redirection de la sortie. Par exemple :
$ echo des donnes supplmentaires > /tmp/echo.out

Ou :
$ echo des donnes supplmentaires > ../../par.ici

Discussion
Le nom de fichier plac derrire le caractre de redirection (>) est en ralit un nom de chemin. Sil ne commence pas par des qualificateurs, le fichier est plac dans le rpertoire courant.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

38

Chapitre 2 Sortie standard

Si le nom de fichier commence par une barre oblique (/), le nom de chemin est alors absolu et le fichier est plac dans la hirarchie du systme de fichiers indique (larborescence), qui commence la racine ( condition que tous les rpertoires intermdiaires existent et que vous ayez lautorisation de les traverser). Nous avons utilis /tmp car ce rpertoire est universellement disponible sur pratiquement tous les systmes Unix. Dans cet exemple, le shell cre le fichier nomm echo.out dans le rpertoire /tmp. Notre deuxime exemple place la sortie dans ../../par.ici en utilisant un chemin relatif. La partie .. est un rpertoire au nom particulier qui existe dans chaque rpertoire et qui fait rfrence au rpertoire parent. Ainsi, chaque occurrence de .. remonte dun niveau dans larborescence du systme de fichiers (vers la racine, qui pourtant ne se trouve pas habituellement au sommet dun arbre). Le point important ici est que nous pouvons rediriger la sortie, si nous le souhaitons, vers un fichier plac dans un rpertoire totalement diffrent de celui dans lequel la commande est excute.

Voir aussi
Le shell bash de Cameron Newham (ditions OReilly), pages 710, pour une introduction aux fichiers, aux rpertoires et la notation pointe (cest--dire . et ..).

2.7. Enregistrer la sortie de la commande ls


Problme
Vous avez essay denregistrer la sortie dune commande ls laide dune redirection, mais le format du fichier rsultant ne vous convient pas.

Solution
Utilisez loption -C de ls lorsque vous redirigez la sortie. Voici une commande ls qui affiche le contenu dun rpertoire :
$ ls a.out $ cong.txt def.conf fichier.txt autre.txt zebres.liste

Lorsque la sortie est redirige vers un fichier par >, le contenu de celui-ci est :
$ ls > /tmp/enreg.out $ cat /tmp/enreg.out a.out cong.txt def.conf fichier.txt autre.txt zebres.liste $

Utilisons prsent loption -C :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.8. Envoyer la sortie et les erreurs vers des fichiers diffrents


$ ls -C > /tmp/enreg.out $ cat /tmp/enreg.out a.out cong.txt def.conf $

39

fichier.txt autre.txt zebres.liste

Nous pouvons galement choisir loption -1 de ls sans la redirection pour obtenir la prsentation suivante :
$ ls -1 a.out cong.txt def.conf fichier.txt autre.txt enreg.out zebres.liste $

La premire tentative de redirection permet alors dobtenir cette sortie.

Discussion
Alors que vous estimiez comprendre le fonctionnement de la redirection, vous lessayez sur une simple commande ls et vous nobtenez pas ce que vous attendiez. La redirection du shell est conue pour tre transparente tous les programmes. Ils ne contiennent donc aucun code particulier pour que leur sortie soit redirigeable. Le shell prend en charge la redirection de la sortie laide du symbole >. Cependant, un programme peut contenir du code qui dtermine si sa sortie a t redirige. Il peut alors se comporter diffremment et cest le cas de ls. Les programmeurs de ls ont estim quune sortie dirige vers lcran doit tre affiche en colonne (option -C), puisque lespace disponible est limit. En revanche, si elle est dirige vers un fichier, ils ont considr que vous prfreriez sans doute avoir un fichier par ligne (option -1). En effet, vous pouvez alors utiliser celui-ci pour dautres oprations (un post-traitement), plus faciles mettre en uvre si chaque nom de fichier se trouve sur sa propre ligne.

Voir aussi
man ls ; la recette 2.6, Enregistrer la sortie vers dautres fichiers, page 37.

2.8. Envoyer la sortie et les erreurs vers des fichiers diffrents


Problme
Vous attendez une sortie dun programme, mais vous ne voulez pas quelle soit pollue par les messages derreur. Vous souhaitez enregistrer les messages derreur, mais il est plus difficile de les retrouver lorsquils sont mlangs la sortie.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

40

Chapitre 2 Sortie standard

Solution
Redirigez la sortie et les messages derreur vers des fichiers diffrents :
$ programme 1> messages.out 2> message.err

Ou encore :
$ programme > messages.out 2> message.err

Discussion
Dans cet exemple, le shell cre deux fichiers de sortie distincts. Le premier, messages.out, va recevoir la sortie gnre par programme. Si ce programme produit des messages derreur, ils sont redirigs vers message.err. Dans les syntaxes 1> et 2>, le chiffre est un descripteur de fichier. 1 correspond STDOUT et 2 STDERR. Lorsque le chiffre est absent, la sortie par dfaut est STDOUT.

Voir aussi
la recette 2.6, Enregistrer la sortie vers dautres fichiers, page 37 ; la recette 2.13, Oublier la sortie, page 43.

2.9. Envoyer la sortie et les erreurs vers le mme fichier


Problme
Grce une redirection, les messages derreur et de sortie peuvent tre enregistrs dans des fichiers spars, mais comment les placer dans un mme fichier ?

Solution
Utilisez la syntaxe du shell pour rediriger les messages derreur standard vers la mme destination que la sortie standard. Voici la version recommande :
$ lesDeux >& fichierSortie

Ou encore :
$ lesDeux &> fichierSortie

La version suivante est plus ancienne et plus longue :


$ lesDeux > fichierSortie 2>&1 lesDeux est simplement notre programme (imaginaire) qui gnre une sortie vers STDERR et vers STDOUT.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.10. Ajouter la sortie un fichier existant

41

Discussion
&> et >& sont simplement des raccourcis qui redirigent STDOUT et STDERR vers la mme destination. Cest prcisment ce que nous souhaitons. Dans le troisime exemple, 1 semble tre la cible de la redirection, mais >& indique que 1 doit tre considr comme un descripteur de fichier la place dun nom de fichier. En ralit, 2>& constitue une seule entit, prcisant que la sortie standard (2) est redirige (>) vers le descripteur de fichier (&) qui suit (1). 2>& doit tre utilis tel quel, sans espace, ou 2 sera considr comme un autre argument et & aura une signification totalement diffrente (excuter la commande en arrire-plan). Pour vous aider, vous pouvez considrer que tous les oprateurs de redirection commencent par un chiffre (par exemple 2>), mais que le chiffre par dfaut de > est 1, cest-dire le descripteur de fichier de la sortie standard. Vous pouvez galement effectuer la redirection dans lautre sens, mme si elle est moins lisible, et rediriger la sortie standard vers la destination de lerreur standard :
$ lesDeux 2> fichierSortie 1>&2

1 dsigne la sortie standard et 2 lerreur standard. En suivant notre raisonnement prcdent, nous aurions pu crire la dernire redirection sous la forme >&2, puisque 1 est le descripteur par dfaut de >. Cependant, nous estimons que la ligne est plus facile lire lorsque les chiffres sont indiqus explicitement dans la redirection vers des fichiers. Faites attention lordre du contenu dans le fichier de sortie. Les messages derreurs peuvent parfois apparatre plus tt dans le fichier qu lcran. Ce comportement est li au fait que lerreur standard nutilise pas de tampons. Leffet est plus prononc lorsque lcriture se fait dans un fichier la place de lcran.

Voir aussi
la recette 2.6, Enregistrer la sortie vers dautres fichiers, page 37 ; la recette 2.13, Oublier la sortie, page 43.

2.10. Ajouter la sortie un fichier existant


Problme
Chaque fois que vous redirigez la sortie, un nouveau fichier est cr. Comment pouvezvous la rediriger une deuxime (une troisime, etc.) fois sans craser le fichier prcdemment obtenu ?

Solution
Les doubles symboles suprieurs (>>) sont un redirecteur bash qui signifie ajouter la sortie :
$ ls > /tmp/ls.out $ cd ../ailleurs

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

42
$ ls >> /tmp/ls.out $ cd ../autrerep $ ls >> /tmp.ls.out $

Chapitre 2 Sortie standard

Discussion
La premire ligne comporte une redirection qui supprime le fichier sil existait dj et envoie la sortie de la commande ls vers un nouveau fichier vide. Les deuxime et troisime invocations de ls emploient le double symbole suprieur (>>) pour indiquer que la sortie doit tre ajoute au fichier et non le remplacer.

Voir aussi
la recette 2.6, Enregistrer la sortie vers dautres fichiers, page 37 ; la recette 2.13, Oublier la sortie, page 43.

2.11. Utiliser seulement le dbut ou la fin dun fichier


Problme
Vous souhaitez afficher ou utiliser uniquement le dbut ou la fin dun fichier.

Solution
Utilisez les commandes head ou tail. Par dfaut, head affiche les dix premires lignes dun fichier, tandis que tail en affiche les dix dernires. Si plusieurs fichiers sont donns en argument, les dix lignes correspondantes de chacun sont affiches. Loption -nombre (par exemple -5) ajuste le nombre de lignes produites en sortie. tail dispose galement des options -f et -F qui continuent afficher la fin du fichier au fur et mesure que des lignes lui sont ajoutes. Son option + est galement trs intressante, comme nous le verrons la recette 2.12, page 43.

Discussion
Avec cat, grep, sort, cut et uniq, head et tail font partie des outils Unix de manipulation de texte les plus employs. Si vous ne les connaissez pas encore, vous vous rendrez rapidement compte quelles sont indispensables.

Voir aussi
la recette 2.12, Sauter len-tte dun fichier, page 43 ; la recette 7.1, Rechercher une chane dans des fichiers, page 150 ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.12. Sauter len-tte dun fichier


la recette 8.1, Trier votre affichage, page 171 ; la recette 8.4, Couper des parties de la sortie, page 176 ; la recette 8.5, Retirer les lignes identiques, page 177 ; la recette 17.21, Numroter les lignes, page 467.

43

2.12. Sauter len-tte dun fichier


Problme
Vous disposez dun fichier contenant une ou plusieurs lignes den-tte et souhaitez traiter uniquement les donnes, en sautant cet en-tte.

Solution
Utilisez la commande tail avec un argument particulier. Par exemple, voici comment sauter la premire ligne dun fichier :
$ tail +2 lignes Ligne 2 Ligne 4 Ligne 5

Discussion
En passant tail un argument constitu dun tiret (-) et dun nombre, vous indiquez le nombre de ligne afficher partir de la fin du fichier. Ainsi, tail -10 fichier prsente les dix dernires lignes de fichier, ce qui correspond au comportement par dfaut. En revanche, si le nom est prcd dun signe plus (+), il correspond un dcalage par rapport au dbut du fichier. Par consquent, tail +1 fichier affiche lintgralit du fichier, tout comme cat. +2 passe la premire ligne, etc.

Voir aussi
man tail ; la recette 13.11, Configurer une base de donnes MySQL, page 271.

2.13. Oublier la sortie


Problme
Parfois, vous ne souhaitez pas enregistrer la sortie dans un fichier. En ralit, vous voulez mme quelquefois ne plus la voir.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

44

Chapitre 2 Sortie standard

Solution
Redirigez la sortie vers /dev/null :
$ find / -name fichier -print 2> /dev/null

Ou :
$ bavard >/dev/null 2>&1

Discussion
Vous pourriez rediriger la sortie vers un fichier, puis le supprimer. Il existe cependant une solution plus simple. Les systmes Unix et Linux disposent dun priphrique spcial qui ne correspond pas du matriel rel, mais une benne bits dans laquelle vous pouvez vider les donnes inutiles. Ce priphrique se nomme /dev/null. Toutes les donnes crites sur ce priphrique disparaissent simplement et noccupent donc aucune place sur le disque. La redirection facilite son utilisation. Dans le premier exemple, seules les informations envoyes sur lerreur standard sont jetes. Dans le deuxime, les donnes sur la sortie et lerreur standard disparaissent. Parfois, mais ce cas est rare, vous pourriez vous trouver devant un systme de fichiers /dev en lecture seule (par exemple, sur certains serveurs dinformation scuriss). Dans cette situation, la solution consistant crire dans un fichier puis le supprimer nest plus envisageable.

Voir aussi
la recette 2.6, Enregistrer la sortie vers dautres fichiers, page 37.

2.14. Enregistrer ou runir la sortie de plusieurs commandes


Problme
Vous souhaitez capturer la sortie par une redirection, mais vous excutez plusieurs commandes sur une seule ligne.
$ pwd; ls; cd ../ailleurs; pwd; ls > /tmp/tout.out

La redirection place la fin de la ligne ne sapplique qu la dernire commande, cest-dire la dernire commande ls. Les sorties de toutes les autres commandes apparaissent lcran. Autrement dit, elles ne sont pas rediriges.

Solution
Utilisez les accolades { } pour regrouper les commandes. La redirection sapplique alors la sortie de toutes les commandes du groupe. Par exemple :
$ { pwd; ls; cd ../ailleurs; pwd; ls; } > /tmp/tout.out

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.14. Enregistrer ou runir la sortie de plusieurs commandes

45

Cette solution recle quelques piges subtils. Les accolades sont en ralit des mots rservs et doivent donc tre entoures despaces. De mme, le point-virgule plac aprs la dernire commande du groupe est obligatoire (avant laccolade fermante).

Vous pourriez galement utiliser les parenthses ( ) pour demander bash dexcuter les commandes dans un sous-shell, puis de rediriger lintgralit de la sortie produite par ce sous-shell. Par exemple :
$ (pwd; ls; cd ../ailleurs; pwd; ls) > /tmp/tout.out

Discussion
Bien que ces deux solutions semblent similaires, elles prsentent deux diffrences importantes. La premire est dordre syntaxique, la seconde smantique. Syntaxiquement, les accolades doivent tre entoures despaces et la dernire commande de la liste doit se terminer par un point-virgule. Tout cela nest pas obligatoire avec les parenthses. Cependant, la diffrence la plus importante est dordre smantique, cest--dire la signification des constructions. Les accolades ne sont quun mcanisme permettant de regrouper plusieurs commandes afin de ne pas tre oblig de rediriger sparment chacune delles. En revanche, les commandes places entre parenthses sexcutent dans une autre instance du shell ; dans un sous-shell cr par le shell courant. Le sous-shell est quasiment identique au shell courant. Les variables denvironnement, y compris $PATH, sont les mmes, mais les signaux sont traits diffremment (nous reviendrons sur les signaux la recette 10.6, page 215). Il existe donc une grande diffrence avec lapproche sous-shell. Dans ce cas, les commandes cd sont excutes dans le sousshell et, lorsque celui-ci se termine, le shell principal na pas chang dtat. Autrement dit, le rpertoire de travail est le mme et ses variables ont toujours les mmes valeurs. Si vous utilisez des accolades, le rpertoire de travail change (../ailleurs dans notre exemple). Toute autre modification apporte, par exemple aux variables, se font dans linstance en cours du shell. Mme si les deux approches donnent le mme rsultat, leurs effets sont trs diffrents. Avec les accolades, vous pouvez crer des blocs de branchement plus concis (voir la recette 6.2, page 116). Par exemple, vous pouvez rduire le code suivant :
if [ $resultat = 1 ]; then echo "Le rsultat est 1 ; excellent." exit 0 else echo "Ouh l l, disparaissez ! " exit 120 fi

en celui-ci :
[ $resultat = 1 ] \ && { echo "Le rsultat est 1 ; excellent." ; exit 0; || { echo "Ouh l l, disparaissez ! " ; exit 120; } } \

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

46

Chapitre 2 Sortie standard

Vous choisirez la solution qui correspond votre style ou celle que vous pensez la plus lisible.

Voir aussi
la recette 6.2, Conditionner lexcution du code, page 116 ; la recette 10.6, Intercepter les signaux, page 215 ; la recette 15.1, Trouver bash de manire portable, page 334 ; la recette 19.5, Sattendre pouvoir modifier les variables exportes, page 490 ; la recette 19.8, Oublier que les tubes crent des sous-shells, page 493 ; la section Variables internes, page 510, pour en savoir plus sur BASH_SUBSHELL.

2.15. Relier une sortie une entre


Problme
Vous souhaitez que la sortie dun programme serve dentre un autre programme.

Solution
Vous pouvez rediriger la sortie du premier programme vers un fichier temporaire, puis utilisez celui-ci comme entre du deuxime programme. Par exemple :
$ cat un.fichier unAutre.fichier > /tmp/cat.out $ sort < /tmp/cat.out ... $ rm /tmp/cat.out

Vous pouvez galement runir toutes ces tapes en une seule, en envoyant directement la sortie vers le deuxime programme grce au symbole | (tube) :
$ cat un.fichier unAutre.fichier | sort

Rien ne vous interdit de lier plusieurs commandes en utilisant autant de tubes que ncessaire :
$ cat mes* | tr 'a-z' 'A-Z' | uniq | awk -f transformation.awk | wc

Discussion
Grce aux tubes, vous navez pas inventer un nom de fichier temporaire, vous en souvenir, puis ne pas oublier de le supprimer. Des programmes comme sort sont capables de lire sur lentre standard (par une redirection avec le symbole <), mais galement partir dun fichier. Par exemple :
$ sort /tmp/cat.out

au lieu de rediriger lentre vers sort :


$ sort < /tmp/cat.out

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.16. Enregistrer une sortie redirige vers une entre

47

Ce fonctionnement (utiliser le fichier indiqu ou lentre standard) est une caractristique classique des systmes Unix/Linux. Cest un modle suivre lorsque des commandes doivent tre relies les unes aux autres par le mcanisme de tube. Si vous crivez vos programmes et vos scripts shell ainsi, ils vous seront plus utiles, ainsi quaux personnes avec qui vous partagez votre travail. Vous pouvez tre bahi par la puissante simplicit du mcanisme de tube et mme le voir comme un mcanisme de traitement parallle rudimentaire. Deux commandes (programmes) peuvent sexcuter en parallle et partager des donnes ; la sortie de lune est lentre de lautre. Elles nont pas sexcuter squentiellement (la premire devant tre termine avant que la seconde puisse dmarrer). La deuxime commande reoit des donnes ds quelles sont fournies par la premire. Cependant, vous devez savoir que les commandes excutes de cette manire (cest--dire, connectes par des tubes) utilisent des sous-shells spars. Mme si cette subtilit peut souvent tre ignore, elle a parfois des implications trs importantes. Nous y reviendrons la recette 19.8, page 493. Prenons lexemple dune commande comme svn -v log | less. Si less se termine avant que Subversion ait fini denvoyer des donnes, vous obtenez une erreur comme svn: Write error: Broken pipe . Mme si ce nest pas trs agrable, ce nest pas grave. Cela se produit ds que vous envoyez de grandes quantits de donnes des programmes comme less. En gnral, vous quittez less ds que vous avez trouv ce que vous recherchiez, mme si dautres donnes transitent encore par le tube.

Voir aussi
la recette 3.1, Lire les donnes dentre depuis un fichier, page 59 ; la recette 19.8, Oublier que les tubes crent des sous-shells, page 493.

2.16. Enregistrer une sortie redirige vers une entre


Problme
Vous souhaitez dboguer une longue suite dentres/sorties lies par des tubes, comme la suivante :
$ cat mes* | tr 'a-z' 'A-Z' | uniq | awk -f transformation.awk | wc

Comment pouvez-vous savoir ce qui se passe entre uniq et awk, sans interrompre la suite de tubes ?

Solution
La solution ce problme consiste utiliser ce que les plombiers appellent un raccord en T. Pour bash, il sagit dutiliser la commande tee afin de dcomposer la sortie en deux f lux identiques, lun dirig vers un fichier, lautre sur la sortie standard afin de ne pas interrompre les tubes.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

48

Chapitre 2 Sortie standard

Dans cet exemple, nous analysons le fonctionnement dune longue suite de tubes en insrant la commande tee entre les commandes uniq et awk :
$ ... uniq | tee /tmp/x.x | awk -f transformation.awk ...

Discussion
La commande tee crit la sortie vers le fichier indiqu en paramtre, ainsi que vers la sortie standard. Dans cet exemple, les donnes sont envoyes dans /tmp/x.x et vers awk, cest--dire la commande laquelle la sortie de tee est connecte via le symbole |. Ne vous proccupez pas du fonctionnement des diffrentes commandes de ces exemples. Nous voulons uniquement illustrer lemploi de tee dans diffrents cas. Commenons par une ligne de commande plus simple. Supposez que vous vouliez enregistrer la sortie dune commande longue afin de la consulter plus tard, tout en suivant le rsultat de son excution lcran. Une commande telle que :
find / -name '*.c' -print | less

risque de trouver un grand nombre de fichiers sources C et donc de remplir plus que la fentre. En utilisant more ou less, vous pouvez examiner plus facilement la sortie, mais, une fois la commande termine, vous ne pouvez pas revoir la sortie sans relancer la commande. Bien entendu, vous pouvez excuter la commande et enregistrer le rsultat dans un fichier :
find / -name '*.c' -print > /tmp/toutes.mes.sources

Cependant, vous devez attendre quelle soit termine avant de consulter le fichier. (Daccord, nous avons tail -f, mais ce nest pas le propos ici.) La commande tee peut remplacer une simple redirection de la sortie standard :
find / -name '*.c' -print | tee /tmp/toutes.mes.sources

Dans cet exemple, puisque la sortie de tee nest pas redirige, elle est affiche lcran. En revanche, la copie de la sortie est envoye vers un fichier, qui pourra tre examin par la suite (par exemple, cat /tmp/toutes.mes.sources). Vous remarquerez galement que, dans ces exemples, nous navons pas redirig lerreur standard. Cela signifie que les erreurs, comme celles que find pourrait gnrer, seront affiches lcran, sans apparatre dans le fichier de tee. Pour enregistrer les erreurs, vous pouvez ajouter 2>&1 la commande find :
find / -name '*.c' -print 2>&1 | tee /tmp/toutes.mes.sources

Elles ne seront pas spares de la sortie standard, mais elles seront au moins conserves.

Voir aussi
man tee ; la recette 18.5, Rutiliser des arguments, page 480 ; la recette 19.13, Dboguer des scripts, page 500.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.17. Connecter des programmes en utilisant la sortie comme argument

49

2.17. Connecter des programmes en utilisant la sortie comme argument


Problme
Que pouvez-vous faire si lun des programmes connect par un tube ne fonctionne pas selon ce principe ? Par exemple, vous pouvez supprimer des fichiers laide de la commande rm en les passant en paramtres :
$ rm mon.java votre.c leur.*

En revanche, puisque rm ne lit pas son entre standard, la ligne suivante ne fonctionnera pas :
find . -name '*.c' | rm

rm reoit les noms de fichiers uniquement via les arguments de la ligne de commande. Comment la sortie dune commande prcdente (par exemple, echo ou ls) peut-elle alors tre place sur la ligne de commande ?

Solution
Utilisez la substitution de commande de bash :
$ rm $(find . -name '*.class') $

Discussion
$( ) englobe une commande excute dans un sous-shell. La sortie de cette commande est mise la place de $( ). Les sauts de lignes prsents dans la sortie sont remplacs par une espace (en ralit, le premier caractre de $IFS, qui, par dfaut, est une espace). Par consquent, les diffrentes lignes de la sortie deviennent des paramtres sur la ligne de commande. Lancienne syntaxe du shell employait des apostrophes inverses (``) la place de $( ). Cette nouvelle syntaxe est conseille car elle est plus facile imbriquer et, peut-tre, plus facile lire. Cependant, vous rencontrerez `` probablement plus souvent que $( ), notamment dans les anciens scripts ou ceux crits par des personnes ayant connu les shells Bourne ou C. Dans notre exemple, la sortie de find, gnralement une liste de noms, est convertie en arguments pour la commande rm. Attention : soyez trs prudent lorsque vous utilisez cette possibilit car rm ne pardonne pas. Si la commande find trouve dautres fichiers, en plus de ceux attendus, rm les supprimera sans vous laisser le choix. Vous ntes pas sous Windows ; vous ne pouvez rcuprer les fichiers supprims partir de la corbeille. La commande rm -i permet de rduire les risques, en vous invitant valider chaque suppression. Si cette solution peut tre envisage avec un petit nombre de fichiers, elle devient vite laborieuse ds quil augmente.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

50

Chapitre 2 Sortie standard

Pour employer ce mcanisme dans bash avec de meilleures garanties, commencez par excuter la commande interne. Si les rsultats obtenus vous conviennent, placez-la dans la syntaxe $( ). Par exemple :
$ find . -name '*.class' Premier.class Autre.class $ rm $(find . -name '*.class') $

La recette 18.2, page 477, expliquera comment rendre ce mcanisme encore plus fiable, en utilisant !! au lieu de saisir nouveau la commande find.

Voir aussi
la recette 15.13, Contourner les erreurs liste darguments trop longue , page 357 ; la recette 18.2, Rpter la dernire commande, page 477.

2.18. Placer plusieurs redirections sur la mme ligne


Problme
Vous souhaitez rediriger une sortie vers plusieurs destinations.

Solution
Utilisez une redirection avec des descripteurs de fichiers afin douvrir tous les fichiers que vous souhaitez utiliser. Par exemple :
$ devier 3> fichier.trois 4> fichier.quatre 5> fichier.cinq 6> ailleurs $

devier peut tre un script shell contenant diffrentes commandes dont les sorties doivent tre envoyes vers diffrentes destinations. Par exemple, vous pouvez crire un script devier contenant des lignes de la forme echo option $OPTSTR >&5. Autrement dit, devier peut envoyer sa sortie vers diffrents descripteurs, que le programme appelant peut rediriger vers diffrentes destinations. De manire similaire, si devier est un programme C excutable, vous pourriez crire sur les descripteurs de fichiers 3, 4, 5 et 6 sans passer par des appels open().

Discussion
La recette 2.8, page 39, a expliqu que chaque descripteur de fichier est indiqu par un nombre, en commenant 0 (zro). Ainsi, lentre standard est le descripteur 0, la sortie standard correspond au descripteur 1 et lerreur standard 2. Vous pouvez donc rediriger la sortie standard en utilisant 1> ( la place dun simple >) suivi dun nom de fichier, mais ce nest pas obligatoire. La version abrge > convient parfaitement. Mais, cela si-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.19. Enregistrer la sortie lorsque la redirection semble inoprante

51

gnifie galement que le shell peut ouvrir un nombre quelconque de descripteurs de fichier et les associer diffrents fichiers, pour que le programme invoqu ensuite depuis la ligne de commande puisse les utiliser. Bien que nous ne recommandons pas cette technique, nous devons admettre quelle est assez tonnante.

Voir aussi
la recette 2.6, Enregistrer la sortie vers dautres fichiers, page 37 ; la recette 2.8, Envoyer la sortie et les erreurs vers des fichiers diffrents, page 39 ; la recette 2.13, Oublier la sortie, page 43.

2.19. Enregistrer la sortie lorsque la redirection semble inoprante


Problme
Vous essayez dutiliser > mais certains messages de sortie (voire tous) apparaissent encore lcran. Par exemple, le compilateur produit des messages derreur :
$ gcc mauvais.c mauvais.c: In function `main': mauvais.c:3: error: `mauvais' undeclared (first use in this function) mauvais.c:3: error: (Each undeclared identifier is reported only once mauvais.c:3: error: for each function it appears in.) mauvais.c:3: error: parse error before "c" $

Vous souhaitez capturer ces messages et tentez donc de rediriger la sortie :


$ gcc mauvais.c > messages.erreur mauvais.c: In function `main': mauvais.c:3: error: `mauvais' undeclared (first use in this function) mauvais.c:3: error: (Each undeclared identifier is reported only once mauvais.c:3: error: for each function it appears in.) mauvais.c:3: error: parse error before "c" $

Malheureusement, cela ne semble pas fonctionner. En ralit, lorsque vous examinez le fichier dans lequel la sortie est cense aller, vous constatez quil est vide :
$ ls -l messages.erreur -rw-r--r-- 1 jp jp 0 2007-06-20 15:30 messages.erreur $ cat messages.erreur $

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

52

Chapitre 2 Sortie standard

Solution
Redirigez la sortie derreur de la manire suivante :
$ gcc mauvais.c 2> messages.erreur $

messages.erreur contient prsent les messages derreur qui taient affichs lcran.

Discussion
Que se passe-t-il donc ? Tout processus Unix ou Linux dmarre gnralement avec trois descripteurs de fichier ouverts : un pour lentre standard (STDIN), un pour la sortie standard (STDOUT) et un pour les messages derreur, appel erreur standard (STDERR). Il revient au programmeur de respecter ces conventions, autrement dit dcrire les messages derreur sur lerreur standard et la sortie normale sur la sortie standard. Cependant, rien ne garantit que tous les messages derreur iront sur lerreur standard. La plupart des utilitaires existants de longue date se comportent ainsi. Cest pourquoi les messages du compilateur ne peuvent tre dvis par une simple redirection >. En effet, elle ne redirige que la sortie standard, non lerreur standard. Chaque descripteur de fichier est indiqu par un nombre, en commenant 0. Lentre standard est donc reprsente par 0, la sortie standard par 1 et lerreur standard par 2. Cela signifie que vous pouvez donc rediriger la sortie standard en utilisant 1> ( la place dun simple >) suivi dun nom de fichier, mais ce nest pas obligatoire. La version abrge > convient parfaitement. Il existe une diffrence entre la sortie et lerreur standard. La premire utilise un tampon, contrairement lerreur standard. Autrement dit, chaque caractre envoy sur lerreur standard est crit individuellement et non mmoris puis crit comme un tout. Les messages derreur sont affichs immdiatement, ce qui permet dviter leur perte en cas de problmes, mais lefficacit sen trouve diminue. Nous ne prtendons pas que la sortie standard nest pas fiable, mais, dans des situations de dysfonctionnement (par exemple, un programme qui se termine de faon impromptue), le contenu du tampon risque ne de pas arriver sur lcran avant la fin du programme. Cest la raison pour laquelle lerreur standard nutilise pas un tampon ; il faut que les messages soient affichs. En revanche, la sortie standard passe par un tampon. Les donnes sont crites uniquement lorsque le tampon est plein ou que le fichier est ferm. Cette solution est plus efficace pour une sortie frquemment sollicite. Cependant, lefficacit devient un aspect secondaire en cas derreurs. Si vous souhaitez voir la sortie en cours denregistrement, utilisez la commande tee prsente la recette 2.16, page 47 :
$ gcc mauvais.c 2>&1 | tee messages.erreur

Lerreur standard est ainsi redirige vers la sortie standard, et toutes deux sont envoyes tee. Cette commande crit son entre dans le fichier indiqu (messages.erreur), ainsi que sur sa sortie standard, qui saffiche lcran puisquelle na pas t redirige. Cette redirection est un cas particulier car lordre des redirections a normalement de limportance. Comparez les deux commandes suivantes :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.20. Permuter STDERR et STDOUT


$ uneCommande >mon.fichier 2>&1 $ uneCommande 2>&1 >mon.fichier

53

Dans le premier cas, la sortie standard est redirige vers un fichier (mon.fichier) et lerreur standard est redirige vers la mme destination que la sortie standard. Les deux sorties apparaissent donc dans mon.fichier. La deuxime commande fonctionne de manire diffrente. Lerreur standard est tout dabord redirige vers la sortie standard (qui, ce stade, est associe lcran), puis la sortie standard est envoye vers mon.fichier. Par consquent, seuls les messages de la sortie standard sont placs dans le fichier, tandis que les erreurs apparaissent lcran. Cependant, cet ordre doit tre invers pour les tubes. En effet, vous ne pouvez pas placer la deuxime redirection aprs le symbole de tube, puisquaprs le tube vient la commande suivante. bash fait donc une exception lorsque vous crivez la ligne suivante et reconnat que la sortie standard est dirige vers un tube :
$ uneCommande 2>&1 | autreCommande

Il suppose donc que 2>&1 signifie que vous souhaitez inclure lerreur standard dans le tube, mme si lordre normal correspond un fonctionnement diffrent. En consquence, dans un tube, il est impossible denvoyer uniquement lerreur standard, sans la sortie standard, vers une autre commande (cela est galement d la syntaxe des tubes en gnral). La seule solution consiste permuter les descripteurs de fichiers, comme lexplique la recette suivante.

Voir aussi
la recette 2.17, Connecter des programmes en utilisant la sortie comme argument, page 49 ; la recette 2.20, Permuter STDERR et STDOUT, page 53.

2.20. Permuter STDERR et STDOUT


Problme
Pour envoyer STDOUT dans un fichier et STDERR vers lcran et dans un fichier laide de la commande tee, vous devez permuter STDERR et STDOUT. Cependant, les tubes ne fonctionnent quavec STDOUT.

Solution
changez STDERR et STDOUT avant la redirection du tube en utilisant un troisime descripteur de fichier :
$ ./monScript 3>&1 1>stdout.journal 2>&3- | tee -a stderr.journal

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

54

Chapitre 2 Sortie standard

Discussion
Lorsque vous redirigez des descripteurs de fichiers, vous dupliquez le descripteur ouvert vers un second. Vous pouvez ainsi permuter des descripteurs, pratiquement de la mme manire quun programme change deux valeurs, cest--dire au travers dun troisime intermdiaire temporaire. Voici comment procder : copier A dans C, copier B dans A, copier C dans B. Les valeurs de A et de B sont alors changes. Pour les descripteurs de fichiers, lopration se passe de la manire suivante :
$ ./monScript 3>&1 1>&2 2>&3

La syntaxe 3>&1 signifie donner au descripteur de fichier 3 la mme valeur que le descripteur de fichier de sortie 1 . Plus prcisment, le descripteur de fichier 1(STDOUT) est dupliqu dans le descripteur de fichier 3 (lintermdiaire). Ensuite, le descripteur de fichier 2 (STDERR) est dupliqu dans STDOUT. Enfin, le descripteur de fichier 3 est dupliqu dans STDERR. Au final, vous changez les descripteurs de fichiers STDERR et STDOUT. Nous devons prsent modifier lgrement cette opration. Une fois la copie de STDOUT ralise (dans le descripteur de fichier 3), nous pouvons rediriger STDOUT dans le fichier denregistrement de la sortie de notre script ou dun autre programme. Puis, nous pouvons copier le descripteur de fichier depuis lintermdiaire (le descripteur de fichier 3) dans STDERR. Lajout du tube fonctionne car il est connect au STDOUT dorigine. Cela conduit la solution propose prcdemment :
$ ./monScript 3>&1 1>stdout.journal 2>&3- | tee -a stderr.journal

Avez-vous remarqu le signe - la fin du terme 2>&3- ? Il permet de fermer le descripteur de fichier 3 lorsque nous nen avons plus besoin. Ainsi, notre programme ne laisse pas de descripteur de fichier ouvert. Noubliez pas de fermer la porte en sortant.

Voir aussi
Administration Linux 200%, 1re dition, hack n5, n>&m : permuter sortie standard et erreur standard , de Rob Flickenger (ditions OReilly) ; la recette 2.19, Enregistrer la sortie lorsque la redirection semble inoprante, page 51 ; la recette 10.1, Convertir un script en dmon, page 207.

2.21. Empcher lcrasement accidentel des fichiers


Problme
Vous ne souhaitez pas effacer par mgarde le contenu dun fichier. Il est trs facile de mal orthographier un nom de fichier et de rediriger une sortie vers un fichier que vous vouliez conserver.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.21. Empcher lcrasement accidentel des fichiers

55

Solution
Demandez au shell dtre plus prudent :
$ set -o noclobber $

Si, par la suite, vous estimez quil est inutile dtre aussi prudent, dsactivez loption :
$ set +o noclobber $

Discussion
Loption noclobber demande bash de ne pas craser un fichier existant lors de la redirection de la sortie. Si le fichier destinataire nexiste pas, tout se passe normalement : bash cre le fichier lors de son ouverture pour y placer la sortie. En revanche, si le fichier existe dj, vous recevez un message derreur. En voici un exemple. Nous commenons par dsactiver loption, uniquement pour placer le shell dans un tat connu, quelle que soit la configuration initiale du systme.
$ set +o noclobber $ echo quelquechose > mon.fichier $ echo autre chose > mon.fichier $ set -o noclobber $ echo quelquechose > mon.fichier bash: mon.fichier: cannot overwrite existing file $ echo autre chose >> mon.fichier $

Lors de la premire redirection de la sortie vers mon.fichier, le shell cre celui-ci. La deuxime fois, bash crase le fichier (il le tronque 0 octet, puis crit partir du dbut). Ensuite, nous fixons loption noclobber et recevons un message derreur lorsque nous tentons dcrire dans le fichier. En revanche, il est possible dajouter du contenu au fichier (avec >>).
Attention ! Loption noclobber ne concerne que lcrasement dun fichier lors de la redirection de la sortie au niveau du shell. Elle nempche aucune autre suppression du fichier par dautres programmes (voir la recette 14.13, page 310).
$ $ $ $ $ echo donnes inutiles > un.fichier echo donnes importantes > autre.fichier set -o noclobber cp un.fichier autre.fichier

Vous remarquerez que ces oprations ne dclenchent aucune erreur. Le fichier est copi par dessus un fichier existant. La copie est ralise avec la commande cp. Le shell nest pas impliqu.

Si vous faites trs attention lorsque vous saisissez les noms des fichiers, cette option peut tre superf lue. Cependant, nous verrons, dans dautres recettes, des noms de fichiers g-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

56

Chapitre 2 Sortie standard

nrs par des expressions rgulires ou passs comme des variables. Ces noms peuvent tre employs dans des redirections de sorties. Auquel cas, lactivation de loption noclobber apporte une certaine scurit et peut empcher certains effets secondaires indsirables (que ce soit par mgarde ou par volont de nuire).

Voir aussi
une bonne rfrence Linux sur la commande chmod et les autorisations des fichiers, par exemple : http://www.linuxforums.org/security/file_permissions.html ; http://www.comptechdoc.org/os/linux/usersguide/linux_ugfilesup.html ; http://www.faqs.org/docs/linux_intro/sect_03_04.html ; http://www.perlfect.com/articles/chmod.shtml. la recette 14.13, Fixer les autorisations, page 310.

2.22. craser un fichier la demande


Problme
Vous activez loption noclobber en permanence, mais, de temps autre, vous souhaitez craser un fichier lors de la redirection dune sortie. Est-il possible de se soustraire occasionnellement la surveillance de bash ?

Solution
Utilisez >| pour rediriger la sortie. Mme si noclobber est active, bash lignore et crase le fichier. Examinez lexemple suivant :
$ echo quelquechose > mon.fichier $ set -o noclobber $ echo autre chose >| mon.fichier $ cat mon.fichier autre chose $ echo encore une fois > mon.fichier bash: mon.fichier: cannot overwrite existing file $

Vous remarquerez que la deuxime commande echo ne produit aucun message derreur, contrairement la troisime dans laquelle la barre verticale (le tube) nest pas utilise. Lorsque le caractre > est utilis seul, le shell vous avertit et ncrase pas le fichier existant.

Discussion
Loption noclobber noutrepasse pas les autorisations de fichier. Si vous ne possdez pas le droit dcriture dans le rpertoire, vous ne pourrez crer le fichier, que vous utilisiez

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

2.22. craser un fichier la demande

57

ou non la construction >|. De manire similaire, vous devez avoir lautorisation dcriture sur le fichier lui-mme pour lcraser, avec ou sans >|. Pourquoi une barre verticale ? Peut-tre parce que le point dexclamation tait dj utilis pour autre chose par bash et que la barre verticale est, visuellement, proche du point dexclamation. Mais pourquoi le point dexclamation (!) serait-il le symbole appropri ? Tout simplement parce quil marque une insistance. En franais (avec limpratif), il pourrait vouloir dire bash fais-le, cest tout ! . Par ailleurs, lditeur vi (et ex) emploie galement ! dans le mme sens avec sa commande dcriture (:w! nomFichier). Sans le !, il refuse dcraser un fichier existant. En lajoutant, vous dites lditeur vasy!

Voir aussi
la recette 14.13, Fixer les autorisations, page 310.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

3
Entre standard
Quelle contienne des donnes pour alimenter un programme ou de simples commandes pour paramtrer le comportement dun script, lentre est tout aussi fondamentale que la sortie. La phase initiale de tout programme concerne la gestion des entres/sorties.

3.1. Lire les donnes dentre depuis un fichier


Problme
Vous souhaitez que vos commandes du shell lisent des donnes depuis un fichier.

Solution
Utilisez la redirection de lentre, symbolise par le caractre <, pour lire des donnes depuis un fichier.
$ wc < mon.fichier

Discussion
Tout comme le symbole > envoie une sortie vers un fichier, le symbole < prend une entre depuis un fichier. Le choix des caractres apporte une information visuelle sur le sens de la redirection. Certaines commandes du shell attendent un ou plusieurs fichiers en argument, mais, lorsquaucun nom nest prcis, elles se tournent vers lentre standard. Ces commandes peuvent alors tre invoques sous la forme commande nomFichier ou commande < nomFichier, avec le mme rsultat. Cet exemple illustre le cas de wc, mais cat et dautres commandes oprent de la mme manire. Cette fonctionnalit pourrait paratre secondaire et vous tre familire si vous avez dj employ la ligne de commande du DOS, mais elle est en ralit trs importante pour lcriture de scripts shell (que la ligne de commande du DOS a emprunt) et savre aussi puissante que simple.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

60

Chapitre 3 Entre standard

Voir aussi
la recette 2.6, Enregistrer la sortie vers dautres fichiers, page 37.

3.2. Conserver les donnes avec le script


Problme
Vous avez besoin de donnes dentre pour votre script, mais vous ne voulez pas quelles soient dans un fichier spar.

Solution
Utilisez un here document, avec les caractres <<, en prenant le texte depuis la ligne de commande et non depuis un fichier. Dans le cas dun script shell, le fichier du script contient les donnes ainsi que les commandes du script. Voici un exemple de script shell plac dans un fichier nomm ext :
$ cat ext # # Voici le document en ligne. # grep $1 <<EOF mike x.123 joe x.234 sue x.555 pete x.818 sara x.822 bill x.919 EOF $

Il peut tre utilis comme un script shell qui recherche le numro de poste associ une personne :
$ ext bill bill x.919 $

ou linverse :
$ ext 555 sue x.555 $

Discussion
La commande grep recherche les occurrences de son premier argument dans les fichiers nomms ou sur lentre standard. Voici les utilisations classiques de grep :
$ grep uneChane fichier.txt $ grep maVar *.c

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

3.3. Empcher un comportement trange dans un here document

61

Dans notre script ext, nous avons paramtr la commande grep en lui indiquant que la chane recherche est un argument du script shell ($1). Mme si grep est souvent employe pour rechercher une certaine chane dans plusieurs fichiers diffrents, dans notre exemple, la chane recherche varie alors que les donnes de recherche sont figes. Nous pourrions placer les numros de tlphone dans un fichier, par exemple numeros.txt et lutiliser lors de linvocation de la commande grep :
grep $1 numeros.txt

Cependant, cette approche exige deux fichiers distincts (le script et le fichier des donnes). Se pose alors la question de leur emplacement et de leur stockage conjoint. Au lieu dindiquer un ou plusieurs noms de fichiers (dans lesquels se fait la recherche), nous prparons un document en ligne et indiquons au shell de rediriger lentre standard vers ce document (temporaire). La syntaxe << prcise que nous voulons crer une source dentre temporaire et le marqueur EOF nest quune chane quelconque (vous pouvez en choisir une autre) qui joue le rle dindicateur de fin de lentre temporaire. Elle nest pas incluse dans lentre, mais signale uniquement l o lentre sarrte. Le script shell normal reprend aprs le marqueur. En ajoutant loption -i la commande grep, la recherche ne tient plus compte de la casse. Ainsi, la commande grep -i $1 <<EOF nous permettrait deffectuer la recherche sur Bill aussi bien que sur bill .

Voir aussi
man grep ; la recette 3.3, Empcher un comportement trange dans un here document, page 61 ; la recette 3.4, Indenter un here document, page 63.

3.3. Empcher un comportement trange dans un here document


Problme
Votre here document1 se comporte bizarrement. Vous essayez de grer une simple liste de donateurs en utilisant la mthode dcrite prcdemment pour les numros de tlphone. Vous avez donc cr un fichier nomm donateurs :
$ cat donateurs # # Simple recherche de nos gnreux donateurs. #

1. N.d.T. : Un here document permet de saisir un ensemble de lignes avant de les envoyer sur lentre standard dune commande.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

62
grep $1 <<EOF # nom montant pete $100 joe $200 sam $ 25 bill $ 9 EOF $

Chapitre 3 Entre standard

Mais son excution produit une sortie trange :


$ ./donateurs bill pete bill00 bill $ 9 $ ./donateurs pete pete pete00 $

Solution
Dsactivez les fonctionnalits des scripts shell dans le here document en appliquant lchappement un ou tous les caractres du marqueur de fin :
# solution grep $1 <<\EOF pete $100 joe $200 sam $ 25 bill $ 9 EOF

Discussion
La diffrence est subtile, mais <<EOF a t remplac par <<\EOF. Vous pouvez galement utiliser <<'EOF' ou mme <<E\OF. Cette syntaxe nest pas la plus lgante, mais elle suffit indiquer bash que les donnes en ligne doivent tre traites diffremment. La page de manuel de bash indique que, normalement (cest--dire sans lchappement), ... toutes les lignes du here document sont assujetties lexpansion des paramtres, la substitution de commandes et lexpansion arithmtique . Par consquent, dans la premire version du script donateurs, les montants sont considrs comme des variables du shell. Par exemple, $100 est trait comme la variable $1 suivi de deux zros. Cest pourquoi nous obtenons pete00 lorsque nous recherchons pete et bill00 pour bill . Lorsque lchappement est appliqu un ou plusieurs caractres de EOF, bash sait quil ne doit pas effectuer dexpansion et le comportement est alors celui attendu :
$ ./donateurs pete pete $100 $

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

3.4. Indenter un here document

63

Vous voudrez parfois que lexpansion du shell sapplique vos donnes, mais ce nest pas le cas ici. Nous estimons quil est prfrable de toujours appliquer lchappement au marqueur, comme dans <<'EOF' ou <<\EOF, afin dviter les rsultats inattendus. Lorsque lexpansion doit concerner vos donnes, indiquez-le explicitement en retirant lchappement.
Si le marqueur EOF est suivi despaces, mme une seule, il nest plus reconnu comme le marqueur de fin. bash absorbe alors la suite du script comme des donnes dentre et continue sa recherche de EOF. Vous devez donc vrifier trs soigneusement quaucun caractre, notamment des espaces ou des tabulations, ne se trouve aprs EOF.

Voir aussi
la recette 3.2, Conserver les donnes avec le script, page 60 ; la recette 3.4, Indenter un here document, page 63.

3.4. Indenter un here document


Problme
Le here document fonctionne parfaitement, mais il souille la parfaite mise en forme de votre script shell. Vous voulez pouvoir lindenter afin de conserver la lisibilit.

Solution
Utilisez <<-. Vous pourrez ensuite employer des caractres de tabulation (uniquement) au dbut des lignes pour indenter cette partie du script :
$ cat monScript.sh ... grep $1 <<-'EOF' Cette partie du script contient beaucoup de donnes. Elle est donc indente avec des tabulations afin de respecter lindentation du script. Les tabulations en dbut de ligne sont supprimes lors de la lecture. EOF ls ... $

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

64

Chapitre 3 Entre standard

Discussion
Le tiret plac juste aprs << indique bash quil doit ignorer les caractres de tabulation placs en dbut de ligne. Cela ne concerne que les caractres tab et non un caractre despacement quelconque. Ce point est particulirement important avec EOF ou tout autre marqueur. Si le dbut de ligne contient des espaces, EOF nest pas reconnu comme le marqueur de fin et les donnes en ligne vont jusqu la fin du fichier (le reste du script est ignor). Par consquent, vous pouvez, pour plus de scurit, toujours aligner gauche EOF (ou tout autre marqueur) et retirer la mise en forme de cette ligne.
Tout comme les espaces places aprs le marqueur EOF lempchent dtre reconnu comme la fin des donnes en ligne (voir lavertissement de la recette 3.3, page 61), tout caractre initial autre quune tabulation provoquera le mme dysfonctionnement. Si votre script base son indentation sur des espaces ou une combinaison despaces et de tabulations, nemployez pas cette solution avec les here documents. Vous devez utiliser uniquement des tabulations ou aucun caractre. Par ailleurs, mfiez-vous des diteurs de texte qui remplacent automatiquement les tabulations par des espaces.

Voir aussi
la recette 3.2, Conserver les donnes avec le script, page 60 ; la recette 3.3, Empcher un comportement trange dans un here document, page 61.

3.5. Lire lentre de lutilisateur


Problme
Vous devez obtenir des donnes dentre de la part de lutilisateur.

Solution
Utilisez linstruction read :
read

ou :
read p "merci de rpondre " REPONSE

ou :
read AVANT MILIEU APRES

Discussion
Dans sa forme la plus simple, une instruction read sans argument lit lentre de lutilisateur et la place dans la variable REPLY.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

3.6. Attendre une rponse Oui ou Non

65

Si vous souhaitez que bash affiche une invite avant la lecture de lentre, ajoutez loption -p. Le mot plac aprs -p devient linvite, mais lutilisation des guillemets vous permet de passer une chane plus longue. Noubliez pas de terminer linvite par un symbole de ponctuation et/ou une espace, car le curseur attendra lentre juste aprs la fin de la chane dinvite. Si vous prcisez plusieurs noms de variables dans linstruction read, lentre est dcompose en mots, qui sont affectes dans lordre aux variables. Si lutilisateur saisit moins de mots quil y a de variables, les variables supplmentaires sont vides. Sil le nombre de mots est suprieur au nombre de variables dans linstruction read, les mots supplmentaires sont placs dans la dernire variable de la liste.

Voir aussi
help read ; la recette 3.8, Demander un mot de passe, page 69 ; la recette 6.11, Boucler avec read, page 133 ; la recette 13.6, Analyser du texte avec read, page 266 ; la recette 14.12, Valider lentre, page 308.

3.6. Attendre une rponse Oui ou Non


Problme
Vous voulez que lutilisateur rponde simplement Oui ou Non, tout en offrant la meilleure interface possible. En particulier, la casse ne doit pas tre prise en compte et une valeur par dfaut doit tre choisie si lutilisateur appuie sur la touche Entre sans rpondre.

Solution
Si les actions raliser sont simples, servez-vous de la fonction suivante :
# bash Le livre de recettes : fonction_choisir # Laisse l'utilisateur faire un choix et excute le code selon sa rponse. # Utilisation : choisir <dfaut (o ou n)> <invite> <action oui> <action non> # Par exemple : # choisir "o" \ # "Voulez-vous jouer ce jeu ?" \ # /usr/games/GuerreThermonucleaireMondiale \ # 'printf "%b" "Au revoir Professeur Falkin."' >&2 # Retour : aucune function choisir { local defaut="$1" local invite="$2"

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

66
local choix_oui="$3" local choix_non="$4" local reponse read -p "$invite" reponse [ -z "$reponse" ] && reponse="$defaut"

Chapitre 3 Entre standard

case "$reponse" in [oO1] ) exec "$choix_oui" # Contrle d'erreurs. ;; [nN0] ) exec "$choix_non" # Contrle d'erreurs. ;; * ) printf "%b" "Rponse inattendue '$reponse'!" >&2 ;; esac } # Fin de la fonction choisir.

Si les actions sont complexes, utilisez la fonction suivante et traitez la rponse dans le code principal :
# bash Le livre de recettes : fonction_choisir.1 # Laisse l'utilisateur faire un choix et retourne une rponse standardise. # La prise en charge de la rponse par dfaut et des actions ultrieures # sont dfinir dans la section if/then qui se trouve aprs le choix # dans le code principal. # Utilisation : choisir <invite> # Exemple : choisir "Voulez-vous jouer ce jeu ?" # Retour : variable globale CHOIX function choisir { CHOIX='' local invite="$*" local reponse read -p "$invite" reponse case "$reponse" in [oO1] ) CHOIX='o';; [nN0] ) CHOIX='n';; * ) CHOIX="$reponse";; esac } # Fin de la fonction choisir.

Le code suivant invoque la fonction choisir pour inviter lutilisateur entrer la date dun paquet et la vrifier. Si lon suppose que la variable $CEPAQUET contient une valeur, la fonction affiche la date et demande son approbation. Si lutilisateur tape o, O ou Entre, la date est accepte. Sil saisit une nouvelle date, la fonction boucle et repose la question (la recette 11.7, page 233, propose une autre manire de traiter ce problme) :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

3.6. Attendre une rponse Oui ou Non


# bash Le livre de recettes : fonction_choisir.2 until [ "$CHOIX" = "o" ]; do printf "%b" "La date de ce paquet est $CEPAQUET\n" >&2 choisir "Est-ce correct ? [O/,<Nouvelle date>] : " if [ -z "$CHOIX" ]; then CHOIX='o' elif [ "$CHOIX" != "o" ]; then printf "%b" "$CEPAQUET est remplac par ${CHOIX}\n" CEPAQUET=$CHOIX fi done # Compiler le paquet ici.

67

Nous allons maintenant examiner deux manires diffrentes de traiter certaines questions de type oui ou non . Faites bien attention aux invites et aux valeurs par dfaut. Dans les deux cas, lutilisateur peut simplement appuyer sur la touche Entre et le script prend alors la valeur par dfaut fixe par le programmeur.
# Si lutilisateur saisit autre chose que le caractre 'n', quelle que # soit sa casse, le journal des erreurs est affich. choisir "Voulez-vous examiner le journal des erreurs ? [O/n] : " if [ "$CHOIX" != "n" ]; then less error.log fi # Si lutilisateur saisit autre chose que le caractre 'y', quelle que # soit sa casse, le journal des erreurs nest pas affich. choisir "Voulez-vous examiner le journal des erreurs ? [o/N] : " if [ "$CHOIX" = "o" ]; then less message.log fi

Enfin, la fonction suivante accepte une entre qui peut ne pas exister :
# bash Le livre de recettes : fonction_choisir.3 choisir "Entrez votre couleur prfre, si vous en avez une : " if [ -n "$CHOIX" ]; then printf "%b" "Vous avez choisi : $CHOIX" else printf "%b" "Vous n'avez pas de couleur prfre." fi

Discussion
Dans les scripts, vous aurez souvent besoin de demander lutilisateur de faire un choix. Pour une rponse arbitraire, consultez la recette 3.5, page 64. Si le choix doit se faire dans une liste doptions, consultez la recette 3.7, page 68.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

68

Chapitre 3 Entre standard

Si les choix possibles et leur code de traitement sont assez simples, la premire fonction sera plus facile utiliser, mais elle nest pas trs souple. La deuxime fonction est plus adaptable mais exige un travail plus important dans le code principal. Vous remarquerez que les invites sont affiches sur STDERR. Ainsi, la sortie effectue sur STDOUT par le script principal peut tre redirige sans que les invites sy immiscent.

Voir aussi
la recette 3.5, Lire lentre de lutilisateur, page 64 ; la recette 3.7, Choisir dans une liste doptions, page 68 ; la recette 11.7, Calculer avec des dates et des heures, page 233.

3.7. Choisir dans une liste doptions


Problme
Vous devez fournir lutilisateur une liste doptions parmi laquelle il doit faire son choix, mais vous souhaitez un minimum de saisie.

Solution
Utilisez la construction select de bash pour gnrer un menu, puis laissez lutilisateur faire son choix en entrant le numro correspondant :
# bash Le livre de recettes : selection_rep listerep="Quitter $(ls /)" PS3='Rpertoire analyser ? ' # Invite de slection. until [ "$repertoire" == "Quitter" ]; do printf "%b" "\a\n\nSlectionnez le rpertoire analyser :\n" >&2 select repertoire in $listerep; do # L'utilisateur tape un nombre, qui est stock dans $REPLY, mais # select retourne la valeur de l'entre. if [ "$repertoire" = "Quitter" ]; then echo "Analyse des rpertoires termine." break elif [ -n "$repertoire" ]; then echo "Vous avez choisi le numro $REPLY,"\ "analyse de $repertoire..." # Faire quelque chose. break else echo "Slection invalide !" fi # Fin du traitement du choix de l'utilisateur. done # Fin de la slection d'un rpertoire. done # Fin de la boucle while non termine.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

3.8. Demander un mot de passe

69

Discussion
Grce la fonction select, il est extrmement facile de prsenter une liste numrote lutilisateur sur STDERR et partir de laquelle il pourra faire son choix. Noubliez pas loption Quitter . Le numro entr par lutilisateur se trouve dans la variable $REPLY et la valeur de lentre est retourne dans la variable indique dans la construction select.

Voir aussi
help select ; help read ; la recette 3.6, Attendre une rponse Oui ou Non, page 65.

3.8. Demander un mot de passe


Problme
Vous devez demander un mot de passe un utilisateur, mais sans quil soit affich lcran.

Solution
read -s -p "mot de passe : " MOTDEPASSE printf "%b" "\n"

Discussion
Loption -s demande la commande read de ne pas afficher les caractres saisis (s comme silence) et loption -p signale que largument suivant reprsente linvite afficher avant de lire lentre. Les donnes saisies par lutilisateur sont places dans la variable denvironnement $MOTDEPASSE. Aprs read, nous ajoutons une instruction printf afin dafficher un saut de ligne. printf est ncessaire car read -s inactive la rptition des caractres saisis et aucun saut de ligne nest donc affich lorsque lutilisateur appuie sur la touche Entre ; toute sortie suivante apparatra sur la mme ligne que linvite. Vous pouvez crire ce code sur une seule ligne pour que le lien entre les deux instructions soit bien clair. Cela vite galement les erreurs si vous devez copier et coller cette ligne ailleurs :
read -s -p "mot de passe : " MOTDEPASSE ; printf "%b" "\n"

Si vous placez le mot de passe dans une variable denvironnement, vous ne devez pas oublier quil se trouve en clair en mmoire et quil peut donc tre obtenu par un vidage de la mmoire (core dump) ou en consultant /proc/core. Il se trouve galement dans lenvironnement du processus, auquel dautres processus peuvent accder. Il est prfrable

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

70

Chapitre 3 Entre standard

demployer des certificats avec SSH. Dans tous les cas, il est plus prudent de supposer que root et dautres utilisateurs de la machine peuvent accder au mot de passe et donc de traiter le cas de manire approprie.
Certains scripts anciens peuvent utiliser s pour dsactiver laffichage lcran pendant la saisie dun mot de passe. Cette solution prsente un inconvnient majeur : si lutilisateur interrompt le script, laffichage des caractres reste dsactiv. Les utilisateurs expriments sauront excuter stty sane pour corriger le problme, mais cest assez perturbant. Si vous devez employer cette mthode, mettez en place une gestion des signaux afin de ractiver laffichage lorsque le script se termine (voir la recette 10.6, page 215).

Voir aussi
help read ; la recette 10.6, Intercepter les signaux, page 215 ; la recette 14.14, Afficher les mots de passe dans la liste des processus, page 311 ; la recette 14.20, Utiliser des mots de passe dans un script, page 319 ; la recette 14.21, Utiliser SSH sans mot de passe, page 321 ; la recette 19.9, Rinitialiser le terminal, page 496.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

4
Excuter des commandes
Le premier objectif de bash (comme de nimporte quel interprteur de commandes) est de vous laisser interagir avec le systme dexploitation de lordinateur afin deffectuer votre travail. En gnral, cela implique le lancement de programmes. Le shell rcupre donc les commandes que vous avez saisies, les analyse pour dterminer les programmes excuter et lance ceux-ci. Examinons le mcanisme de base du lancement des programmes et explorons les possibilits offertes par bash, comme lexcution des programmes au premier ou larrireplan, squentiellement ou en parallle, avec une indication de la russite ou de lchec des programmes, et dautres.

4.1. Lancer nimporte quel excutable


Problme
Vous voulez excuter une commande sur un systme Linux ou Unix.

Solution
Utilisez bash et saisissez le nom de la commande linvite.
$ unProgramme

Discussion
Cela semble plutt simple et, en un certain sens, cest effectivement le cas, mais, en coulisses, il se passe de nombreuses choses que vous ne verrez jamais. Limportant est de bien comprendre que le travail fondamental de bash est de charger et dexcuter des programmes. Tout le reste nest quun habillage autour de cette fonction. Il existe bien sr les variables du shell, les instructions for pour les boucles et if/then/else pour les branchements, ainsi que diffrents mcanismes de gestion des entres et des sorties, mais tout cela nest en ralit quun dcor.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

72

Chapitre 4 Excuter des commandes

O bash trouve-t-il le programme excuter ? Pour trouver lexcutable que vous avez indiqu, il utilise une variable du shell appele $PATH. Cette variable contient une liste de rpertoires spars par des deux-points (:). bash recherche dans chacun de ces rpertoires un fichier correspondant au nom prcis. Lordre des rpertoires est important. bash les examine dans leur ordre dapparition dans la variable et prend le premier excutable trouv.
$ echo $PATH /bin:/usr/bin:/usr/local/bin:. $

La variable $PATH prcdente contient quatre rpertoires. Le dernier de la liste est juste un point (appel rpertoire point ou simplement point), qui reprsente le rpertoire de travail. Le point est le nom dun rpertoire qui se trouve dans chaque rpertoire dun systme de fichiers Linux ou Unix ; quel que soit lendroit o vous vous trouvez, le point fait rfrence ce rpertoire. Par exemple, si vous copiez un fichier depuis un rpertoire vers le rpertoire point (cest--dire cp /autre/endroit/fichier .), vous copiez en ralit le fichier dans le rpertoire en cours. En plaant le rpertoire point dans la variable $PATH, bash recherche non seulement les commandes dans les autres rpertoires, mais galement dans le rpertoire de travail (.). Certains considrent que lajout du rpertoire point dans la variable $PATH constitue un un risque de scurit important. En effet, une personne pourrait vous duper et vous faire excuter sa propre version (nuisible) dune commande la place de celle suppose. Si ce rpertoire arrive en tte de la liste, la version de ls propre cette personne supplante la commande ls et cest elle que vous excuterez, sans le savoir. Pour vous en rendre compte, testez le code suivant :
$ $ $ $ $ $ $ bash cd touch ls chmod 755 ls PATH=".:$PATH" ls

La commande ls semble ne plus fonctionner dans votre rpertoire personnel. Elle ne produit aucune sortie. Lorsque vous passez dans un autre rpertoire, par exemple cd /tmp, ls fonctionne nouveau. Pourquoi ? Dans votre rpertoire personnel, cest le fichier vide appel ls qui est excut (il ne fait rien) la place de la commande ls normale qui se trouve dans /bin. Puisque, dans notre exemple, nous commenons par lancer une nouvelle copie de bash, vous pouvez quitter cet environnement sabot en sortant du sous-shell. Mais avant cela, noubliez pas de supprimer la commande ls invalide :
$ cd $ rm ls $ exit $

Vous pouvez facilement imaginer le potentiel malfaisant dune recherche dans le rpertoire point avant tous les autres. Lorsque le rpertoire point se trouve la fin de la variable $PATH, vous ne pourrez pas tre tromp aussi facilement. Si vous len retirez totalement, vous tes plus en scurit

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

4.2. Connatre le rsultat de lexcution dune commande

73

et vous pouvez toujours excuter les commandes du rpertoire courant en les prfixant par ./ :
$ ./monScript

Cest vous de dcider.


Vous ne devez jamais placer le rpertoire point ou des rpertoires modifiables dans la variable $PATH de root. Pour plus dinformations, consultez les recettes 14.9, page 300, et 14.10, page 303.

Noubliez pas de fixer les autorisations dexcution des fichiers avant dinvoquer le script :
$ chmod a+x ./monScript $ ./monScript

Cette opration ne doit tre effectue quune seule fois. Ensuite, vous pouvez invoquer le script comme nimporte quelle commande. Les experts bash crent souvent un rpertoire bin personnel, analogue aux rpertoires systme /bin et /usr/bin qui contiennent les programmes excutables. Dans votre bin personnel, vous pouvez placer des copies de vos scripts shell prfrs, ainsi que dautres commandes personnalises ou prives. Ensuite, ajoutez votre rpertoire personnel $PATH, mme en dbut de liste (PATH=~/bin:$PATH). De cette manire, vous avez accs vos commandes prfres sans risque dexcuter celles dautres personnes potentiellement malveillantes.

Voir aussi
le chapitre 16, Configurer bash, page 367, pour plus dinformations sur la personnalisation de votre environnement ; la recette 1.3, Chercher et excuter des commandes, page 6 ; la recette 14.9, Trouver les rpertoires modifiables mentionns dans $PATH, page 300 ; la recette 14.10, Ajouter le rpertoire de travail dans $PATH, page 303 ; la recette 16.9, Crer son rpertoire priv dutilitaires, page 389 ; la recette 19.1, Oublier les autorisations dexcution, page 485.

4.2. Connatre le rsultat de lexcution dune commande


Problme
Vous voulez savoir si la commande que vous avez excute a russi ou chou.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

74

Chapitre 4 Excuter des commandes

Solution
La variable $? du shell contient une valeur diffrente de zro si la commande a chou, mais condition que le programmeur qui a crit la commande ou le script shell ait respect les conventions tablies :
$ uneCommande jai russi... $ echo $? 0 $ commandeInvalide jai chou... $ echo $? 1 $

Discussion
Ltat de sortie dune commande est plac dans la variable $? du shell. Sa valeur se trouve dans lintervalle 0 255. Lorsque vous crivez un script shell, nous vous conseillons de le faire se terminer avec une valeur diffrente de zro en cas derreur (elle doit tre infrieure 255). Pour retourner un code dtat de sortie, utilisez linstruction exit (par exemple, exit 1 ou exit 0). Cependant, vous devez savoir que ltat de la sortie ne peut tre lu une seule fois :
$ commandeInvalide jai chou... $ echo $? 1 $ echo $? 0 $

Pourquoi obtenons-nous 0 la deuxime fois ? Tout simplement parce que la variable indique alors ltat de sortie de la commande echo qui prcde. La premire commande echo $? a retourn 1, cest--dire ltat de sortie de commandeInvalide. Cette commande echo sest parfaitement droule et ltat de sortie le plus rcent correspond donc un succs (la valeur 0). Puisque vous navez quune seule occasion de le consulter, de nombreux scripts shell affectent immdiatement ltat de sortie une autre variable :
$ commandeInvalide jai chou... $ ETAT=$? $ echo $ETAT 1 $ echo $ETAT 1 $

Vous pouvez conserver la valeur dans la variable $ETAT aussi longtemps que ncessaire. Nous illustrons ce fonctionnement dans des exemples en ligne de commande, mais les variables comme $? sont, en ralit, plus utiles dans les scripts. Vous pouvez toujours
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

4.3. Excuter plusieurs commandes la suite

75

savoir si une commande sest bien droule en regardant votre cran. En revanche, dans un script, les commandes peuvent avoir des comportements inattendus. bash a pour avantage dutiliser un langage de scripts identique aux commandes saisies au niveau de linvite dans une fentre de terminal. Il est ainsi beaucoup plus facile de vrifier une syntaxe et une logique pendant lcriture de scripts. Ltat de sortie est trs souvent employ dans les scripts, principalement dans des instructions if, pour effectuer des actions diffrentes selon le succs ou lchec dune commande. Voici un exemple simple sur lequel nous reviendrons dans dautres recettes :
$ uneCommande ... $ if (( $? )) ; then echo chec ; else echo OK; fi

Voir aussi
la recette 4.5, Dterminer le succs dune commande, page 77 ; la recette 4.8, Afficher des messages en cas derreur, page 80 ; la recette 6.2, Conditionner lexcution du code, page 116.

4.3. Excuter plusieurs commandes la suite


Problme
Vous souhaitez excuter plusieurs commandes, mais certaines prennent beaucoup de temps et vous ne voulez pas attendre quelles se terminent avant de lancer les suivantes.

Solution
Ce problme a trois solutions, mme si la premire est plutt triviale : il suffit de les saisir. Un systme Linux ou Unix est suffisamment labor pour vous permettre de saisir des commandes pendant quil excute les prcdentes. Vous pouvez donc simplement saisir toutes les commandes lune aprs lautre. Une autre solution galement simple consiste placer ces commandes dans un fichier et de demander ensuite bash de les excuter. Autrement dit, vous crivez un script shell simple. Supposons que vous vouliez excuter trois commandes : long, moyen et court, dont les noms refltent leur temps dexcution. Vous devez les excuter dans cet ordre, mais vous ne voulez pas attendre que long soit termine avant dinvoquer les autres commandes. Vous pouvez utiliser un script shell (ou fichier batch), de la manire la plus simple qui soit :
$ cat > simple.script long moyen court ^D # Ctrl-D, non visible. $ bash ./simple.script

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

76

Chapitre 4 Excuter des commandes

La troisime solution, probablement la meilleure, consiste excuter chaque commande la suite. Si vous voulez lancer tous les programmes, mme si le prcdent a chou, sparez-les par des points-virgules sur la mme ligne de commande :
$ long ; moyen ; court

Pour excuter un programme uniquement si le prcdent sest bien pass (et sils grent tous correctement leur code de sortie), sparez-les par deux esperluettes :
$ long && moyen && court

Discussion
Lexemple de cat nest quune solution trs primitive dentre du texte dans la fichier. Nous redirigeons la sortie de la commande vers le fichier nomm simple.script (la redirection de la sortie est explique au chapitre 2). La meilleure solution consiste employer un vritable diteur de texte, mais il est plus difficile de la reprsenter sur de tels exemples. partir de maintenant, lorsque nous voudrons montrer un script, nous donnerons uniquement le texte en dehors de la ligne de commande ou dbuterons lexemple par une commande de la forme cat nomFichier pour envoyer le contenu du fichier lcran (au lieu de rediriger la sortie de notre saisie vers le fichier) et donc lafficher dans lexemple. Cette solution simple a pour objectif de montrer quil est possible de placer plusieurs commandes sur la ligne de commande de bash. Dans le premier cas, la deuxime commande nest excute quune fois la premire termine, la deuxime nest excute quune fois la troisime termine, etc. Dans le second cas, la deuxime commande nest lance que si la premire sest termine avec succs, la troisime que si la deuxime sest termine avec succs, etc.

4.4. Excuter plusieurs commandes la fois


Problme
Vous souhaitez excuter trois commandes indpendantes les unes des autres et ne voulez pas attendre que lune se termine avant de passer la suivante.

Solution
Excutez les commandes en arrire-plan en ajoutant une esperluette (&) la fin de la ligne. Vous pouvez ainsi dmarrer les trois tches la fois :
$ long & [1] 4592 $ moyen & [2] 4593 $ court $

Mieux encore, placez-les toutes sur la mme ligne de commande :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

4.5. Dterminer le succs dune commande


$ long & moyen & court [1] 4592 [2] 4593 $

77

Discussion
Lorsquune commande sexcute en arrire-plan, cela signifie en ralit que le clavier est dtach de lentre de la commande et que le shell nattend pas quelle se termine pour rendre la main et accepter dautres commandes. La sortie de la commande est toujours envoye lcran (except si vous avez indiqu un comportement diffrent). Par consquent, les trois tches verront leur sortie mlange lcran. Les valeurs numriques affiches correspondent au numro de la tche (entre les crochets) et lidentifiant du processus de la commande dmarre en arrire-plan. Dans cet exemple, la tche 1 (processus 4592) correspond la commande long et la tche 2 (processus 4593) moyen. La commande court ne sexcute pas en arrire-plan car nous navons pas ajout une esperluette la fin de la ligne. bash attend quelle se termine avant de revenir linvite (le caractre $). Le numro de tche et lidentifiant de processus peuvent tre utiliss pour agir, de manire limite, sur la tche. La commande kill %1 arrte lexcution de long (puisque son numro de job est 1). Vous pouvez galement indiquer son numro de processus (kill 4592) pour obtenir le mme rsultat final. Le numro de tche peut galement servir replacer la commande correspondante au premier plan. Pour cela, utilisez la commande fg %1. Si une seule tche sexcute en arrire-plan, son numro est mme inutile, il suffit dinvoquer fg. Si vous lancez un programme et ralisez ensuite quil prend plus de temps que vous le pensiez, vous pouvez le suspendre avec Ctrl-Z ; vous revenez alors linvite. La commande bg poursuit lexcution du programme en arrire-plan. Cela quivaut ajouter & lors du lancement de la commande.

Voir aussi
le chapitre 2, Sortie standard, page 31, pour la redirection de la sortie.

4.5. Dterminer le succs dune commande


Problme
Vous souhaitez excutez certaines commandes, mais uniquement si dautres ont russi. Par exemple, vous voulez changer de rpertoire (avec cd) et supprimer tous les fichiers quil contient. Cependant, les fichiers ne doivent pas tre supprims si la commande cd a chou (par exemple, si les permissions ne vous autorisent pas aller dans le rpertoire ou si son nom a t mal crit).

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

78

Chapitre 4 Excuter des commandes

Solution
Vous pouvez combiner ltat de sortie ($?) de la commande cd et une instruction if pour effectuer la suppression (commande rm) uniquement si cd sest bien passe.
cd monTmp if (( $? )); then rm * ; fi

Discussion
Bien videmment, tout cela ne serait pas ncessaire si les commandes taient effectues la main. Vous verriez alors les messages derreur produits par la commande cd et dcideriez de ne pas excuter la commande rm. Dans un script, les choses sont diffrentes. Le test est indispensable pour vrifier que vous nallez pas effacer par mgarde tous les fichiers du rpertoire de travail. Supposons que vous invoquiez ce script partir dun mauvais rpertoire, cest--dire un rpertoire qui nait pas de sous-rpertoire monTmp. La commande cd choue donc et le rpertoire courant reste le mme. Sans linstruction if, qui vrifie si cd a russi, le script se poursuit avec la ligne suivante. Lexcution de rm * supprime alors tous les fichiers du rpertoire courant. Mieux vaut ne pas oublier le test avec if ! Do la variable $? obtient-elle sa valeur ? Il sagit du code de sortie de la commande. Les programmeurs C y verront la valeur donne largument de la fonction exit() ; par exemple, exit(4); affectera la valeur 4 cette variable. Vis--vis du shell, 0 reprsente un succs et une autre valeur indique un chec. Si vous crivez des scripts bash, vous devez absolument vrifier quils fixent leur valeur de retour. Ainsi, $? sera correctement affecte par vos scripts. Dans le cas contraire, la valeur donne cette variable sera celle de la dernire commande excute, ce qui ne donnera peut-tre pas le rsultat escompt.

Voir aussi
la recette 4.2, Connatre le rsultat de lexcution dune commande, page 73 ; la recette 4.6, Utiliser moins dinstructions if, page 78.

4.6. Utiliser moins dinstructions if


Problme
En tant que programmeur consciencieux, vous avez cur de suivre les recommandations donnes la recette 4.5, page 77. Vous appliquez le concept prsent votre dernier script shell, mais il devient illisible cause de toutes ces instructions if qui vrifient le code de retour de chaque commande. Existe-t-il une autre solution ?

Solution
Utilisez loprateur double esperluette de bash pour dfinir une excution conditionnelle :
$ cd monTmp && rm *

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

4.7. Lancer de longues tches sans surveillance

79

Discussion
En sparant deux commandes par une double esperluette, vous demandez bash dexcuter la premire commande, puis la seconde uniquement si la premire a russi (si son tat de sortie vaut 0). Cela quivaut une instruction if qui vrifie le code de sortie de la premire commande afin de conditionner lexcution de la seconde :
cd monTmp if (( $? )); then rm * ; fi

La syntaxe de la double esperluette provient de loprateur logique ET du langage C. Si vous navez pas oubli votre cours de logique, vous devez savoir que lvaluation de lexpression logique A ET B ne sera vraie que si les deux (sous-)expressions A et B svaluent vrai. Si lune delles est fausse, lexpression globale est fausse. Le langage C exploite ce fonctionnement et, avec une expression de la forme if (A && B) { ... }, commence par valuer A. Si cette expression est fausse, il ne tente mme pas dvaluer B, puisque le rsultat de lvaluation globale est dj connu (faux, puisque A a t value faux). Quel est donc le lien avec bash ? Si ltat de sortie de la premire commande (celle gauche de &&) est diffrent de zro (elle a chou), alors la deuxime expression nest pas value. Autrement dit, lautre commande nest pas excute. Si vous voulez grer les erreurs, mais sans multiplier les instructions if, indiquez bash de quitter le script ds quil rencontre une erreur (un tat de sortie diffrent de zro) dans une commande ( lexception des boucles while et des instructions if dans lesquelles il utilise dj ltat de sortie). Pour cela, activez loption -e.
set -e cd monTmp rm *

Lorsque loption -e est active, le shell interrompt lexcution du script ds quune commande choue. Par exemple, si la commande cd choue, le script se termine et ne passe jamais par la commande rm *. Nous dconseillons cette solution avec un shell interactif, car, si le shell se termine, sa fentre disparat galement.

Voir aussi
la recette 4.8, Afficher des messages en cas derreur, page 80, pour une explication de la syntaxe ||, qui est similaire, en un sens, mais galement assez diffrente de &&.

4.7. Lancer de longues tches sans surveillance


Problme
Vous dmarrez une tche en arrire-plan, puis quittez le shell et partez prendre un caf. Lorsque vous revenez, la tche ne sexcute plus, mais elle nest pas termine. En ralit, la tche na gure progress et semble stre arrte ds que vous avez quitt le shell.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

80

Chapitre 4 Excuter des commandes

Solution
Si vous lancez une tche en arrire-plan et voulez quitter le shell avant la fin de la tche, vous devez alors dmarrer la tche avec nohup :
$ nohup long & nohup: ajout la sortie de `nohup.out' $

Discussion
Lorsque vous placez la tche en arrire-plan (avec &), elle reste un processus enfant du shell bash. Si vous quittez une instance du shell, bash envoie un signal darrt (hang-up) tous ses processus enfants. Cest pour cela que lexcution de votre tche na pas dur trs longtemps. Ds que vous avez quitt bash, il a tu votre tche darrire-plan. La commande nohup configure simplement les processus enfants de manire ce quils ignorent les signaux darrt. Vous pouvez toujours stopper une tche avec la commande kill, car elle envoie un signal SIGTERM et non un signal SIGHUP. Mais avec nohup, bash ne tuera pas incidemment votre tche. nohup affiche un message prcisant quelle ajoute votre sortie un fichier. En ralit, elle essaie juste dtre utile. Puisque vous allez certainement quitter le shell aprs avoir lanc une commande nohup, la sortie de la commande sera perdue (la session bash dans la fentre de terminal ne sera plus active). Par consquent, o la tche peut-elle envoyer sa sortie ? Plus important encore, si elle est envoye vers une destination inexistante, elle peut provoquer une erreur. nohup redirige donc la sortie votre place et lajoute au fichier nohup.out dans le rpertoire de travail. Si vous redirigez explicitement la sortie sur la ligne de commande, nohup est suffisamment intelligente pour le dtecter et ne pas utiliser nohup.out.

Voir aussi
le chapitre 2, Sortie standard, page 31, pour la redirection de la sortie, car cela vous sera certainement utile pour une tche en arrire-plan ; la recette 10.1, Convertir un script en dmon, page 207 ; la recette 17.4, Restaurer des sessions dconnectes avec screen, page 433.

4.8. Afficher des messages en cas derreur


Problme
Vous souhaitez que votre script shell soit plus bavard lorsquil rencontre des erreurs. Vous voulez obtenir des messages derreur lorsque des commandes ne fonctionnent pas, mais les instructions if ont tendance masquer le droulement des commandes.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

4.9. Excuter des commandes places dans une variable

81

Solution
Parmi les programmeurs shell, lidiome classique consiste utiliser || avec les commandes afin de gnrer des messages de dbogage ou derreur. En voici un exemple :
cmd || printf "%b" "chec de cmd. vous de jouer...\n"

Discussion
Similaire && dans les conditions dvaluation de la seconde expression, || indique au shell de ne pas se proccuper de lvaluation de la seconde expression si la premire est vraie (en cas de succs). Comme pour &&, la syntaxe de || est issue de la logique et du langage C, dans lequel le rsultat global est connu (vrai) si la premire expression de A OU B svalue vrai ; il est donc inutile dvaluer la seconde expression. Dans bash, si la premire expression retourne 0 (russit), lexcution se poursuit. Ce nest que si la premire expression retourne une valeur diffrente de zro que la deuxime partie est value et donc excute. Attention, ne tombez pas dans le pige suivant :
cmd || printf "%b" "CHEC.\n" ; exit 1

Linstruction exit est excute dans tous les cas ! Le OU concerne uniquement les deux commandes qui lentourent. Si vous voulez quexit ne soit excute que dans une situation derreur, vous devez la regrouper avec printf. Par exemple :
cmd || { printf "%b" "CHEC.\n" ; exit 1 ; }

Les singularits de la syntaxe de bash imposent la prsence du point-virgule aprs la dernire commande, juste avant }. Par ailleurs, cette accolade fermante doit tre spare du texte environnant par une espace.

Voir aussi
la recette 2.14, Enregistrer ou runir la sortie de plusieurs commandes, page 44 ; la recette 4.6, Utiliser moins dinstructions if, page 78, pour une explicaiton de la syntaxe de &&.

4.9. Excuter des commandes places dans une variable


Problme
Vous souhaitez que votre script excute des commandes diffrentes selon certaines conditions. Comment pouvez-vous modifier la liste des commandes excutes ?

Solution
Il existe de nombreuses solutions ce problme ; cest tout lobjectif des scripts. Dans les chapitres venir, nous tudierons diffrentes logiques de programmation permettant de rsoudre ce problme, comme les instructions if/then/else ou case. Voici une ap[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

82

Chapitre 4 Excuter des commandes

proche assez diffrente qui souligne les possibilits de bash. Nous pouvons nous servir du contenu dune variable (voir le chapitre 5), non seulement pour les paramtres, mais galement pour la commande elle-mme :
FN=/tmp/x.x PROG=echo $PROG $FN PROG=cat $PROG $FN

Discussion
Le nom du programme est plac dans une variable (ici $PROG), laquelle nous faisons ensuite rfrence l o le nom dune commande est attendu. Le contenu de la variable ($PROG) est alors interprt comme la commande excuter. Le shell bash analyse la ligne de commande, remplace les variables par leur valeur, puis prend le rsultat de toutes les substitutions et le traite comme sil avait t saisi directement sur la ligne de commande.
Attention aux noms des variables que vous utilisez. Certains programmes, comme InfoZip, utilisent leurs propres variables denvironnement, comme $ZIP et $UNZIP, pour le passage de paramtres. Si vous utilisez une instruction comme ZIP='/usr/bin/zip', vous risquez de passer plusieurs jours essayer de comprendre pourquoi cela fonctionne depuis la ligne de commande, mais pas dans votre script. Vous pouvez nous croire sur parole, nous parlons par exprience.

Voir aussi
le chapitre 11, Dates et heures, page 223 ; la recette 14.3, Dfinir une variable $PATH sre, page 294 ; la recette 16.19, Crer des fichiers dinitialisation autonomes et portables, page 414 ; la recette 16.20, Commencer une configuration personnalise, page 416 ; lannexe C, Analyse de la ligne de commande, page 569, pour une description des diffrentes substitutions effectues sur la ligne de commande ; vous lisez les autres chapitres avant dexaminer ce sujet.

4.10. Excuter tous les scripts dun rpertoire


Problme
Vous souhaitez excuter un ensemble de scripts, mais la liste volue en permanence. Vous ajoutez continuellement de nouveaux scripts, mais vous ne voulez pas grer une liste.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

4.10. Excuter tous les scripts dun rpertoire

83

Solution
Placez les scripts excuter dans un rpertoire, puis demandez bash dinvoquer tout ce quil y trouve. Au lieu de grer une liste, vous pouvez simplement adapter le contenu de ce rpertoire. Voici un script qui excute tous les programmes qui se trouvent dans un rpertoire :
for SCRIPT in /chemins/vers/les/scripts/* do if [ -f $SCRIPT -a -x $SCRIPT ] then $SCRIPT fi done

Discussion
Nous reviendrons en dtail sur la boucle for et linstruction if au chapitre 6. La variable $SCRIPT va successivement contenir le nom de chaque fichier qui correspond au motif *, cest--dire tout ce qui se trouve dans le rpertoire de travail (except les fichiers invisibles qui commencent par un point). Si le contenu de la variable correspond un fichier (le test -f), dont les autorisations dexcution sont actives (le test -x), le shell tente de lexcuter. Dans cet exemple simple, nous noffrons pas la possibilit de passer des arguments aux scripts excuts. Il sera peut-tre adapt vos besoins personnels, mais il ne peut tre qualifi de robuste. Certains pourraient mme le considrer comme dangereux. Cependant, nous esprons que vous avez compris lide sous-jacente : les scripts peuvent bnficier de certaines structures dcriture, comme les langages de programmation.

Voir aussi
le chapitre 6, Logique et arithmtique, page 113, pour plus dinformations sur les boucles for et les instructions if.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5
Variables du shell
La programmation avec le shell bash ressemble aux autres formes de programmation, notamment par lexistence de variables. Il sagit demplacements permettant de stocker des chanes et des nombres, qui peuvent tre modifis, compars et passs entre les diffrentes parties du programme. bash dispose doprateurs particuliers qui savrent trs utiles lors des rfrences aux variables. bash fournit galement des variables internes, qui apportent des informations indispensables sur les autres variables dun script. Ce chapitre prsente les variables de bash et certains mcanismes spcifiques employs dans les rfrences aux variables. Il explique galement comment vous en servir dans vos scripts. Dans un script bash, les variables sont gnralement crites en majuscules, bien que rien ne vous y oblige ; il sagit dune pratique courante. Vous navez pas besoin de les dclarer, juste de les utiliser lorsque vous le souhaitez. Elles sont toutes de type chane de caractres, mme si certaines oprations de bash peuvent traiter leur contenu comme des nombres. Voici un exemple dutilisation :
# Script trs simple qui utilise des variables du shell # (nanmoins, il est bien comment). MAVAR="valeur" echo $MAVAR # Une deuxime variable, mais sans les guillemets. MA_2E=uneAutre echo $MA_2E # Les guillemets sont indispensables dans le cas suivant : MONAUTRE="autre contenu pour echo" echo $MONAUTRE

La syntaxe des variables de bash prsente deux aspects importants qui ne sont peut-tre pas intuitivement vidents pour des variables de shell. Tout dabord, la syntaxe daffectation nom=valeur est suffisamment simple, mais il ne peut y avoir despace autour du signe gal. Pourquoi est-ce ainsi ? Le rle fondamental du shell est de lancer des programmes. Vous nommez le programme sur la ligne de commande et le shell lexcute. Tous les mots qui
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

86

Chapitre 5 Variables du shell

se trouvent aprs ce nom sur la ligne de commande constituent les arguments du programme. Prenons, par exemple, la commande suivante :
$ ls nomFichier

ls est le nom de la commande et nomFichier est son premier et seul argument. Quel est le rapport ? Examinons laffectation dune variable dans bash en autorisant les espaces autour du signe gal :
MAVAR = valeur

Vous pouvez facilement imaginer que le shell aura quelques difficults diffrencier le nom dune commande invoquer (comme lexemple de ls) et laffectation dune variable. Cest notamment le cas pour les commandes qui peuvent utiliser des symboles = dans leurs arguments (par exemple test). Par consquent, pour faire dans la simplicit, le shell refuse les espaces autour du signe gal dune affectation. Dans le cas contraire, il interprte chaque lment comme des mots spars. Ce choix a un effet secondaire. Vous ne pouvez pas placer de signe gal dans le nom dun fichier, en particulier celui dun script shell (en ralit cest possible, mais fortement dconseill). Le deuxime aspect important de la syntaxe des variables du shell rside dans lutilisation du symbole dollar ($) pour les rfrences aux variables. Il nest pas ajout au nom de la variable lors de laffectation, mais indispensable pour obtenir sa valeur. Les variables places dans une expression $(( ... )) font exception cette rgle. Du point de vue du compilateur, cette diffrence dans la syntaxe daffectation et dobtention de la valeur dune variable a un rapport avec la valeur-L et la valeur-R de la variable pour le ct gauche (L) et droit (D) de loprateur daffectation. Une fois encore, la raison de cette distinction rside dans la simplicit. Prenons le cas suivant :
MAVAR=valeur echo MAVAR vaut prsent MAVAR

Comme vous pouvez le constater, il nest pas vident de diffrencier la chane littrale "MAVAR" et la valeur de la variable $MAVAR. Vous pourriez penser utiliser des guillemets, mais si toutes les chanes littrales devaient tre places entre guillemets, la lisibilit et la simplicit dutilisation en souffriraient normment. En effet, tous les noms autres que ceux dune variable devraient tre placs entre guillemets, ce qui inclut les commandes ! Avez-vous vraiment envie de saisir la ligne suivante ?
$ "ls" "-l" "/usr/bin/xmms"

Cela dit, elle fonctionne parfaitement. Au lieu de tout placer entre des guillemets, il est plus simple de faire rfrence aux variables en utilisant la syntaxe de la valeur-R. Ajoutez un symbole dollar devant le nom dune variable pour obtenir sa valeur :
MAVAR=valeur echo MAVAR vaut prsent $MAVAR

Puisque bash ne manipule que des chanes de caractres, nous avons besoin du symbole dollar pour reprsenter une rfrence une variable.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.1. Documenter un script

87

5.1. Documenter un script


Problme
Avant daller plus loin sur les scripts shell ou les variables, vous devez savoir comment documenter vos scripts. En effet, il ny a rien de plus important que de pouvoir comprendre le contenu du script, surtout plusieurs mois aprs lavoir crit.

Solution
Documentez votre script en ajoutant des commentaires. Le caractre # reprsente le dbut dun commentaire. Tous les caractres qui viennent ensuite sur la ligne sont ignors par le shell.
# # Voici un commentaire. # # Utilisez les commentaires le plus souvent possible. # Les commentaires sont vos amis.

Discussion
Certaines personnes considrent que le shell, les expressions rgulires et dautres lments des scripts shell ont une syntaxe en criture uniquement. Elles veulent simplement dire quil est pratiquement impossible de comprendre les complexits de nombreux scripts shell. La meilleure dfense contre ce pige consiste employer les commentaires (vous pouvez aussi utiliser des noms de variables significatifs). Avant toute syntaxe trange ou expression complique, il est fortement conseill dajouter un commentaire :
# Remplacer le point-virgule par une espace. NOUVEAU_PATH=${PATH/;/ } # # changer le texte qui se trouve des deux cts dun point-virgule. sed -e 's/^\(.*\);\(.*\)$/\2;\1/' < $FICHIER

Dans un shell interactif, les commentaires peuvent mme tre saisis linvite de commande. Il est possible de dsactiver cette fonction, mais elle est active par dfaut. Dans certains cas, il peut tre utile dajouter des commentaires en mode interactif.

Voir aussi
la section Options de shopt, page 517, pour lactivation et la dsactivation des commentaires.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

88

Chapitre 5 Variables du shell

5.2. Incorporer la documentation dans les scripts


Problme
Vous souhaitez disposer dun moyen simple pour fournir une documentation mise en forme lutilisateur final de votre script (par exemple, des pages de manuel ou des pages HTML). Vous voulez conserver le code et la documentation dans le mme fichier afin de simplifier les mises jour et la distribution.

Solution
Incorporez la documentation dans le script en utilisant la commande ne fait rien (les deux-points) et un document en ligne :
#!/usr/bin/env bash # bash Le livre de recettes : documentation_incorporee echo 'Le code du script shell vient ici' # Utilisez une commande "ne fait rien" (:) et un document en ligne # pour incorporer la documentation. : <<'FIN_DE_DOC' Incorporez ici la documentation au format POD (Plan Old Documentation) de Perl ou en texte brut. Une documentation jour est prfrable aucune documentation. Exemple de documentation au format POD de Perl adapt des exemples CODE/ch07/Ch07.001_Best_Ex7.1 et 7.2 du livre De l'art de programmer en Perl (ditions O'Reilly). =head1 NAME MON~PROGRAMME--Une ligne de description =head1 SYNOPSIS MON~PROGRAMME [OPTIONS] <fichier> =head1 OPTIONS -h = Cette aide. -v = tre plus bavard. -V = Afficher la version, le copyright et la licence. =head1 DESCRIPTION

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.2. Incorporer la documentation dans les scripts


Une description complte de l'application et de ses fonctionnalits. Peut comporter des sous-sections (cd =head2, =head3, etc.)

89

[...]

=head1 LICENCE AND COPYRIGHT =cut FIN_DE_DOC

Ensuite, pour extraire et utiliser la documentation POD, servez-vous des commandes suivantes :
# Pour un affichage lcran, avec pagination automatique. $ perldoc monScript

# Uniquement les sections "utilisation". $ pod2usage monScript

# Crer une version HTML. $ pod2html monScript > monScript.html

# Crer une page de manuel. $ pod2man monScript > monScript.1

Discussion
Toute documentation en texte brut ou balis peut tre utilise de cette manire, quelle soit mlange au code ou, mieux encore, place la fin de script. Lorsque bash est install sur un systme, il est fort probable que Perl lest galement. Par consquent, le format POD (Plain Old Documentation) est un bon choix. Perl est gnralement fourni avec des programmes pod2* qui convertissent POD en fichiers HTML, LaTeX, de page de manuel, texte et utilisation. De lart de programmer en Perl de Damian Conway (ditions OReilly) propose un excellent module de bibliothque et des modles de documentation qui peuvent tre facilement convertis dans tout format de documentation, y compris le texte brut. Consultez les exemples CODE/ch07/Ch07.001_Best_Ex7.1 et 7.2 dans larchive disponible ladresse http://www.oreilly.fr/archives/3698Exemples.tar.gz. Si lintgralit de la documentation est incorpore tout la fin de script, vous pouvez galement ajouter une instruction exit 0 juste avant le dbut de la documentation. Cela permet ainsi de quitter le script sans obliger le shell analyser chacune des lignes la recherche de la fin du document en ligne. Si vous mlangez code et documentation au cur du script, vitez dutiliser cette instruction.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

90

Chapitre 5 Variables du shell

Voir aussi
http://www.oreilly.fr/archives/3698Exemples.tar.gz ; Embedding manpages in Shell Scripts with kshdoc http://www.unixlabplus. com/unix-prog/kshdoc/kshdoc.html

5.3. Amliorer la lisibilit des scripts


Problme
Vous souhaitez rendre votre script aussi lisible que possible, afin den faciliter la comprhension et la maintenance ultrieure.

Solution
documentez votre script comme lexpliquent les recettes 5.1, page 87, et 5.2, page 88 ; indentez et utilisez lespacement vertical avec soin ; donnez des noms significatifs aux variables ; utilisez des fonctions et donnez-leur des noms significatifs ; coupez les lignes des emplacements judicieux, moins de 76 caractres (environ) ; placez les lments les plus importants gauche.

Discussion
La documentation doit expliquer les objectifs et non les dtails vidents du code. Si vous respectez les autres points, votre code devrait tre clair. Ajoutez des rappels, fournissez des donnes dexemple ou des intituls et notez tous les dtails qui se trouvent dans votre tte au moment o vous crivez le code. Si certaines parties du code lui-mme sont subtiles ou obscures, documentez-les. Nous conseillons dutiliser quatre espaces par niveau dindentation, sans tabulation et surtout sans mlanger espaces et tabulations. Les raisons de cette recommandation sont nombreuses, mme sil sagit souvent de prfrences personnelles ou professionnelles. En effet, quatre espaces reprsentent toujours quatre espaces, quelle que soit la configuration de votre diteur (except avec des polices proportionnelles) ou de votre imprimante. Quatre espaces sont suffisamment visibles lorsque vous parcourez le script et restent suffisamment courtes pour autoriser plusieurs niveaux dindentation sans que les lignes soient colles sur la droite de lcran ou de la page imprime. Nous vous suggrons galement dindenter les instructions occupant plusieurs lignes avec deux espaces supplmentaires, ou plus, pour que le code soit plus clair. Utilisez les espaces verticaux, avec ou sans sparateur, pour crer des blocs de code connexe. Faites-le galement pour les fonctions. Donnez des noms significatifs aux variables et aux fonctions. Le seul endroit o des noms comme $i ou $x peuvent tre accepts est dans une boucle for. Vous pourriez
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.3. Amliorer la lisibilit des scripts

91

penser que des noms courts et cods permettent de gagner du temps sur le moment, mais nous pouvons vous assurer que la perte de temps sera dix cent fois suprieure lorsque vous devrez corriger ou modifier le script. Limitez les lignes environ 76 caractres. Nous savons bien que la plupart des crans peuvent faire mieux que cela. Mais, le papier et les terminaux offrant 80 caractres sont encore trs rpandus et vous pourriez avoir besoin dun peu despace droite du code. Il est assez pnible de devoir constamment utiliser la barre de dfilement vers la droite ou que les instructions passent automatiquement la ligne sur lcran ou le papier. Malheureusement, il existe des exceptions ces rgles. Lorsque vous crivez des lignes qui doivent tre passes dautres programmes, peut-tre via SSH (Secure Shell), ainsi que dans dautres situations particulires, leur coupure risque de poser plus de problmes que den rsoudre. Cependant, dans la plupart des cas, elle est prfrable. Lorsque vous coupez des lignes, essayez de placer les lments les plus importants gauche. En effet, nous lisons le code de gauche droite et la nuisance due la coupure de la ligne est alors moindre. Il est galement ainsi plus facile de parcourir le ct gauche du code sur plusieurs lignes. Parmi les exemples suivants, lequel trouvez-vous le plus facile lire ?
# Bon. [ $resultats ] \ && echo "Nous avons un bon rsultat dans $resultats" \ || echo 'Le rsultat est vide, il y a un problme'

# galement bon. [ $resultats ] && echo "Nous avons un bon rsultat dans $resultats" \ || echo 'Le rsultat est vide, il y a un problme'

# OK, mais pas idal. [ $resultats ] && echo "Nous avons un bon rsultat dans $resultats" \ || echo 'Le rsultat est vide, il y a un problme'

# Mauvais. [ $resultats ] && echo "Nous avons un bon rsultat dans $resultats" || echo 'Le rsultat est vide, il y a un problme'

# Mauvais. [ $resultats ] && \ echo "Nous avons un bon rsultat dans $resultats" || \ echo 'Le rsultat est vide, il y a un problme'

Voir aussi
la recette 5.1, Documenter un script, page 87 . la recette 5.2, Incorporer la documentation dans les scripts, page 88.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

92

Chapitre 5 Variables du shell

5.4. Sparer les noms de variables du texte environnant


Problme
Vous souhaitez afficher une variable au ct dun autre texte. Vous utilisez le symbole dollar dans la rfrence la variable. Cependant, comment pouvez-vous distinguer la fin du nom de la variable et le texte qui vient ensuite ? Par exemple, supposons que vous utilisiez une variable en tant que partie du nom dun fichier :
for NF in 1 2 3 4 5 do unScript /tmp/rap$NFport.txt done

Comment le shell va-t-il interprter cela ? Il suppose que le nom de la variable commence au symbole $ et se termine au symbole de ponctuation. Autrement dit, il considre que la variable se nomme $NFport la place de $NF.

Solution
Utilisez la syntaxe complte dune rfrence de variable, qui inclut non seulement le symbole dollar, mais galement des accolades autour du nom :
unScript /tmp/rap${NF}port.txt

Discussion
Puisque les noms des variables du shell sont uniquement constitus de caractres alphanumriques, les accolades ne sont pas toujours ncessaires. Une espace ou un signe de ponctuation (except le soulign) indique trs clairement la fin du nom dune variable. Mais, en cas de doute, utilisez les accolades.

Voir aussi
la recette 1.6, Protger la ligne de commande, page 12.

5.5. Exporter des variables


Problme
Vous avez dfini une variable dans un script, mais, lorsque vous appelez un autre script, elle nest pas connue.

Solution
Exportez les variables que vous souhaitez passer dautres scripts :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.5. Exporter des variables


export MAVAR export NOM=valeur

93

Discussion
Il est parfois prfrable quun script ne connaisse pas les variables dun autre. Si vous appelez un script shell depuis lintrieur dune boucle for dun premier script, vous ne voulez pas que le second perturbe les itrations de votre boucle for. En revanche, vous pourriez vouloir passer des informations des scripts. Dans ce cas, exportez la variable afin que sa valeur soit connue des autres programmes invoqus par votre script. Pour obtenir la liste de toutes les variables exportes, et leur valeur, excutez la commande interne env (ou export -p). Elles sont toutes accessibles votre script lors de son excution. Pour la plupart, elles ont t dfinies par les scripts de dmarrage de bash (voir le chapitre 16 sur la configuration et la personnalisation de bash). Linstruction dexportation peut simplement nommer la variable exporter. Mme si cette instruction peut tre place juste avant lendroit o vous avez besoin dexporter la valeur, les dveloppeurs regroupent souvent ces instructions avec les dclarations de variables au dbut du script. Vous pouvez galement effectuer lexportation lors laffectation de la variable, mais cela ne fonctionne pas dans les anciennes versions du shell. Une fois la variable exporte, vous pouvez modifier sa valeur sans lexporter nouveau. Vous rencontrerez donc parfois des instructions similaires aux suivantes :
export NOMFICHIER export TAILLE export MAX ... MAX=2048 TAILLE=64 NOMFICHIER=/tmp/toto

ou bien :
export NOMFICHIER=/tmp/toto export TAILLE=64 export MAX=2048 ... NOMFICHIER=/tmp/toto2 ... NOMFICHIER=/tmp/toujours_exporte

Attention : les variables sont exportes par valeur. Si vous modifiez la valeur dans le script appel, sa valeur lors du retour dans le script appelant na pas chang. Se pose donc la question comment renvoyer au script appelant une valeur modifie dans le script appel ? . La rponse est claire : cest impossible. Malheureusement, il nexiste pas dautres rponses. Vous devez concevoir vos scripts de manire ce quils naient pas besoin de cette fonctionnalit. Quels mcanismes emploient donc les dveloppeurs pour sadapter cette contrainte ?

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

94

Chapitre 5 Variables du shell

Une solution consiste envoyer la valeur modifie sur la sortie du script appel et la lire dans le script appelant. Par exemple, supposons quun script exporte la variable $VAL et appelle ensuite un autre script qui la modifie. Pour obtenir la nouvelle valeur, le script invoqu doit lafficher sur la sortie standard et le script invoquant doit la lire et laffecter $VAL (voir la recette 10.5, page 213) :
VAL=$(unAutreScript)

Vous pouvez mme modifier plusieurs valeurs et les afficher tour tour sur la sortie standard. Le programme appelant utilise ensuite une instruction read pour rcuprer chaque ligne de la sortie et les placer dans les variables adquates. Cependant, le script appel ne doit rien afficher dautre sur la sortie standard (tout au moins avant ou parmi les variables) et cela cre une dpendance trs forte entre les scripts (peu intressant quant leur maintenance).

Voir aussi
help export ; le chapitre 16, Configurer bash, page 367, pour plus dinformations sur la configuration et la personnalistion de bash ; la recette 5.6, Afficher les valeurs de toutes les variables, page 94 ; la recette 10.5, Utiliser des fonctions : paramtres et valeur de retour, page 213 ; la recette 19.5, Sattendre pouvoir modifier les variables exportes, page 490.

5.6. Afficher les valeurs de toutes les variables


Problme
Vous souhaitez voir la liste de toutes les variables exportes et leur valeur. Devez-vous afficher chacune delles avec echo ? Comment savoir quelles sont exportes ?

Solution
Utilisez la commande set pour obtenir la valeur de toutes les variables et les dfinitions de fonctions du shell courant. Utilisez la commande env (ou export -p) pour obtenir uniquement les variables qui ont t exportes et qui sont disponibles dans les sous-shells.

Discussion
La commande set, invoque sans autre argument, affiche sur la sortie standard la liste de toutes les variables du shell actuellement dfinies ainsi que leur valeur, dans le format nom=valeur. La commande env est similaire. Lune ou lautre produit une liste assez longue de variables, dont certaines vous seront inconnues. Elles ont t cres pendant le processus de dmarrage du shell.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.7. Utiliser des paramtres dans un script

95

La liste gnre par env est un sous-ensemble de celle cre set, puisque toutes les variables ne sont pas exportes. Si vous ntes intress que par quelques variables ou valeurs prcises, utilisez un tube vers une commande grep. Par exemple :
$ set | grep MA

Dans ce cas, seules les variables dont le nom ou la valeur contient les deux caractres MA sont prsentes.

Voir aussi
help set ; help export ; man env ; le chapitre 16 pour plus dinformations sur la configuration et la personnalistion de bash ; lannexe A pour la liste de toutes les variables internes au shell.

5.7. Utiliser des paramtres dans un script


Problme
Vous souhaitez que les utilisateurs puissent invoquer votre script avec un paramtre. Vous pourriez leur demander de dfinir une variable du shell, mais cela ne se fait pas. Vous avez galement besoin de passer des donnes un autre script. Vous pourriez accorder les deux scripts quant aux variables denvironnement utilises, mais cela cre une trop grande dpendance entre eux.

Solution
Utilisez les paramtres de la ligne de commande. Tous les mots placs sur la ligne de commande dun script shell lui sont accessibles au travers de variables numrotes :
# Script shell simple. echo $1

Le script affiche le premier paramtre fourni sur la ligne de commande au moment de son invocation. Le voici en action :
$ cat tres_simple.sh # Script shell simple. echo ${1} $ ./tres_simple.sh vous voyez ce que je veux dire vous $ ./tres_simple.sh encore une fois encore $

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

96

Chapitre 5 Variables du shell

Discussion
Les autres paramtres sont disponibles dans les variables ${2}, ${3}, ${4}, ${5}, etc. Les accolades sont inutiles avec les nombres dun chiffre, except pour sparer les noms du texte environnant. En gnral, les scripts nattendent que quelques paramtres, mais, si vous arrivez ${10}, vous devez utiliser les accolades ou le shell pensera quil sagit de la variable ${1} immdiatement suivie de la chane littrale 0 :
$ cat delicat.sh echo $1 $10 ${10} $ ./delicat.sh I II III IV V VI VII VIII IX X XI I I0 X $

La valeur du dixime argument est X, mais si vous crivez $10 dans votre script, le shell affiche alors $1, le premier paramtre, suivi immdiatement dun zro, le caractre littral plac ct de $1 dans linstruction echo.

Voir aussi
la recette 5.4, Sparer les noms de variables du texte environnant, page 92.

5.8. Parcourir les arguments dun script


Problme
Vous souhaitez effectuer certaines oprations sur la liste des arguments. Vous savez crire le script de manire traiter un argument en utilisant $1, mais comment prendre en charge un nombre quelconque de paramtres ? Vous souhaitez tre en mesure dinvoquer votre script de la manire suivante :
toutTraiter *.txt

Le shell va construire une liste de noms de fichiers qui correspondent aux motifs *.txt (pour les noms qui se terminent par .txt).

Solution
Utilisez la variable spciale du shell $* pour faire rfrence tous les arguments dans une boucle for :
# bash Le livre de recettes : chmod_tous.1 # # Modifier les autorisations d'un ensemble de fichiers. # for NF in $* do echo modification de $NF chmod 0750 $NF done

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.9. Accepter les paramtres contenant des espaces

97

Discussion
Le choix de la variable $NF vous revient. Vous pouvez utiliser nimporte quel nom de variable. $* fait rfrence tous les arguments fournis sur la ligne de commande. Par exemple, si lutilisateur saisit la ligne suivante :
$ ./chmod_tous.1 abc.txt unAutre.txt toutesMesNotes.txt

le script est invoqu avec la variable $1 gale abc.txt, la variable $2 gale unAutre.txt et la variable $3 gale toutesMesNotes.txt, mais $* contient la liste complte. Autrement dit, dans linstruction for, la variable $* est remplace par la liste, ce qui quivaut crire le code suivant :
for NF in abc.txt unAutre.txt toutesMesNotes.txt do echo modification de $NF chmod 0750 $NF done

La boucle for prend une valeur de la liste la fois, laffecte la variable $NF et poursuit lexcution des instructions places entre do et done. La boucle est ensuite recommence pour chacune des autres valeurs. Mais ce nest pas encore fini ! Ce script fonctionne uniquement avec les fichiers dont les noms ne contiennent pas despace. Consultez les deux recettes suivantes pour savoir comment lamliorer.

Voir aussi
help for ; la recette 6.12, Boucler avec un compteur, page 135.

5.9. Accepter les paramtres contenant des espaces


Problme
Vous avez crit un script qui attend un nom de fichier en paramtre et il semblait fonctionner parfaitement jusqu ce que lun des noms de fichiers contienne une espace.

Solution
Vous devez placer entre guillemets les paramtres qui pourraient contenir des noms de fichiers. Lorsque vous faites rfrence une variable, placez la rfrence entre des guillemets (").

Discussion
Merci beaucoup, Apple ! En voulant tre plus agrables lutilisateur, ils ont rpandu lutilisation des espaces dans les noms de fichiers. Il est ainsi devenu possible de donner

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

98

Chapitre 5 Variables du shell

des noms comme Mon rapport et Nos chiffres de vente la place des moins agrables MonRapport et Nos_chiffres_de_vente. Si lutilisateur en a t satisfait, la vie du shell sen est trouve complique, car lespace est pour lui un sparateur de mots. Les noms de fichiers constituaient prcdemment un seul mot, mais ce nest plus le cas et nous devons en tenir compte. L o un script shell utilisait simplement ls -l $1, il est dsormais prfrable dcrire ls -l "$1", avec des guillemets autour du paramtre. Sans cela, si le paramtre inclut une espace, il sera trait comme deux mots spars et seule une partie du nom se trouvera dans $1. Examinons ce dysfonctionnement :
$ cat simple.sh # Script shell simple. ls -l ${1} $ $ ./simple.sh Oh quel gachis ls: Oh: Aucun fichier ou rpertoire de ce type $

Lors de linvocation du script, nous navons pas plac le nom de fichier entre guillemets. bash voit donc trois arguments et place le premier (Oh) dans $1. La commande ls sexcute donc avec Oh comme seul argument et ne trouve pas ce fichier. Invoquons prsent le script en plaant des guillemets autour du nom de fichier :
$ ./simple.sh "Oh quel gachis" ls: Oh: Aucun fichier ou rpertoire de ce type ls: quel: Aucun fichier ou rpertoire de ce type ls: gachis: Aucun fichier ou rpertoire de ce type $

Ce nest pas encore le rsultat attendu. bash a pris le nom de fichier constitu de trois mots et la plac dans la variable $1 qui se trouve sur la ligne de la commande ls dans notre script. Mais, puisque nous navons pas plac la rfrence la variable entre des guillemets, ls considre chaque mot comme un argument distinct, cest--dire des noms de fichiers spars. Elle ne trouve aucun deux. Essayons une version du script qui entoure la rfrence par des guillemets :
$ cat entreGuillemets.sh # Notez les guillemets. ls -l "${1}" $ $ ./entreGuillemets.sh "Oh quel gachis" -rw-r--r-- 1 jp jp 0 2007-06-20 14:12 Oh quel gachis $

Puisque la rfrence "{$1}" est place entre guillemets, elle est traite comme un seul mot (un seul nom de fichier) et la commande ls reoit alors un seul argument, cest-dire le nom du fichier, et peut effectuer son travail.

Voir aussi
le chapitre 19, Bourdes du dbutant, page 485, pour les erreurs classiques ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.10. Accepter des listes de paramtres contenant des espaces


la recette 1.6, Protger la ligne de commande, page 12 ;

99

lannexe A, Listes de rfrence, page 505 pour plus dinformations sur le traitement de la ligne de commande.

5.10. Accepter des listes de paramtres contenant des espaces


Problme
Comme la conseill la recette prcdente, vous avez plac des guillemets autour de votre variable. Cependant, vous obtenez toujours des erreurs. Votre script est similaire celui de la recette 5.8, page 96, mais il choue lorsque le nom dun fichier contient une espace :
# for NF in $* do chmod 0750 "$NF" done

Solution
Le problme vient de la variable $* employe dans la boucle for. Dans ce cas, nous devons utiliser une autre variable du shell, $@. Lorsquelle est place entre guillemets, chaque argument de la liste rsultante est entour de guillemets. Le script peut donc tre crit de la manire suivante :
#!/usr/bin/env bash # bash Le livre de recettes : chmod_tous.2 # # Modifier les autorisations d'un ensemble de fichiers avec les # guillemets adapts pour le cas o des noms de fichiers incluent # des espaces. # for NF in "$@" do chmod 0750 "$NF" done

Discussion
La variable $* contient la liste des arguments fournis au script shell. Prenons par exemple linvocation suivante du script :
$ monScript voici les arguments

$* fait alors rfrence aux trois arguments voici les arguments. Employez cette variable dans une boucle for :
for NF in $*
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

100

Chapitre 5 Variables du shell

Au premier tour de boucle, le premier mot (voici) est affect $NF. Au deuxime tour, le deuxime mot (les) est affect $NF, etc. Les arguments peuvent tre des noms de fichiers placs sur la ligne de commande grce une correspondance de motifs. Par exemple :
$ monScript *.mp3

Le shell slectionne alors tous les fichiers du rpertoire courant dont les noms se terminent par les quatre caractres .mp3 et les passe au script. Prenons un exemple avec trois fichiers MP3 :
voix.mp3 musique planante.mp3 hit.mp3

Le nom du deuxime fichier contient une espace entre musique et planante. Linvocation suivante :
$ monScript *.mp3

donne en ralit ceci :


$ monScript voix.mp3 musique planante.mp3 hit.mp3

Si le script contient la ligne :


for NF in $*

elle est remplace par :


for NF in voix.mp3 musique planante.mp3 hit.mp3

La liste contient donc quatre mots et non trois. Le huitime caractre du nom du deuxime fichier est une espace (musique planante.mp3), or les espaces sont considres comme des sparateurs de mots par le shell (musique et planante.mp3). $NF aura donc la valeur musique lors de la deuxime itration de la boucle for. la troisime itration, $NF prendra la valeur planante.mp3, qui ne correspond pas non plus au nom de votre fichier. Vous obtiendrez alors des messages derreur concernant des fichiers non trouvs. Il semblerait logique dessayer de placer $* entre guillemets :
for NF in "$*"

Mais la ligne devient alors :


for NF in "voix.mp3 musique planante.mp3 hit.mp3"

La variable $NF contient donc une seule valeur qui correspond lintgralit de la liste. Par consquent, vous obtenez un message derreur similaire au suivant :
chmod: ne peut accder 'voix.mp3 musique planante.mp3 hit.mp3': Aucun fichier ou rpertoire de ce type

La solution consiste employer la variable $@ et lentourer de guillemets. Sans les guillemets, $* et $@ donnent le mme rsultat, mais, avec les guillemets, bash les traites de manire diffrente. Une rfrence $* donne alors lintgralit de la liste lintrieur dun seul jeu de guillemets, comme nous lavons fait jusqu prsent. En revanche, une rfrence $@ ne produit pas une seule chane mais une liste de chanes entre guillemets, une pour chaque argument.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.11. Compter les arguments


Utilisons $@ dans notre exemple de fichiers MP3 :
for NF in "$@"

101

Cette ligne est convertie en :


for NF in "voix.mp3" "musique planante.mp3" "hit.mp3"

Vous pouvez constater que le deuxime nom de fichier est maintenant plac entre guillemets. Lespace fait partie de son nom et nest plus considre comme un sparateur entre deux mots. Lors du deuxime passage dans la boucle, $NF reoit la valeur musique planante.mp3, qui comporte une espace. Vous devez donc faire attention lorsque vous utilisez $NF. Vous devrez probablement la placer galement entre guillemets afin que lespace dans le nom reste un lment de la chane et ne devienne pas un sparateur. Autrement dit, vous devez employer "$NF" :
$ chmod 0750 "$NF"

Pourquoi ne pas toujours opter pour "$@" dans une boucle for ? Peut-tre parce que cette expression est plus difficile saisir et que pour lcriture de scripts rapides, lorsque vous savez que les noms des fichiers ne comportent pas despace, vous pouvez probablement garder lancienne syntaxe $*. En revanche, pour des scripts plus robustes, nous vous recommandons de choisir "$@". Dans ce livre, ces deux variables sont employes de manire quivalente, simplement parce que les vieilles habitudes ont la vie dure.

Voir aussi
la recette 5.8, Parcourir les arguments dun script, page 96 ; la recette 5.9, Accepter les paramtres contenant des espaces, page 97 ; la recette 5.12, Extraire certains arguments, page 103 ; la recette 6.12, Boucler avec un compteur, page 135.

5.11. Compter les arguments


Problme
Vous avez besoin de connatre le nombre de paramtres passs au script.

Solution
Utilisez la variable du shell ${#}. Voici un script qui impose la prsence de trois arguments :
#!/usr/bin/env bash # bash Le livre de recettes : verifier_nb_args # # Vrifier que le nombre d'arguments est correct : # Utilisez la syntaxe donne ou bien if [ $# -lt 3 ] if (( $# < 3 ))

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

102
then printf printf exit 1 elif (( $# then printf printf exit 2 else printf fi

Chapitre 5 Variables du shell

"%b" "Erreur. Il manque des arguments.\n" >&2 "%b" "usage : monScript fichier1 op fichier2\n" >&2 > 3 )) "%b" "Erreur. Il y a trop d'arguments.\n" >&2 "%b" "usage : monScript fichier1 op fichier2\n" >&2

"%b" "Nombre d'arguments correct. Traitement en cours...\n"

Voici un exemple de son excution, une premire fois avec trop darguments et une seconde fois avec le nombre darguments attendu :
$ ./monScript monFichier va ecraser votreFichier Erreur. Il y a trop d'arguments. usage : monScript fichier1 op fichier2 $ ./monScript monFichier ecrase votreFichier Nombre d'arguments correct. Traitement en cours...

Discussion
Aprs les commentaires de dbut ( ne pas oublier dans un script), le test if vrifie si le nombre darguments fournis (indiqu par $#) est suprieur trois. Si cest le cas, nous affichons un message derreur, rappelons lutilisateur la bonne utilisation et quittons le script. Les messages derreur sont redirigs vers lerreur standard. Nous respectons ainsi le rle de lerreur standard comme canal de transport des messages derreur. La valeur de retour du script est galement diffrente selon lerreur dtecte. Bien que ce fonctionnement ne soit pas trs important dans ce cas, il devient utile lorsquun script peut tre invoqu par dautres scripts. Il est ainsi possible de dtecter les erreurs (grce une valeur de retour diffrente de zro) mais galement de diffrencier les types derreurs. Attention : ne confondez pas ${#} avec ${#VAR} ou ${VAR#alt}, uniquement parce quelles utilisent toutes le caractre # entre des accolades. La premire donne le nombre darguments, la deuxime la longueur de la valeur de la variable VAR et la troisime une certaine forme de substitution.

Voir aussi
la recette 4.2, Connatre le rsultat de lexcution dune commande, page 73 ; la recette 5.1, Documenter un script, page 87 ; la recette 5.12, Extraire certains arguments, page 103 ; la recette 5.18, Modifier certaines parties dune chane, page 109 ; la recette 6.12, Boucler avec un compteur, page 135.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.12. Extraire certains arguments

103

5.12. Extraire certains arguments


Problme
Tout script shell digne de ce nom aura deux sortes darguments : des options qui modifient le comportement du script et les arguments rellement utiliss pour laccomplissement de son objectif. Vous avez besoin dun mcanisme permettant denlever les arguments reprsentant des option, aprs les avoir analyss. Reprenons le script suivant :
for NF in "$@" do echo modification de $NF chmod 0750 "$NF" done

Il est trs simple. Il affiche le nom du fichier sur lequel il travaille, puis il en modifie les autorisations. Cependant, vous aimeriez parfois quil naffiche pas le nom du fichier. Comment pouvez-vous ajouter une option qui dsactive ce comportement prolixe tout en conservant la boucle for ?

Solution
#!/usr/bin/env bash # bash Le livre de recettes : utiliser_option # # Utilise et extrait une option. # # Analyse l'argument facultatif. VERBEUX=0; if [[ $1 = -v ]] then VERBEUX=1; shift; fi # # Le vrai travail se fait ici. # for NF in "$@" do if (( VERBEUX == 0 )) then echo modification de $NF fi chmod 0750 "$NF" done

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

104

Chapitre 5 Variables du shell

Discussion
Nous avons ajout une variable doption, $VERBEUX, pour prciser si le nom du fichier doit tre affich avant la modification des autorisations. Cependant, une fois que le script a vu le paramtre -v et fix la variable VERBEUX, -v ne doit plus faire partie de la liste des arguments. Linstruction shift demande bash de dcaler ses arguments dune position vers le bas. Le premier argument ($1) est cart et $2 devient $1, $3 devient $2, etc. Ainsi, lors de lexcution de la boucle for, la liste des arguments (dans $@) ne contient plus loption -v mais dbute directement avec le paramtre suivant. Cette approche est bien adapte la gestion dune seule option. En revanche, lorsquelles sont plus nombreuses, vous avez besoin dun mcanisme un peu plus labor. Par convention, les options dun script shell ne dpendent pas (en gnral) dun emplacement. Par exemple, monScript -a -p doit tre quivalent monScript -p -a. Par ailleurs, un script robuste doit tre en mesure de grer les rptitions doptions et les ignorer ou afficher une erreur. La recette 13.1, page 257, prsentera une analyse plus robuste des options base sur la commande getopts de bash.

Voir aussi
help shift ; la recette 5.8, Parcourir les arguments dun script, page 96 ; la recette 5.11, Compter les arguments, page 101 ; la recette 5.12, Extraire certains arguments, page 103 ; la recette 6.15, Analyser les arguments de la ligne de commande, page 139 la recette 13.1, Analyser les arguments dun script, page 257 ; la recette 13.2, Afficher ses propres messages derreur lors de lanalyse, page 260.

5.13. Obtenir des valeurs par dfaut


Problme
Votre script shell attend des arguments sur la ligne de commande. Vous aimeriez fournir des valeurs par dfaut afin que les valeurs les plus courantes puissent tre utilises sans les saisir chaque invocation.

Solution
Utilisez la syntaxe ${:-} lors des rfrences aux paramtres pour donner une valeur par dfaut :
REP_FICHIER=${1:-"/tmp"}

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.14. Fixer des valeurs par dfaut

105

Discussion
Plusieurs oprateurs spciaux peuvent tre employs dans les rfrences aux variables du shell. Celui utilis prcdemment, loprateur :-, signifie que si la variable $1 nest pas fixe ou si elle est nulle, il faut alors prendre la valeur indique ensuite, cest--dire /tmp dans notre exemple. Dans le cas contraire, il faut prendre la valeur qui se trouve dj dans $1. Cet oprateur peut tre utilis avec nimporte quelle variable du shell, pas uniquement avec les paramtres positionnels (1, 2, 3, etc.), mme sil sagit de son usage le plus frquent. Bien entendu, vous pouvez obtenir le mme rsultat avec une instruction if qui vrifie si la variable est nulle ou si elle est indfinie (nous laissons cet exercice au lecteur), mais ce type de contrle est tellement frquent dans les scripts que cette syntaxe est un raccourci bienvenu.

Voir aussi
la page de manuel de bash sur la substitution des paramtres ; Le shell bash de Cameron Newham (ditions OReilly), pages 9192 ; Introduction aux scripts shell de Nelson H.F. Beebe et Arnold Robbins (ditions OReilly), pages 115116 ; la recette 5.14, Fixer des valeurs par dfaut, page 105.

5.14. Fixer des valeurs par dfaut


Problme
Votre script doit sappuyer sur certaines variables denvironnement, quil sagisse de celles frquemment utilises (par exemple $USER) ou de celles propres votre projet. Si vous voulez crire un script shell robuste, vous devez vrifier que ces variables ont une valeur correcte. Comment pouvez-vous assurer que cest bien le cas ?

Solution
Utilisez loprateur daffectation dans la premire rfrence une variable du shell afin dattribuer une valeur cette variable si elle nest pas dj dfinie :
cd ${HOME:=/tmp}

Discussion
Dans lexemple prcdent, la rfrence $HOME retourne la valeur actuelle de la variable $HOME, except si elle est vide ou si elle nest pas dfinie. Dans ces deux situations, la valeur /tmp est retourne et affecte $HOME afin que les prochaines rfrences cette variable retournent cette nouvelle valeur. Voici ce principe en action :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

106
$ echo ${HOME:=/tmp} /home/uid002 $ unset HOME # En gnral, viter. $ echo ${HOME:=/tmp} /tmp $ echo $HOME /tmp $ cd ; pwd /tmp $

Chapitre 5 Variables du shell

Lappel unset retire toute valeur la variable. Ensuite, lorsque nous utilisons loprateur := dans la rfrence cette variable, la nouvelle valeur (/tmp) lui est attribue. Les rfrences ultrieures $HOME retournent cette nouvelle valeur. Vous ne devez pas oublier lexception suivante concernant loprateur daffectation : ce mcanisme ne fonctionne pas avec les paramtres positionnels (par exemple, $1 ou $*). Dans ce cas, utilisez :- dans des expressions de la forme ${1:-defaut} qui retourneront la valeur sans tenter laffectation. Pour mmoriser ces symboles sotriques, vous pouvez vous aider de la diffrence visuelle entre ${VAR:=valeur} et ${VAR:-valeur}. La variante := ralise une affectation et retourne la valeur place droite de loprateur. La variante :- ne fait que la moiti de ce travail elle retourne uniquement la valeur sans procder laffectation puisque le symbole nest que la moiti du signe gal (une barre horizontale la place de deux barres). Si cela ne vous aide pas, oubliez-le.

Voir aussi
la recette 5.13, Obtenir des valeurs par dfaut, page 104.

5.15. Utiliser null comme valeur par dfaut valide


Problme
Vous devez fixer une valeur par dfaut, mais vous souhaitez que la chane vide soit accepte. La valeur par dfaut ne doit servir que dans le cas o la variable nest pas dfinie. Avec loprateur ${:=}, la nouvelle valeur est choisie dans deux cas : lorsque la valeur de la variable du shell na pas t pralablement fixe (ou a t explicitement retire) et lorsque la valeur a t fixe mais est vide, par exemple HOME="" ou HOME=$AUTRE ( $AUTRE nayant pas de valeur).

Solution
Le shell peut diffrencier ces deux cas. En retirant les deux-points (:), vous indiquez que la substitution ne doit avoir lieu que si la valeur nest pas fixe. Si vous crivez simplement ${HOME=/tmp}, sans les deux-points, laffectation se fera uniquement dans le cas o la variable ne contient pas de valeur (na jamais t fixe ou a t explicitement indfinie).

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.16. Indiquer une valeur par dfaut variable

107

Discussion
Amusons-nous nouveau avec la variable $HOME, mais, cette fois-ci, sans les deux-points de loprateur :
$ echo ${HOME=/tmp} # La substitution nest pas ncessaire. /home/uid002 $ HOME="" # En gnral, viter. $ echo ${HOME=/tmp} # La substitution na pas lieu. $ unset HOME # En gnral, viter. $ echo ${HOME=/tmp} # La substitution a lieu. /tmp $ echo $HOME /tmp $

Lorsque nous affectons une chane vide la variable $HOME, loprateur = ne procde pas la substitution car $HOME a bien une valeur, mme si elle est nulle. En revanche, lorsque nous supprimons la dfinition de la variable, la substitution a lieu. Si vous souhaitez autoriser les chanes vides, utilisez loprateur =, sans les deux-points. Cependant, := est plus souvent employ car vous ne pouvez pas faire grand-chose avec une valeur vide, que ce soit dlibr ou non.

Voir aussi
la recette 5.13, Obtenir des valeurs par dfaut, page 104 ; la recette 5.14, Fixer des valeurs par dfaut, page 105.

5.16. Indiquer une valeur par dfaut variable


Problme
Vous avez besoin que la valeur par dfaut de la variable ne soit pas une constante.

Solution
La partie droite des rfrences aux variables du shell peut tre un peu plus labore quune simple constante. Par exemple :
cd ${BASE:="$(pwd)"}

Discussion
Comme le montre lexemple, la valeur de remplacement nest pas ncessairement une chane constante. Il peut sagir du rsultat dune expression plus complexe, y compris issue de lexcution de commandes dans un sous-shell. Dans notre exemple, si la variable $BASE nest pas fixe, le shell excute la commande interne pwd (pour obtenir le rpertoire de travail) et affecte la chane gnre la variable.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

108

Chapitre 5 Variables du shell

Que pouvez-vous donc placer sur la partie droite de loprateur (et des autres oprateurs similaires) ? La page de manuel de bash stipule que le contenu de la partie droite de loprateur est soumis lexpansion du tilde, lexpansion des paramtres, la substitution de commandes et lexpansion arithmtique . Voici ce que cela signifie : lexpansion des paramtres vous permet dutiliser dautres variables du shell dans lexpression : ${BASE:=${HOME}} ; lexpansion du tilde signifie quune expression comme ~bob est reconnue et remplace par le rpertoire personnel de lutilisateur bob. Utilisez ${BASE:=~uid17} pour que la valeur par dfaut soit le rpertoire personnel de lutilisateur uid17, mais ne placez pas cette chane entre guillemets car ils annulent lexpansion du tilde ; la substitution de commandes a t employe dans lexemple. Elle excute les commandes et affectent leur sortie la variable. Les commandes sont incluses entre les parenthses, cest--dire $( commandes ) ; lexpansion arithmtique vous permet deffectuer une arithmtique entire dans lexpression, en utilisant la syntaxe $(( ... )). En voici un exemple :
echo ${BASE:=/home/uid$((ID+1))}

Voir aussi
la recette 5.13, Obtenir des valeurs par dfaut, page 104.

5.17. Afficher un message derreur pour les paramtres non dfinis


Problme
Tous ces raccourcis pour prciser une valeur par dfaut sont bien pratiques, mais vous voulez obliger les utilisateurs donner des valeurs, sans quoi le traitement ne peut pas se poursuivre. Sils ont oubli un paramtre, cest peut-tre parce quils ne comprennent pas vraiment lutilisation de votre script. Vous ne voulez rien laisser au hasard. Vous recherchez donc un moyen, autre quune suite dinstructions if, pour vrifier chacun des nombreux paramtres.

Solution
Utilisez la syntaxe ${:?} lors dune rfrence a un paramtre. bash affichera un message derreur et quittera le script si le paramtre nest pas fix ou sil est nul.
#!/usr/bin/env bash # bash Le livre de recettes : verifier_params_indefinis # USAGE="usage : monScript rpertoire fichierSource conversion" REP_FICHIER=${1:?"Erreur. Vous devez indiquer un rpertoire initial."} FICHIER_SRC=${2:?"Erreur. Vous devez fournir un fichier source."} TYPE_CONV=${3:?"Erreur. ${USAGE}"}

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.18. Modifier certaines parties dune chane


Voici un exemple dexcution du script avec des paramtres insuffisants :

109

$ ./monScript /tmp /dev/null ./monScript: line 5: 3: Erreur. usage: monScript rpertoire fichierSource conversion $

Discussion
Le contrle consiste vrifier si le premier paramtre est fix (ou nul). Dans le cas contraire, il affiche un message derreur et quitte le script. La troisime variable utilise une autre variable du shell dans son message. Vous pouvez mme y excuter une autre commande :
TYPE_CONV=${3:?"Erreur. $USAGE. $(rm $REP_FICHIER)"}

Si le troisime paramtre nest pas fix, le message derreur contient la phrase Erreur. , suivie de la valeur de la variable $USAGE et de toute sortie gnre par la commande qui supprime le fichier indiqu par la variable $REP_FICHIER. Cest vrai, l nous nous laissons un peu emporter. Vous pouvez rendre votre script shell affreusement compact. Il est prfrable de gaspiller un peu despace et quelques octets pour que sa logique soit plus claire :
if [ -z "$3" ] then echo "Erreur. $USAGE" rm $REP_FICHIER fi

Voici une autre remarque : le message derreur produit par ${:?} inclut le nom de fichier du script shell et un numro de ligne. Par exemple :
./monScript: line 5: 3: Erreur. usage: monScript rpertoire fichierSource conversion

Puisque nous navons aucun moyen dagir sur cette partie du message et puisquelle ressemble une erreur dans le script shell lui-mme, sans mentionner le problme de lisibilit, cette technique nest pas trs rpandue dans les scripts shell de qualit industrielle. Elle peut cependant savrer trs utile pour le dbogage.

Voir aussi
la recette 5.13, Obtenir des valeurs par dfaut, page 104 ; la recette 5.14, Fixer des valeurs par dfaut, page 105 ; la recette 5.16, Indiquer une valeur par dfaut variable, page 107.

5.18. Modifier certaines parties dune chane


Problme
Vous souhaitez renommer tout un ensemble de fichiers. Les noms des fichiers sont correctement traits, mais pas leurs extensions.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

110

Chapitre 5 Variables du shell

Solution
Utilisez lexpansion des paramtres de bash, qui retire le texte correspondant un motif.
#!/usr/bin/env bash # bash Le livre de recettes : suffixer # # Renommer les fichiers *.bof en *.bash. for NF in *.bof do mv "${NF}" "${NF%bof}bash" done

Discussion
La boucle for parcourt la liste des fichiers du rpertoire de travail dont les noms se terminent par .bof. La variable $NF prend successivement la valeur de chaque nom. lintrieur de la boucle, la commande mv renomme le fichier (le dplace de lancien nom vers le nouveau nom). Nous plaons chaque nom de fichier entre guillemets pour le cas o lun deux contiendrait des espaces. Llment central de cette opration rside dans la rfrence $NF qui inclut une suppression automatique des caractres bof de fin. La notation ${ } dlimite la rfrence pour que le terme bash qui suit soit ajout immdiatement la fin de la chane. Voici le fonctionnement dcompos en plusieurs tapes :
SANS_BOF="${NF%bof}" NOUVEAU_NOM="${SANS_BOF}bash" mv "${NF}" "${NOUVEAU_NOM}"

Vous pouvez ainsi voir les diffrentes phases de suppression du suffixe indsirable, de la cration du nouveau nom et du renommage du fichier. Cependant, runir toutes ces tapes sur une seule ligne est intressant, une fois les oprateurs spciaux bien compris. Outre la suppression dune sous-chane de la variable, nous remplaons galement bof par bash. Par consquent, nous pouvons utiliser loprateur de substitution pour les rfrences de variable, cest--dire la barre oblique (/). De manire similaire aux commandes dun diteur (par exemple, celles disponibles dans vi et sed) qui utilisent la barre oblique pour dlimiter les substitutions, voici une autre version de la conversion :
mv "${NF}" "${NF/.bof/.bash}"

Contrairement aux commandes de ces diteurs, la barre oblique finale est absente car son rle est jou par laccolade fermante. Cependant, nous navons pas choisi cette approche car elle ne permet pas dancrer la substitution, qui se produit donc nimporte o dans la variable. Par exemple, si un fichier se nomme boboffert.bof, la substitution donne donc bobashfert.bof, ce qui ne correspond pas vraiment ce que nous voulions. En utilisant une double barre oblique la place de la premire, toutes les occurrences dans la variable seraient remplaces. Nous obtiendrions alors bobashfert.bash, ce qui nest pas mieux.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

5.19. Utiliser les tableaux

111

Le tableau 5-1 prsente les diffrents oprateurs de manipulation du contenu des variables au sein des rfrences. Testez-les tous, ils sont trs utiles. Tableau 5-1. Oprateurs de manipulation de chanes
lintrieur de ${ ... } nom:dbut:longueur #nom nom#motif nom##motif nom%motif nom%%motif nom/motif/chane nom//motif/chane Action effectue Retourne la sous-chane commenant dbut et ayant la longueur indique. Retourne la longueur de la chane. Supprime le (plus court) motif partir du dbut. Supprime le (plus long) motif partir du dbut. Supprime le (plus court) motif partir de la fin. Supprime le (plus long) motif partir de la fin. Remplace la premire occurrence. Remplace toutes les occurrences.

Voir aussi
man rename ; la recette 12.5, Comparer deux documents, page 254.

5.19. Utiliser les tableaux


Problme
Nous avons prsent de nombreux scripts avec des variables, mais bash peut-il prendre en charge un tableau de variables ?

Solution
Oui. bash dispose prsent dune syntaxe pour les tableaux une dimension.

Description
Les tableaux sont faciles initialiser si vous en connaissez les valeurs au moment de lcriture du script. Voici le format employer :
MONTAB=(premier deuxime troisime quatrime)

Chaque lment du tableau est un mot spar de la liste incluse entre les parenthses. Ensuite, vous pouvez faire rfrence chacun de ces lments de la manire suivante :
echo les ${MONTAB[0]} et ${MONTAB[2]} coureurs

La sortie est alors :


les premier et troisime coureurs

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

112

Chapitre 5 Variables du shell

Si vous crivez simplement $MONTAB, vous obtenez uniquement le premier lment, comme si vous aviez utilis ${MONTAB[0]}.

Voir aussi
Le shell bash de Cameron Newham (ditions OReilly), pages 158162, pour plus dinformations sur les tableaux.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6
Logique et arithmtique
Larithmtique constitue lune des principales volutions des versions modernes de bash par rapport au shell Bourne originel. Les premires versions de linterprteur de commandes taient incapables dune quelconque arithmtique. Il fallait invoquer un programme spar, mme pour ajouter 1 une variable. En un sens, cest un tribut la versatilit et la puissance du shell ; il peut servir normment de tches, malgr sa pitre prise en charge de larithmtique. ce moment-l, personne nimaginait que le shell pourrait tre aussi utile et aussi employ. Cependant, lautomatisation des tches rptitives, par un simple mcanisme de comptage, a rvl le besoin dune syntaxe simple et directe pour larithmtique. Son absence dans le shell Bourne originel a largement contribu au succs du shell C (csh) lorsque quil a propos un syntaxe de programmation similaire celle du langage C, notamment pour les variables numriques. Mais tout cela a bien volu. Si vous navez pas utilis larithmtique de bash depuis un moment, vous risquez dtre fort surpris. Outre larithmtique, bash dispose des structures de contrle familires tous les programmeurs. La construction if/then/else permet de prendre des dcisions. Les boucles while et for sont galement prsentes, mais vous verrez que bash y ajoute quelques particularits. Linstruction case offre de nombreuses possibilits, grce sa correspondance de motif. Il existe aussi une construction assez trange, nomme select. Aprs avoir pass en revue toutes ces fonctionnalits, nous terminerons ce chapitre par le dveloppement de deux calculatrices simples en ligne de commande.

6.1. Utiliser larithmtique dans un script


Problme
Vous avez besoin deffectuer quelques calculs simples dans votre script.

Solution
Utilisez $(( )) ou let pour les expressions arithmtiques entires.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

114
TOTAL=$((TOTAL + 5 + MAX * 2)) let TOTAL+=5+MAX*2

Chapitre 6 Logique et arithmtique

Discussion
Tant que les oprations se font en arithmtique entire, vous pouvez employer tous les oprateurs standard (de type C) lintrieur de $(( )). Vous disposez galement dun oprateur, **, pour calculer une puissance. Par exemple, MAX=$((2**8)) donne 256. Les espaces ne sont pas ncessaires, ni interdites, autour des oprateurs et des arguments dans une expression $(( )) (mais les deux astrisques de ** doivent tre colls). En revanche, vous ne pouvez pas placer despace autour du signe gal, comme cest le cas pour toute affectation de variables bash. Si vous crivez la ligne suivante :
TOTAL = $((TOTAL + 5)) # Ne donne pas le rsultat escompt !

bash tente dexcuter un programme nomm TOTAL, dont le premier argument est un signe gal et le deuxime le rsultat de la somme de 5 et de $TOTAL. Noubliez pas de retirer toute espace qui pourrait se trouver autour du signe gal. Pour obtenir la valeur dune variable, vous placez normalement le symbole $ devant son nom (par exemple, $TOTAL ou $MAX). Cependant, cela nest pas ncessaire lintrieur des doubles parenthses. Par exemple, dans lexpression $((TOTAL +5 MAX * 2)), le symbole dollar nest pas obligatoire devant le nom des variables du shell. En effet, le caractre $ plac lextrieur des parenthses sapplique lexpression entire. En revanche, pour utiliser un paramtre positionnel (par exemple, $2), il est ncessaire dajouter le symbole $ afin de le diffrencier dune constante numrique (par exemple, 2) :
TOTAL=$((TOTAL + $2 + DECALAGE))

Linstruction let interne bash apporte un mcanisme similaire pour effectuer une arithmtique entire avec les variables du shell. Elle emploie les mmes oprateurs arithmtiques que la construction $(( )) :
let TOTAL=TOTAL+5

En revanche, elle fournit dautres oprateurs daffectation plus exotiques. Par exemple, la ligne suivante est quivalente la prcdente :
let TOTAL+=5

Elle devrait tre familire aux programmeurs C/C++ et Java. Le tableau 6-1 donne la liste de ces oprateurs daffectation particuliers. Tableau 6-1. Les oprateurs daffectation de bash
Oprateur
= *= /= %=

Opration en plus de laffectation affectation simple multiplication division reste

Utilisation
a=b a*=b a/=b a%=b

Signification
a=b a=(a*b) a=(a/b) a=(a%b)

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.1. Utiliser larithmtique dans un script


Tableau 6-1. Les oprateurs daffectation de bash (suite)
Oprateur
+= -= <<= >>= &= ^= |=

115

Opration en plus de laffectation addition soustraction dcalage dun bit gauche dcalage dun bit droite Et binaire Ou exclusif binaire Ou binaire

Utilisation
a+=b a-=b a<<=b a>>=b a&=b a^=b a|=b

Signification
a=(a+b) a=(a-b) a=(a<<b) a=(a>>b) a=(a&b) a=(a^b) a=(a|b)

Ces oprateurs daffectation sont galement disponibles dans la construction $(( )), condition quils soient placs intrieur des doubles parenthses. Le premier oprateur correspond strictement laffectation dune variable shell. Les affectations peuvent galement tre effectues en cascade, par le biais de loprateur virgule :
echo $(( X+=5 , Y*=3 ))

Dans cet exemple, les deux affectations sont ralises et le rsultat de la seconde expression est affich, car loprateur virgule retourne la valeur de sa deuxime expression. Si vous ne voulez pas afficher le rsultat, il suffit dutiliser linstruction let :
let X+=5 Y*=3

Loprateur virgule est inutile ici car chaque mot dune instruction let constitue une expression arithmtique en soi. En gnral, dans les scripts bash, certains caractres ont des significations particulires, par exemple lastrisque pour les motifs gnriques ou les parenthses pour lexcution dun sous-shell. En revanche, dans les instructions let ou les constructions $(( )), ils perdent leur signification spciale et il est donc inutile dutiliser les guillemets ou les barres obliques inverses pour les chapper :
let Y=(X+2)*10 Y=$(( ( X + 2 ) * 10 ))

Linstruction let et la construction $(( )) diffrent galement sur un autre point, la gestion des espaces. Linstruction let exige quil ny ait aucune espace autour de loprateur daffectation (le signe gal), ainsi quautour des autres oprateurs. Lintgralit de lexpression arithmtique doit constituer un seul mot. En revanche, la construction $(( )) est plus tolrante et accepte les espaces lintrieur des parenthses. Elle est donc moins sujette aux erreurs et le code est plus facile lire. Il sagit de notre solution prfre pour effectuer une arithmtique entire dans bash. Cependant, nous faisons une exception pour laffectation += occasionnelle ou loprateur ++, ou bien lorsque nous devenons nostalgiques des beaux jours de la programmation en BASIC (avec son instruction LET).

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

116

Chapitre 6 Logique et arithmtique

Attention, il sagit dune arithmtique entire et non en virgule flottante. Une expression comme 2/3 donne donc la valeur 0 (zro). La division se fait sur des entiers et supprime donc la partie dcimale.

Voir aussi
help let ; la page de manuel de bash.

6.2. Conditionner lexcution du code


Problme
Vous souhaitez vrifier que le nombre darguments est correct et prendre des dcisions diffrentes selon le rsultat du test. Vous avez besoin dune construction de branchement.

Solution
Linstruction if de bash est similaire celle des autres langages de programmation :
if [ $# -lt 3 ] then printf "%b" "Erreur. Il manque des arguments.\n" printf "%b" "usage : monScript fichier1 op fichier2\n" exit 1 fi

Ou bien :
if (( $# < 3 )) then printf "%b" "Erreur. Il manque des arguments.\n" printf "%b" "usage : monScript fichier1 op fichier2\n" exit 1 fi

Voici une combinaison complte dinstruction if, avec une clause elif (version bash de else-if) et une clause else :
if (( $# < then printf printf exit 1 elif (( $# then printf printf 3 )) "%b" "Erreur. Il manque des arguments.\n" "%b" "usage : monScript fichier1 op fichier2\n" > 3 )) "%b" "Erreur. Il y a trop darguments.\n" "%b" "usage : monScript fichier1 op fichier2\n"

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.2. Conditionner lexcution du code


exit 2 else printf "%b" "Nombre darguments correct. Traitement en cours...\n" fi

117

Vous pouvez mme crire du code comme celui-ci :


[ $resultat = 1 ] \ && { echo "Le rsultat est 1 ; excellent." ; exit 0; || { echo "Ouh l l, disparaissez ! " ; exit 120; } } \

(Pour une explication de ce dernier exemple, consultez la recette 2.14, page 44.)

Discussion
Nous devons examiner deux aspects : la structure de base dune instruction if et la raison de ses diffrentes syntaxes (parenthses ou crochets, oprateurs ou options). Le premier peut expliquer le second. Voici la forme gnrale dune instruction if (daprs la page de manuel de bash) :
if liste; then liste; [ elif liste; then liste; ] ... [ else liste; ] fi

Les caractres [ et ] de notre description servent dlimiter les parties facultatives de linstruction (par exemple, certaines instructions if nont pas de clause else). Commenons par examiner la version de if sans les lments facultatifs. Voici la forme la plus simple dune instruction if :
if liste; then liste; fi
Dans bash, le point-virgule joue le mme rle que le saut de ligne ; il termine une instruction. Dans les premiers exemples de la section Solution, nous aurions donc pu rendre les exemples plus concis en utilisant des points-virgules, mais les sauts de ligne amliorent leur lisibilit.

Si lon sen rfre aux autres langages de programmation, le sens de la partie then liste semble clair il sagit des instructions qui seront excutes lorsque la condition du if svalue vrai. En revanche, quen est-il de if liste ? Nous sommes plutt habitus if expression. Noubliez pas que nous sommes dans un shell, cest--dire un interprteur de commandes. Son rle principal est dexcuter des commandes. Par consquent, la partie liste qui se trouve aprs if contient une liste de commandes. Dans ce cas, quel est llment qui permet de dterminer le branchement (lexcution de then ou de else) ? Il sagit tout simplement de la valeur de retour de la dernire commande de la liste. Cette valeur, comme vous devez vous en souvenir, est galement disponible dans la variable $?. Pour illustrer ce point, prenons un exemple un peu trange :
$ cat essayerCeci.sh if ls; pwd; cd $1; then echo succs; else

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

118
echo chec; fi pwd $ bash ./essayerCeci.sh /tmp ... $ bash ./essayerCeci.sh /inexistant ... $

Chapitre 6 Logique et arithmtique

Dans ce script est un peu bizarre, le shell excute trois commandes (ls, pwd et cd) avant deffectuer le branchement. Largument de la commande cd est le premier fourni lors de linvocation du script. Sil est absent, le shell excute simplement cd, qui ramne dans le rpertoire personnel. Comment cela fonctionne-t-il ? Laffichage de succs ou de chec dpend de la russite de la commande cd. Dans notre exemple, cd est la dernire commande de la liste donne if. Si elle choue, la clause else est slectionne. En revanche, si elle russit, la clause then est choisie. Les commandes bien crites et les commandes internes retournent la valeur 0 (zro) lorsquelles ne rencontrent aucune erreur pendant leur excution. Si elles dtectent un problme, par exemple un paramtre erron, des erreurs dentre/sortie ou un fichier non trouv, elles retournent une valeur diffrente de zro (et souvent une valeur diffrente pour chaque type derreur dtecte). Cest pourquoi il est important que les dveloppeurs de scripts shell et de programmes C (ou en dautres langages) sassurent que les valeurs retournes par leur code sont significatives. Le bon fonctionnement dune instruction if dune autre personne pourrait en dpendre ! Voyons maintenant comment nous pouvons passer de cette construction if, un tantinet trange, une instruction if plus habituelle, comme on la rencontre gnralement dans les programmes. Cest le cas dans les exemples montrs au dbut de cette recette. En effet, ils ne ressemblent pas vraiment une liste dinstructions. Essayons le code suivant qui implique le test dune taille :
if test $# -lt 3 then echo recommencez. fi

Remarquez-vous ce qui pourrait ressembler, sinon une liste complte, tout au moins une seule commande shell ? La commande interne test, qui compare les valeurs de ses arguments, retourne 0 lorsque son valuation est vraie, 1 sinon. Pour le constater par vous-mme, essayez la commande test sur une ligne et vrifiez sa valeur de retour avec echo $?. Notre premier exemple, qui commenait par if [ $# -lt 3 ], ressemble fortement celui bas sur linstruction test. En effet, [ est en ralit la commande test, cest--dire juste un autre nom pour la mme commande. Lorsque vous utilisez le nom [, il faut galement un ] en dernier paramtre, pour des raisons de lisibilit et desthtisme. Cela explique donc la premire syntaxe : lexpression de linstruction if est en ralit une liste dune seule commande, test.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.3. Tester les caractristiques des fichiers

119

Dans les premires versions dUnix, test tait un excutable spar et [ un lien vers cet excutable. Ils existent encore sous forme de programmes excutables dans dautres shells, mais bash a choisi den faire des commandes internes.

prsent, examinons lexpression if (( $# < 3 )) utilise dans le deuxime exemple de la section Solution. Les doubles parenthses font partie des commandes combines. Elles sont utiles dans les instructions if car elles effectuent une valuation arithmtique de lexpression quelles contiennent. Il sagit dune amlioration rcente de bash, ajoute spcialement pour les cas dutilisation comme dans les instructions if. Les distinctions importantes entre les deux formes de syntaxe utilises dans une instruction if rsident dans lexpression des tests et les aspects tests. Les doubles parenthses sont strictement des expressions arithmtiques. Les crochets peuvent galement tester certaines caractristiques de fichiers, mais leur syntaxe est moins adapte aux expressions arithmtiques. Cest dautant plus vrai lorsque vous regroupez de longues expressions avec des parenthses (qui doivent tre places entre guillemets ou chappes).

Voir aussi
help if ; help test ; man test ; la recette 2.14, Enregistrer ou runir la sortie de plusieurs commandes, page 44 ; la recette 4.2, Connatre le rsultat de lexcution dune commande, page 73 ; la recette 6.3, Tester les caractristiques des fichiers, page 119 ; la recette 6.5, Tester les caractristiques des chanes, page 123 ; la recette 15.11, Obtenir lentre depuis une autre machine, page 354.

6.3. Tester les caractristiques des fichiers


Problme
Vous souhaitez rendre votre script robuste en vrifiant que le fichier dentre existe avant de le lire, que vous avez les droits dcriture sur le fichier de sortie ou quun rpertoire existe avant dinvoquer cd. Comment effectuer tous ces contrles dans un script bash ?

Solution
Utilisez les possibilits de vrification des caractristiques de fichiers offertes par la commande test dans des instructions if. Vos problmes particuliers peuvent tre rsolus par des scripts ressemblant celui-ci :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

120

Chapitre 6 Logique et arithmtique

#!/usr/bin/env bash # bash Le livre de recettes : verifier_fichier # REP=/tmp FICHIER_ENTREE=/home/yucca/donnees.reelles FICHIER_SORTIE=/home/yucca/autres.resultats if [ -d "$REP" ] then cd $REP if [ -e "$FICHIER_ENTREE" ] then if [ -w "$FICHIER_SORTIE" ] then calculer < "$FICHIER_ENTREE" >> "$FICHIER_SORTIE" else echo "Impossible d'crire dans $FICHIER_SORTIE" fi else echo "Impossible de lire depuis $FICHIER_ENTREE" fi else echo "Impossible d'aller dans $REP" fi

Discussion
Nous plaons toutes les rfrences aux diffrents fichiers entre guillemets pour le cas o les chemins contiendraient des espaces. Il ny en a pas dans cet exemple, mais ce sera peut-tre le cas si vous modifiez le chemin. Nous testons puis excutons la commande cd avant les deux autres conditions. Dans cet exemple, cela na pas vraiment dimportance, mais si FICHIER_ENTREE ou FICHIER_SORTIE taient des chemins relatifs (qui ne dbutent pas la racine du systme de fichiers, cest-dire sans commencer par / ), le test peut svaluer vrai avant cd et faux aprs, ou vice versa. En procdant ainsi, le test est effectu juste avant lutilisation des fichiers. Loprateur >> nous permet dajouter la sortie dans le fichier des rsultats, sans lcraser. Si vous deviez le remplacer, les autorisations dcriture sur ce fichier nauraient pas besoin dtre testes car vous auriez alors uniquement besoin dune autorisation dcriture sur le rpertoire qui le contient. Lensemble des tests peut tre combin dans une longue instruction if en utilisant loprateur -a, mais, en cas dchec, il est impossible de donner un message derreur utile car vous ne savez de quel test vient le problme. Vous pouvez galement tester dautres caractristiques. Trois dentre elles utilisent des oprateurs ordinaires, chacun attendant deux noms de fichiers : FICHIER1 -nt FICHIER2 Est plus rcent que (en fonction de la date de dernire modification).

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.3. Tester les caractristiques des fichiers


FICHIER1 -ot FICHIER2 Est plus ancien que.

121

FICHIER1 -ef FICHIER2 Ont le mme numro de priphrique et dinode (fichier identique, mme sil sagit de liens diffrents). Le tableau 6-2 dcrit les autres tests associs aux fichiers (la section Oprateurs de test, page 536, donne une liste plus complte). Il sagit uniquement doprateurs unaires qui prennent la forme option nomFichier, par exemple if [ -e monFichier ]. Tableau 6-2. Oprateurs unaires pour le test des caractristiques de fichiers
Option
-b -c -d -e -f -g -h -G -k -L -O -p -r -s -S -u -w -x

Description Le fichier est un priphrique en mode bloc (comme /dev/hda1). Le fichier est un priphrique en mode caractre (comme /dev/tty). Le fichier est un rpertoire. Le fichier existe. Le fichier est un fichier normal. Le bit SGID (set group ID) du fichier est positionn. Le fichier est un lien symbolique (identique -L). Le fichier appartient lidentifiant de groupe rel. Le bit sticky du fichier est positionn. Le fichier est un lien symbolique (identique -h). Le fichier appartient lidentifiant dutilisateur rel. Le fichier est un tube nomm. Le fichier peut tre lu. Le fichier nest pas vide (sa taille est suprieure zro). Le fichier est une socket. Le bit SUID (set user ID) du fichier est positionn. Le fichier peut tre modifi. Le fichier peut tre excut.

Voir aussi
la recette 2.10, Ajouter la sortie un fichier existant, page 41 ; la recette 4.6, Utiliser moins dinstructions if, page 78 ; la section Oprateurs de test, page 536.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

122

Chapitre 6 Logique et arithmtique

6.4. Tester plusieurs caractristiques


Problme
Comment pouvez-vous tester plusieurs caractristiques ? Faut-il imbriquer les instructions if ?

Solution
Utilisez les oprateurs logiques ET (-a) et OU (-o) pour combiner plusieurs tests en une seule expression. Par exemple :
if [ -r $FICHIER -a -w $FICHIER ]

Cette expression vrifie si le fichier peut tre lu et modifi.

Discussion
Toutes les conditions de test dun fichier incluent la vrification implicite de son existence. Il est donc inutile de vrifier si un fichier existe et sil peut tre lu. Sil nexiste pas, il ne pourra pas tre lu. Les conjonctions (-a pour ET et -o pour OU) peuvent tre employes avec toutes les conditions de test. Elles ne sont pas limites aux caractristiques de fichiers. Une mme instruction peut inclure plusieurs conjonctions et/ou. Vous devrez peut-tre ajouter des parenthses pour fixer les priorits, comme dans a et (b ou c), mais noubliez pas dannuler la signification particulire des parenthses en les faisant prcder dune barre oblique inverse ou en les plaant entre guillemets. Cependant, ne placez pas lintgralit de lexpression entre des guillemets, car elle deviendrait alors un seul terme trait comme un test de chane vide (voir la recette 6.5, page 123). Voici un exemple de test plus complexe dans lequel les parenthses sont correctement chappes :
if [ -r "$NF" -a \( -f "$NF" -o -p "$NF" \) ]

Lordre dvaluation de ces expressions nest pas le mme quen Java ou C. Dans ces langages, si la premire partie dune expression ET est fausse (ou vraie dans une expression OU), la seconde partie nest pas value (lexpression est court-circuite). Cependant, puisque le shell effectue plusieurs passes sur linstruction pendant la prparation de son valuation (substitution des paramtres, etc.), les deux parties de la condition peuvent tre partiellement values. Si dans cet exemple simple cela na pas dimportance, il nen est pas de mme dans les cas plus compliqus. Par exemple :
if [ -z "$V1" -o -z "${V2:=ZUT}" ]

Mme si $V1 est vide, ce qui est suffisant pour ne pas avoir besoin dvaluer la deuxime partie de la condition (vrifier si $V2 est vide) de linstruction if, il est possible que la valeur de $V2 ait dj t modifie (comme effet secondaire de la substitution des paramtres pour $V2). Ltape de substitution des paramtres est effectue avant les tests -z. Suivez-vous ? Que ce soit le cas ou non, sachez simplement que vous ne devez pas vous appuyer sur des raccourcis dans vos conditions. Si vous avez besoin de ce genre de fonctionnement, dcomposez linstruction en deux if imbriques.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.5. Tester les caractristiques des chanes

123

Voir aussi
la recette 6.5, Tester les caractristiques des chanes, page 123 ; lannexe C, Analyse de la ligne de commande, page 569, pour plus dinformations sur le traitement de la ligne de commande.

6.5. Tester les caractristiques des chanes


Problme
Vous souhaitez que votre script vrifie la valeur de certaines chanes avant de les employer. Les chanes peuvent reprsenter lentre de lutilisateur, tre lues depuis un fichier ou tre des variables denvironnement passes au script.

Solution
La commande interne test permet deffectuer quelques tests simples, en utilisant le crochet dans les instructions if. Vous pouvez vrifier si une variable contient du texte et si les valeurs (chanes) de deux variables sont gales.

Discussion
Par exemple :
#!/usr/bin/env bash # bash Le livre de recettes : verifier_chaine # # instruction if # Vrifie si la chane a une longueur. # # Utilise l'argument de la ligne de commande. VAR="$1" # if [ "$VAR" ] then echo contient du texte else echo est vide fi # if [ -z "$VAR" ] then echo est vide else echo contient du texte fi

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

124

Chapitre 6 Logique et arithmtique

Lexpression a une longueur est dlibre. Deux types de variables peuvent ne pas avoir de longueur : celles auxquelles une chane vide a t affecte et celles qui nont pas reu de valeur. Ce test ne distingue pas ces deux cas. Il vrifie simplement que la variable contient des caractres. Les guillemets autour de lexpression "$VAR" sont importants car ils permettent dviter que la syntaxe soit perturbe par lentre de lutilisateur. Si la valeur de $VAR est x -a 7 -lt 5 et si les guillemets ntaient pas utiliss, lexpression :
if [ -z $VAR ]

deviendrait alors (aprs la substitution de variable) :


if [ -z x -a 7 -lt 5 ]

Elle est tout fait valide, mais elle ne produit pas le rsultat attendu (vous ne savez pas si la chane contient ou non des caractres).

Voir aussi
la recette 6.7, Tester avec des correspondances de motifs, page 126 ; la recette 6.8, Tester avec des expressions rgulires, page 127 ; la recette 14.2, viter lusurpation de linterprteur, page 294 ; la section Oprateurs de test, page 536.

6.6. Tester lgalit


Problme
Vous souhaitez vrifier si deux variables du shell sont gales, mais il existe deux oprateurs de tests diffrents : -eq et = (ou ==). Lequel devez-vous choisir ?

Solution
Le type de la comparaison dtermine loprateur utiliser. Les comparaisons numriques se font avec loprateur -eq et les comparaisons de chanes avec = (ou ==).

Discussion
Voici un script simple qui illustre ce cas :
#!/usr/bin/env bash # bash Le livre de recettes : chaine_ou_nombre # # Le bon vieux dilemme de la comparaison des chanes # et des nombres. # VAR1=" 05 " VAR2="5"

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.6. Tester lgalit


printf "%s" "sont-elles gales (avec -eq) ? " if [ "$VAR1" -eq "$VAR2" ] then echo OUI else echo NON fi printf "%s" "sont-elles gales (avec =) ? " if [ "$VAR1" = "$VAR2" ] then echo OUI else echo NON fi

125

Lexcution du script produit la sortie suivante :


$ bash chaine_ou_nombre sont-elles gales (avec -eq) ? OUI sont-elles gales (avec =) ? NON $

Alors que la valeur numrique des deux variables est la mme (5), des caractres, comme les zros de tte ou les espaces, peuvent faire que les chanes littrales sont diffrentes. Les deux oprateurs = et == sont accepts, mais le premier est conforme au standard Postfix et il est plus portable. Pour vous aider dterminer la comparaison adapte, vous pouvez imaginer que loprateur -eq est similaire loprateur .eq. du langage FORTRAN. (FORTRAN est trs orient calcul scientifique.) En ralit, il existe plusieurs oprateurs de comparaison numrique, chacun est similaire un ancien oprateur de FORTRAN. Les abrviations, donnes au tableau 6-3, sont suffisamment mnmoniques pour tre comprises (en anglais). Tableau 6-3. Les oprateurs de comparaison de bash
Nombre
-lt -le -gt -ge -eq -ne

Chane
< <= > >=

Signification Infrieur . Infrieur ou gal . Suprieur . Suprieur ou gal . gal . Diffrent de.

=, ==
!=

Sachez quen Perl, ces oprateurs sont employs de manire oppose. Autrement dit, eq, ne, etc. sont des oprateurs de comparaison de chanes, tandis que ==, !=, etc. sappliquent aux nombres.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

126

Chapitre 6 Logique et arithmtique

Voir aussi
la recette 6.7, Tester avec des correspondances de motifs, page 126 ; la recette 6.8, Tester avec des expressions rgulires, page 127 ; la recette 14.12, Valider lentre, page 308 ; la section Oprateurs de test, page 536.

6.7. Tester avec des correspondances de motifs


Problme
Vous souhaitez tester une chane, non par rapport une constante littrale, mais par rapport un motif. Par exemple, vous voulez savoir si le nom dun fichier correspond un fichier JPEG.

Solution
Utilisez les doubles crochets dans une instruction if que les correspondances de motifs du shell soient acceptes droite de loprateur gal :
if [[ "${NOM_FICHIER}" == *.jpg ]]

Discussion
Les doubles crochets sont une syntaxe rcente (bash version 2.01). Il ne sagit pas de lancienne version [ de la commande test, mais dun nouveau mcanisme de bash. Il utilise les mmes oprateurs que le crochet simple, mais, dans ce cas, le signe gal est un comparateur de chane plus puissant. Cet oprateur peut tre constitu dun seul signe gal ou dun double signe gal, comme nous lavons utilis dans lexemple prcdent. Leur smantique est la mme. Nous prfrons employer le double signe gal (en particulier avec la correspondance de motifs) pour souligner la diffrence, mais la correspondance des motifs est apporte par les doubles crochets, non par le signe gal. Dans la correspondance de motif classique, le caractre * correspond un nombre quelconque de caractres, le point dinterrogation (?) un seul caractre et les crochets indiquent la liste des caractres valides. Vous noterez quils ressemblent aux caractres gnriques du shell et quils ne sont pas des expressions rgulires. Ne placez pas le motif entre guillemets. Si la chane de notre exemple avait t entoure de guillemets, la correspondance naurait trouv que les chanes dont le premier caractre est un astrisque. Grce dautres options de bash, vous pouvez bnficier de possibilits de correspondances de motifs plus labores. tendons notre exemple afin de rechercher les noms de fichiers qui se terminent par .jpg ou .jpeg :
shopt -s extglob if [[ "$NN" == *.@(jpg|jpeg) ]] then # Traitement...

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.8. Tester avec des expressions rgulires

127

La commande shopt -s permet dactiver des options du shell. Loption extglob concerne la prise en charge de la correspondance de motifs tendue (ou globalisation). Dans ce mode, nous pouvons dfinir plusieurs motifs, spars par le caractre | et regroups dans des parenthses. Le premier caractre qui prcde les parenthses fixe le type de correspondances avec les motifs. Dans notre exemple, le caractre @ stipule que la correspondance ne doit se faire quavec une seule occurence dun des motifs de la liste. Le tableau 6-4 rsume les diffrentes possibilits (voir aussi la section Oprateurs pour la correspondance de motifs tendue extglob, page 547). Tableau 6-4. Symboles de regroupement pour la correspondance de motif tendue
Regroupement
@( ... ) *( ... ) +( ... ) ?( ... ) !( ... )

Signification Une seule occurrence. Aucune ou plusieurs occurrences. Une ou plusieurs occurrences. Aucune ou une occurrence. Pas ces occurrences, mais tout le reste.

Les correspondances sont sensibles la casse, mais la commande shopt -s nocasematch (dans bash versions 3.1+) permet de modifier ce fonctionnement. Cette option affecte les commandes case et [[.

Voir aussi
la recette 14.2, viter lusurpation de linterprteur, page 294 ; la recette 16.7, Adapter le comportement et lenvironnement du shell, page 386 ; la section Options de shopt, page 517 ; la section Caractres pour la correspondance de motifs, page 546 ; la section Oprateurs pour la correspondance de motifs tendue extglob, page 547.

6.8. Tester avec des expressions rgulires


Problme
Parfois, mme la correspondance de motifs tendue active par loption extglob ne suffit pas. Il faut des expressions rgulires. Supposons que vous rcupriez le contenu dun CD de musique classique dans un rpertoire et que la commande ls affiche les noms suivants :
$ ls Ludwig Ludwig Ludwig Ludwig Ludwig $ Van Van Van Van Van Beethoven Beethoven Beethoven Beethoven Beethoven 01 02 03 04 05 Allegro.ogg Adagio un poco mosso.ogg Rondo - Allegro.ogg "Coriolan" Overture, Op. 62.ogg "Leonore" Overture, No. 2 Op. 72.ogg

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

128

Chapitre 6 Logique et arithmtique

Vous souhaitez crire un script qui donne un nom plus simple ces fichiers, par exemple uniquement le numro du titre.

Solution
Utilisez la correspondance dexpression rgulire avec loprateur =~. Lorsquune chane correspond, les diffrentes parties du motif sont disponibles dans la variable $BASH_REMATCH. Voici la partie du script qui concerne la correspondance de motif :
#!/usr/bin/env bash # bash Le livre de recettes : rechercher_titre # for PISTECD in * do if [[ "$PISTECD" =~ "([[:alpha:][:blank:]]*)- ([[:digit:]]*) - (.*)$" ]] then echo La piste ${BASH_REMATCH[2]} est le fichier ${BASH_REMATCH[3]} mv "$PISTECD" "Piste${BASH_REMATCH[2]}" fi done
Ce script ncessite bash version 3.0 ou ultrieure car les versions plus anciennes ne disposent pas de loprateur =~. Par ailleurs, bash version 3.2 a unifi la gestion des motifs dans les oprateurs de commande conditionnelle == et =~, mais a introduit un bogue subtil li aux guillemets. Il a t corrig dans la version 3.2 patch #3. Si la solution donne prcdemment ne fonctionne pas, vous utilisez sans doute bash version 3.2 sans le correctif. Vous pouvez passer une version plus rcente ou contourner le bogue en utilisant une version moins lisible de lexpression rgulire. Elle consiste supprimer les guillemets autour de lexpression rgulire et chapper toutes les parenthses et tous les caractres espace :
if [[ "$PISTECD" =~ \([[:alpha:][:blank:]]*\)\ \([[:digit:]]*\)\ -\ \(.*\)\$ ]]

Discussion
Si vous avez lhabitude des expressions rgulires de sed, awk ou des anciens shells, vous aurez certainement remarqu quelques lgres diffrences avec celles-ci. Les plus les videntes sont les classes de caractres, comme [:alpha:], et labsence dchappement sur les parenthses de regroupement ; vous ncrivez pas \(, comme ce serait le cas dans sed. Dans cette version des expressions rgulires, \( reprsente une parenthse littrale. Les sous-expressions, chacune incluse entre des parenthses, servent remplir la variable tableau de bash, $BASH_REMATCH. Llment dindice zro, $BASH_REMATCH[0], contient lintgralit de la chane qui correspond lexpression rgulire. Les sousexpressions sont disponibles dans $BASH_REMATCH[1], $BASH_REMATCH[2], etc. Chaque fois quune expression rgulire est employe de cette manire, elle remplit la variable $BASH_REMATCH. Puisque dautres fonctions bash peuvent galement utiliser une correspondance dexpression rgulire, vous devez recopier cette variable le plus tt possible

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.8. Tester avec des expressions rgulires

129

afin de conserver ses valeurs pour une utilisation ultrieure. Dans notre exemple, puisque nous exploitons immdiatement les valeurs, dans la clause if/then, nous navons pas besoin de les sauvegarder. Les expressions rgulires ont souvent t dcrites comme des expressions en criture seule, car elles sont trs difficiles dchiffrer. Nous allons construire pas pas celle de notre exemple afin de vous montrer comment nous lavons obtenu. Dans notre exemple, les noms de fichiers ont le format gnral suivant :
Ludwig Van Beethoven - 04 - "Coriolan" Overture, Op. 62.ogg

Autrement dit, ils sont constitus, dans lordre, du nom du compositeur, du numro de piste, du titre du morceau et de lextension .ogg (le CD a t converti au format Ogg Vorbis, afin dobtenir des fichiers de petite taille, mais de haute fidlit). Lexpression commence, gauche, par une parenthse ouvrante (gauche). Il sagit du dbut dune premire sous-expression. Nous y plaons une expression qui correspond la premire partie du nom de fichier, cest--dire le nom du compositeur (lexpression est signale en gras) :
([[:alpha:][:blank:]]*)- ([[:digit:]]*) - (.*)$

Le nom du compositeur est constitu dun nombre quelconque de caractres alphabtiques et despaces. Les crochets servent regrouper les caractres qui composent le nom. Au lieu dcrire [A-Za-z0-9], nous utilisons les classes de caractres [:alpha:] et [:blank:], en les plaant lintrieur des crochets. Nous ajoutons ensuite un astrisque pour indiquer 0 ou plusieurs rptitions. La parenthse droite ferme la premire sous-expression, qui est suivie dun tiret et dune espace. La deuxime sous-expression (signale en gras) correspond au numro de piste :
([[:alpha:][:blank:]]*)- ([[:digit:]]*) - (.*)$

Elle commence par une autre parenthse gauche. Les numros de pistes sont des entiers, constitus de chiffres (la classe de caractres [:digit:]). Nous indiquons ce format lintrieur dune autre paire de crochets, suivie dun astrisque pour indiquer zro ou plusieurs occurrences du contenu entre les crochets (cest--dire des chiffres). Notre motif est ensuite compos dune espace, dun tiret et dune espace. La dernire sous-expression collecte tous les caractres restants dans le nom du fichier, y compris le nom du morceau et lextension de fichier :
([[:alpha:][:blank:]]*)- ([[:digit:]]*) - (.*)$

Cette troisime sous-expression (.*) est un grand classique des expressions rgulires. Elle signifie nimporte quel nombre (*) de tout caractre (.). Nous terminons lexpression par un symbole dollar, qui correspond la fin de la chane. Les correspondances sont sensibles la casse, mais la commande shopt -s nocasematch (disponible dans bash versions 3.1+) permet de changer ce fonctionnement. Cette option affecte les commandes case et [[.

Voir aussi
man regex (Linux, Solaris, HP-UX, Mac) ou man re_format (BSD) pour tous les dtails concernant votre bibliothque dexpressions rgulires ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

130

Chapitre 6 Logique et arithmtique

Matrise des expressions rgulires, 2e dition de Jeffrey E. F. Friedl (ditions OReilly) ; la recette 7.7, Utiliser des motifs plus complexes dans la recherche, page 157 ; la recette 7.8, Rechercher un numro de scu, page 158 ; la recette 19.15, Confondre caractres gnriques du shell et expressions rgulires, page 503.

6.9. Modifier le comportement avec des redirections


Problme
Normalement, un script doit se comporter de la mme manire, que lentre provienne du clavier ou dun fichier, ou que la sortie aille vers lcran ou un fichier. Cependant, il arrive parfois que son fonctionnement doive tre diffrent selon ces critres de redirection. Comment pouvez-vous alors procder ?

Solution
Utilisez loption test -t dans une instruction if pour excuter du code diffrent en fonctions des deux comportements souhaits.

Discussion
Rf lchissez deux fois avant demprunter cette voie. Une grande partie de la puissance et de la souplesse des scripts bash vient du fait quils peuvent tre connects par des tubes. Vous devez avoir une trs bonne raison dimplmenter un comportement diffrent lorsque lentre ou la sortie est dirige.

Voir aussi
la recette 2.18, Placer plusieurs redirections sur la mme ligne, page 50 ; la recette 2.19, Enregistrer la sortie lorsque la redirection semble inoprante, page 51 ; la recette 2.20, Permuter STDERR et STDOUT, page 53 ; la recette 10.1, Convertir un script en dmon, page 207 ; la recette 15.9, Utiliser la redirection du rseau de bash, page 348 ; la recette 15.12, Rediriger la sortie pour toute la dure dun script, page 356 ; la section Redirection des entres/sorties, page 537.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.10. Boucler avec while

131

6.10. Boucler avec while


Problme
Vous souhaitez que votre script effectue de manire rpte certaines actions, tant quune certaine condition est satisfaite.

Solution
Utilisez une boucle while pour des conditions arithmtiques :
while (( COMPTEUR < MAX )) do raliser les actions let COMPTEUR++ done

Pour des conditions lies au sywtme de fichiers :


while [ -z "$VERROU_FICHIER" ] do raliser les actions done

Pour lire lentre :


while read ligneDeTexte do traiter $ligneDeTexte done

Discussion
Les doubles parenthses dans la premire instruction while reprsentent des expressions arithmtiques, de manire trs similaire aux expressions $(( )) pour laffectation dune variable du shell. Elles dlimitent une expression arithmtique et supposent que les noms de variables mentionnes doivent tre drfrencs. Autrement dit, vous ne devez pas crire $VAR mais VAR lintrieur des parenthses. Les crochets dans while [ -z "$FICHIER_VERROU" ] ont la mme utilisation que dans linstruction if ; le crochet unique quivaut linstruction test. Le dernier exemple, while read ligneDeTexte, nutilise aucune parenthse, crochet ou accolade. Dans bash, la syntaxe de linstruction while est dfinie de manire ce que la condition soit une liste de commandes excuter (comme pour linstruction if) et ltat de sortie de la dernire commande dtermine si la condition est vraie (0) ou fausse (diffrent de 0). Linstruction read retourne 0 lorsque la lecture russit et -1 en fin de fichier. Autrement dit, la condition de while est vraie lorsque la lecture se passe bien, mais, lorsque la fin du fichier est atteinte (-1 est retourn), elle devient fausse et la boucle se termine. ce stade, linstruction excute est celle qui vient aprs done.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

132

Chapitre 6 Logique et arithmtique

Vous pourriez penser que la logique continuer boucler tout pendant que linstruction retourne zro est quelque peu inverse. La plupart des langages de type C emploient la logique oppose, cest--dire boucler tant que la valeur est diffrente de zro . Mais, dans le shell, la valeur de retour zro signifie que tout sest bien pass, contrairement une valeur diffrente de zro qui indique une sortie en erreur. Cela explique galement le fonctionnement de la construction (( )). Toute expression lintrieur des parenthses est value et, si le rsultat est diffrent de zro, alors ltat de sortie de (( )) est zro ; inversement, un rsultat gal zro retourne un. Nous pouvons donc crire des expressions comme le feraient les programmeurs Java ou C, mais linstruction while respecte la logique de bash et sattend ce que zro signifie vrai. Dun point de vue pratique, cela signifie que nous pouvons crire une boucle infinie de la manire suivante :
while (( 1 )) { ... }

Elle convient parfaitement au programmeur C. Mais, noubliez pas que linstruction while attend une valeur de retour gale zro, ce qui est le cas car (( )) retourne 0 pour un rsultat vrai (cest--dire diffrent de zro). Avant de quitter cette forme de boucle, revenons sur lexemple while read, qui lit depuis lentre standard (le clavier), et voyons comment le modifier pour lire lentre depuis un fichier. Il existe trois manires de procder. La premire ne demande aucune modification de linstruction. Elle consiste rediriger lentre standard vers un fichier au moment de linvocation du script :
$ monScript <nom.fichier

Mais, supposons que vous ne vouliez pas laisser cette possibilit lappelant. Si vous savez quel fichier doit tre manipul ou sil a t fourni en argument votre script, vous pouvez alors employer la mme boucle while, mais en redirigeant lentre vers le fichier :
while read ligneDeTexte do traiter la ligne done < fichier.entree

Enfin, vous pouvez utiliser la commande cat pour envoyer le fichier sur la sortie standard et rediriger celle-ci vers lentre standard de linstruction while :
cat fichier.entree | \ while read ligneDeTexte do traiter la ligne done

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.11. Boucler avec read

133

cause du tube, la commande cat et la boucle while (y compris la partie traiter la ligne) sexcutent toutes deux dans des sous-shells distincts. Autrement dit, avec cette mthode, les commandes du script dans la boucle while ne peuvent affecter les autres parties du script qui se trouvent hors de la boucle. Par exemple, les variables dfinies lintrieur de la boucle while nauront plus forcment ces valeurs une fois la boucle termine. Ce nest pas le cas avec la solution while read ... done < fichier.entree, car il ne sagit pas dun tube.

Dans le dernier exemple, il ny a aucun caractre aprs la barre oblique inverse, juste un saut de ligne. Par consquent, elle chappe le saut de ligne, indiquant au shell de continuer sur la ligne suivante sans terminer la ligne en cours. Il est ainsi plus facile de distinguer les deux actions (la commande cat et linstruction while).

Voir aussi
la recette 6.2, Conditionner lexcution du code, page 116 ; la recette 6.3, Tester les caractristiques des fichiers, page 119 ; la recette 6.4, Tester plusieurs caractristiques, page 122 ; la recette 6.5, Tester les caractristiques des chanes, page 123 ; la recette 6.6, Tester lgalit, page 124 ; la recette 6.7, Tester avec des correspondances de motifs, page 126 ; la recette 6.8, Tester avec des expressions rgulires, page 127 ; la recette 6.11, Boucler avec read, page 133 ; la recette 19.8, Oublier que les tubes crent des sous-shells, page 493.

6.11. Boucler avec read


Problme
quoi peut donc servir une boucle while ? Trs souvent, elle sert lire la sortie des commandes prcdentes. Supposons que vous utilisiez le systme de gestion des versions Subversion, dont le programme excutable sappelle svn. (Un exemple avec cvs serait trs similaire.) Lorsque vous examinez une sous-arborescence pour savoir si des fichiers ont t modifis, vous pouvez obtenir une sortie similaire la suivante :
$ svn status bcb M bcb/amin.c ? bcb/dmin.c ? bcb/mdiv.tmp A bcb/optrn.c M bcb/optson.c ? bcb/prtbout.4161 ? bcb/rideaslist.odt ? bcb/x.maxc $

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

134

Chapitre 6 Logique et arithmtique

Les lignes qui commencent par un point dinterrogation dsignent des fichiers inconnus de Subversion ; il sagit en loccurrence de fichiers de travail et de copies temporaires de fichiers. Les lignes qui dbutent par A correspondent des fichiers nouvellement ajouts. Celles qui commencent par M indiquent des fichiers modifis depuis la dernire validation. Nous voulons nettoyer le rpertoire en supprimant tous les fichiers de travail, cest-dire ceux qui se trouvent sur les lignes commenant par un point dinterrogation.

Solution
Essayez :
svn status mesSources | grep '^?' | cut -c8- | \ while read NF; do echo "$NF"; rm -rf "$NF"; done

Ou :
svn status mesSources | \ while read BALISE NF do if [[ $BALISE == \? ]] then echo $NF rm -rf "$NF" fi done

Discussion
Les deux scripts permettent de supprimer les fichiers marqus dun point dinterrogation par svn. La premire approche sappuie sur diffrents sous-programmes (ce nest pas vraiment un problme aujourdhui avec la rapidit des processeurs) et tient normalement sur une seule ligne dans une fentre de terminal classique. Elle utilise grep pour slectionner uniquement les lignes qui commencent (grce ^) par un point dinterrogation. Lexpression '^?' est place entre apostrophes afin dannuler les significations particulires de ces caractres sous bash. Ensuite, la commande cut retient uniquement les caractres partir de la colonne huit et jusqu la fin de la ligne. Nous obtenons ainsi les noms de fichiers que la boucle while doit traiter. Linstruction read retourne une valeur diffrente de zro lorsque lentre est vide ; la boucle se termine alors. Tant que lentre contient des donnes, read affecte la ligne de texte lue la variable "$NF", qui correspond au fichier supprimer. Nous ajoutons les options -rf pour le cas o le fichier inconnu serait en ralit un rpertoire de fichiers et pour supprimer galement les fichiers en lecture seule. Si vous ne voulez pas une suppression aussi radicale, retirez ces options. Le deuxime script est plus orient shell, car il na pas besoin de grep pour sa recherche, qui sappuie sur linstruction if, ni de cut pour lanalyse, qui se fait avec une instruction read. Nous lavons mis en forme comme devrait ltre un script dans un fichier. Si vous le saisissez linvite de commande, vous pouvez retirer lindentation, mais nous prfrons une meilleure lisibilit lconomie de saisie.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.12. Boucler avec un compteur

135

Linstruction read dans ce deuxime script lit dans deux variables et non une seule. Cest ainsi que nous demandons bash danalyser la ligne en deux lments (le caractre de dbut et le nom de fichier). read convertit son entre en deux mots, comme des mots sur la ligne de commande du shell. Le premier est affect la premire variable de la liste donne dans linstruction read, le deuxime est affect la deuxime variable, etc. La dernire variable de la liste reoit la fin de la ligne, mme si elle contient plusieurs mots. Dans notre exemple, la valeur de $BALISE correspond au premier mot, qui est le caractre (M, A ou ?) dont la fin est indique par lespace. Celle-ci dsigne galement le dbut du mot suivant. La variable $NF prend le reste de la ligne, ce qui est important ici car les noms de fichiers peuvent contenir des espaces ; nous ne voulons pas uniquement le premier mot du nom de fichier. Le script supprime le fichier et la boucle reprend.

Voir aussi
lannexe D, Gestion de versions, page 575.

6.12. Boucler avec un compteur


Problme
Vous souhaitez excuter une boucle un nombre de fois dtermin. Vous pouvez employer une boucle while avec un dcomptage, mais les langages de programmation offrent les boucles for pour un tel idiome. Comment pouvez-vous le raliser avec bash ?

Solution
Utilisez un cas particulier de la syntaxe for, qui ressemble beaucoup au langage C, mais avec des doubles parenthses :
$ for (( i=0 ; i < 10 ; i++ )) ; do echo $i ; done

Discussion
Dans les versions prcdentes du shell, la syntaxe de la boucle for ne permettait ditrer que sur une liste fige dlments. Il sagissait dune grande innovation pour les langages orients mot comme ceux des scripts shell, qui manipulent des noms de fichiers et autres donnes analogues. Mais, lorsque les utilisateurs avaient besoin de compter, ils crivaient parfois du code similaire au suivant :
for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done

Si cela peut convenir aux petites boucles, il est assez difficile deffectuer 500 itrations. (Imbriquer 5 10 boucles nest pas vraiment une solution !) En ralit, il faut une boucle for capable de compter.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

136

Chapitre 6 Logique et arithmtique

Cette boucle for spciale, avec une syntaxe de type C, est un ajout relativement rcent bash (apparue dans la version 2.04). Voici sa forme la plus gnrale :
for (( expr1 ; expr2 ; expr3 )) ; do list ; done

Les doubles parenthses indiquent que les expressions sont arithmtiques. Vous navez pas besoin dutiliser le symbole $ (comme dans $i, except pour les arguments comme $1) dans les rfrences aux variables places lintrieur des doubles parenthses. Les expressions sont des expressions arithmtiques entires qui offrent une grande varit doprateurs, notamment la virgule pour inclure plusieurs oprations dans une mme expression :
for (( i=0, j=0 ; i+j < 10 ; i++, j++ )) do echo $((i*j)) done

Cette boucle for initialise deux variables (i et j) et comporte une deuxime expression plus complexe qui additionne les valeurs de ces variables avant de comparer le rsultat une constante. Loprateur virgule est nouveau employ dans la troisime expression pour incrmenter les deux variables.

Voir aussi
la recette 6.13, Boucler avec des valeurs en virgule flottante, page 136 ; la recette 17.22, crire des squences, page 469.

6.13. Boucler avec des valeurs en virgule flottante


Problme
Les expressions arithmtiques dune boucle for ne manipulent que des entiers. Comment utiliser des valeurs en virgule f lottante ?

Solution
Utilisez la commande seq pour gnrer la valeur en virgule f lottante (si elle existe sur votre systme) :
for vf in $(seq 1.0 .01 1.1) do echo $vf; et autres actions done

Ou :
seq 1.0 .01 1.1 | \ while read vf do echo $vf; et autres actions done

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.14. Raliser des branchements multiples

137

Discussion
La commande seq gnre une suite de nombres en virgule f lottante, un sur chaque ligne. Ses arguments correspondent la valeur de dpart, la valeur dincrment et la valeur de fin. Si vous tes habitu aux boucles for du langage C ou si vous vous tes initi aux boucles avec le langage BASIC (par exemple, FOR I=4 TO 10 STEP 2), cet ordre des arguments pourrait vous tonner. Avec la commande seq, lincrment se trouve au centre. Dans le premier exemple, la construction $( ) excute la commande dans un sous-shell et retourne le rsultat avec les sauts de ligne remplacs par une espace. Chaque valeur est ainsi une chane pour la boucle for. Dans le deuxime exemple, seq est excute comme une commande dont la sortie est envoye par un tube dans une boucle while qui lit chaque ligne et effectue des actions. Lorsque la suite des nombres est trs longue, vous devez opter pour cette approche car la commande seq peut sexcuter en parallle de linstruction while. Dans la version base sur une boucle for, seq doit sexcuter entirement et placer lintgralit de sa sortie sur la ligne de commande pour la passer linstruction for. Lorsque la suite de nombres est trs longue, cette approche peut demander beaucoup de temps et de mmoire.

Voir aussi
la recette 6.12, Boucler avec un compteur, page 135 ; la recette 17.22, crire des squences, page 469.

6.14. Raliser des branchements multiples


Problme
Vous devez effectuer tout un ensemble de comparaisons, mais la construction if/then/else savre plutt longue et rptitive. Existe-t-il une meilleure solution ?

Solution
Utilisez linstruction case qui permet de dfinir plusieurs branchements :
case $NF in *.gif) gif2png $NF ;; *.png) pngOK $NF ;; *.jpg) jpg2gif $NF ;; *.tif | *.TIFF) tif2jpg $NF ;; *) printf "Fichier non pris en charge : %s" $NF ;; esac

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

138
Voici linstruction if/then/else quivalente :

Chapitre 6 Logique et arithmtique

if [[ $NF == *.gif ]] then gif2png $NF elif [[ $NF == *.png ]] then pngOK $NF elif [[ $NF == *.jpg ]] then jpg2gif $NF elif [[ $NF == *.tif || $NF == *.TIFF ]] then tif2jpg $NF else printf "Fichier non pris en charge : %s" $NF fi

Discussion
Linstruction case dveloppe le mot (avec substitution des paramtres) plac entre les mots-cls case et in. Elle tente dtablir une correspondance entre ce mot et les motifs indiqus. Cette fonctionnalit du shell est trs puissante. Elle neffectue pas simplement une comparaison de valeurs, mais des correspondances de motifs. Notre exemple prsente des motifs simples : *.gif correspond toute suite de caractres (comme indiqu par *) qui se termine par les caractres littraux .gif. La barre verticale |, qui reprsente un OU logique, permet de sparer diffrents motifs qui doivent mener la mme action. Dans notre exemple, si $NF se termine par .tif ou par .TIFF, la correspondance de motifs existe alors et la commande tif2jpg (fictive) est excute. Les doubles points-virgules terminent lensemble dinstructions excutes en cas de correspondance. Il nexiste aucun mot-cl else ou default pour prciser les instructions excuter lorsquaucun motif ne correspond. la place, utilisez * comme dernier motif, puisquil correspond nimporte quelle chane. En le plaant la fin, il joue le rle de clause par dfaut et correspond tout ce qui na pas encore trouv de correspondance. Les programmeurs C/C++ et Java auront remarqu que linstruction case de bash est similaire linstruction switch de leur langage et que chaque motif correspond un cas. Cependant, il est important de noter que la variable de case est une variable du shell (en gnral une chane) et que les cas sont des motifs (non des valeurs constantes). Les motifs se terminent par une parenthse droite ( la place des deux-points). Lquivalent au break des instructions switch de C/C++ et Java est, en bash, un double point-virgule. Lquivalent du mot-cl default est, en bash, le motif *. Les correspondances sont sensibles la casse, mais vous pouvez changer ce fonctionnement laide de la commande shopt -s nocasematch (disponible dans bash versions 3.1+). Cette option affecte les commandes case et [[.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.15. Analyser les arguments de la ligne de commande

139

Linstruction case se termine par esac (cest--dire case pel lenvers ; endcase tait sans doute trop long, un peu comme elif, la place de elseif , qui est plus concis).

Voir aussi
help case ; help shopt ; la recette 6.2, Conditionner lexcution du code, page 116.

6.15. Analyser les arguments de la ligne de commande


Problme
Vous souhaitez crire un script shell simple pour afficher une ligne de tirets. Mais, vous voulez que des paramtres prcisent la longueur de la ligne et le caractre employer la place du tiret, si ncessaire. La syntaxe doit tre la suivante :
tirets tirets 50 tirets -c= 50 tirets -cx # # # # Affiche Affiche Affiche Affiche 72 50 50 72 tirets. tirets. signes gal. caractres x.

Quel est la faon la plus simple danalyser ces arguments ?

Solution
Pour des scripts professionnels, vous devez utiliser la commande interne getopts. Mais, nous souhaitons vous montrer linstruction case en action. Par consquent, dans ce cas simple, lanalyse des arguments se fera avec case. Voici le dbut du script (voir la recette 12.1, page 239, pour une version complte) :
#!/usr/bin/env bash # bash Le livre de recettes : tirets # # tirets - affiche une ligne de tirets # # options : # longueur de la ligne (72 par dfaut) # -c X utiliser le caractre X la place du tiret # LONGUEUR=72 CARACTERE='-' while (( $# > 0 )) do case $1 in

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

140

Chapitre 6 Logique et arithmtique

[0-9]*) LONGUEUR=$1 ;; -c) shift; CARACTERE=${1:--} ;; *) printf 'usage : %s [-c X] [#]\n' $(basename $0) >&2 exit 2 ;; esac shift done # # suite...

Discussion
La longueur (72) et le caractre (-) par dfaut sont fixs au dbut du script (aprs quelques commentaires utiles). La boucle while nous permet danalyser plusieurs paramtres. Elle se poursuit tant que le nombre darguments ($#) est suprieur zro. Linstruction case recherche les correspondances avec trois motifs diffrents. Premirement, [0-9]* correspond un chiffre suivi de nimporte quel caractre. Nous aurions pu utiliser une expression plus labore pour naccepter que des nombres, mais nous supposerons que tout argument qui commence par un chiffre est un nombre. Si ce nest pas le cas, par exemple si lutilisateur a saisi 1T4, le script affiche une erreur lorsquil tente demployer la variable $LONGUEUR. Pour le moment, nous ferons avec. Le deuxime motif est la chane littrale -c, qui demande une correspondance exacte. Lorsquelle est trouve, nous utilisons la commande interne shift pour carter cet argument, nous prenons ensuite le suivant (qui est prsent le premier argument, cest-dire $1) et nous enregistrons le nouveau choix de caractre. La rfrence $1 utilise loprateur :- (cest--dire, ${1:-x}) pour donner une valeur par dfaut si le paramtre nest pas fix. De cette manire, si lutilisateur saisit -c mais oublie de prciser le caractre, la valeur par dfaut, indique juste aprs loprateur :-, est choisie. Dans lexpression ${1:-x}, cette valeur par dfaut est x. Dans notre script, nous avons crit ${1:--} (remarquez les deux signes moins) et le caractre par dfaut est donc le (deuxime) signe moins. Le troisime motif (*) correspond toute chane de caractres. Par consquent, les arguments qui nont pas trouv de correspondance avec les motifs prcdents seront traits par ce cas. En le plaant la fin de linstruction case, il permet de prendre en charge tous les cas invalides et dafficher un message derreur lutilisateur (puisque les paramtres ne sont pas corrects). Si vous dbutez avec bash, le message derreur affich par printf mrite sans doute quelques explications. Vous devez examiner quatre parties de cette instruction. La premire est simplement constitue du nom de la commande, printf. La deuxime reprsente la chane de format employe par printf (voir la recette 2.3, page 34, et la section printf, page 540). Nous plaons la chane entre apostrophes afin que le shell ne tente pas de linterprter. La dernire partie de la ligne (>&2) demande au shell de rediriger la sortie vers lerreur standard (puisquil sagit dun message derreur). Les dveloppeurs de

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.15. Analyser les arguments de la ligne de commande

141

scripts ne prtent pas toujours attention ce point et omettent souvent cette redirection des messages derreur. Nous pensons quil est prfrable de prendre lhabitude de toujours rediriger les messages derreur vers lerreur standard. La troisime partie de la ligne invoque un sous-shell pour excuter la commande basename avec $0 en argument et placer la sortie sous forme dun texte sur la ligne de commande. Cet idiome classique est souvent employ pour retirer le chemin qui se trouve au dbut de la commande invoque. Par exemple, examinons ce qui se passe si nous utilisions uniquement $0. Voici deux invocations du mme script. Examinez les messages derreur :
$ tirets -g usage : tirets [-c X] [#] $ /usr/local/bin/tirets -g usage : /usr/local/bin/tirets [-c X] [#]

La seconde invocation prcise le nom de chemin complet. Le message derreur contient donc galement ce nom de chemin complet. Pour viter dafficher cette information, nous extrayons de $0 uniquement le nom de base du script, laide de la commande basename. Les messages derreur sont alors les mmes, quelle que soit la manire dont le script est invoqu :
$ tirets -g usage : tirets [-c X] [#] $ /usr/local/bin/tirets -g usage : tirets [-c X] [#]

Mme si cela est un peu plus long que de figer le nom du script dans le code ou demployer directement $0, ce temps dexcution supplmentaire nest pas important car il sagit dun message derreur et le script va sarrter. Nous terminons linstruction case par esac et invoquons nouveau shift pour retirer largument qui vient dtre trait par linstruction case. Si nous ne procdions pas ainsi, la boucle while analyserait indfiniment le mme argument. Linstruction shift dplace le deuxime argument ($2) en premire position ($1), le troisime en deuxime position, etc. De plus, la variable $# est chaque fois dcrmente de un. Aprs quelques itrations, $# atteint finalement la valeur zro (lorsquil ny a plus darguments) et la boucle se termine. Laffichage des tirets (ou dun autre caractre) nest pas prsent dans cet exemple, car nous voulions nous concentrer sur linstruction case. Le script complet, avec une fonction daffichage du message dutilisation, est donn la recette 12.1, page 239.

Voir aussi
help case ; help getopts ; help getopt ; la recette 2.3, Mettre en forme la sortie, page 34 ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

142

Chapitre 6 Logique et arithmtique

la recette 5.8, Parcourir les arguments dun script, page 96 ; la recette 5.11, Compter les arguments, page 101 ; la recette 5.12, Extraire certains arguments, page 103 ; la recette 12.1, Afficher une ligne de tirets, page 239 ; la recette 13.1, Analyser les arguments dun script, page 257 ; la recette 13.2, Afficher ses propres messages derreur lors de lanalyse, page 260 ; la section printf, page 540.

6.16. Crer des menus simples


Problme
Vous avez un script SQL simple que vous aimeriez excuter pour diffrentes bases de donnes. Vous pourriez donner le nom de la base de donnes sur la ligne de commande, mais vous souhaitez un fonctionnement plus interactif, en choisissant la base de donnes dans une liste de noms.

Solution
Utilisez linstruction select pour crer des menus textuels simples. Voici un exemple :
#!/usr/bin/env bash # bash Le livre de recettes : init_bd.1 # LISTE_BD=$(sh ./listebd | tail +2) select BD in $LISTE_BD do echo Initialisation de la base de donnes : $BD mysql -uuser -p $BD <monInit.sql done

Pour le moment, ne vous proccupez pas de savoir comment $LISTE_BD obtient ses valeurs. Sachez simplement quil sagit dune liste de mots (similaire la sortie dune commande ls). Linstruction select affiche ces mots, chacun prcd dun numro, et une invite pour lutilisateur. Celui-ci fait son choix en saisissant le numro. Le mot correspondant est alors affect la variable prcise aprs le mot-cl select (dans ce cas BD). Voici un exemple dexcution de ce script :
$ ./init_bd 1) testBD 2) inventaireSimple 3) inventairePrincipal 4) autreBD #? 2 Initialisation de la base de donnes : inventaireSimple #? $

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.17. Modifier linvite des menus simples

143

Discussion
Lorsque lutilisateur tape 2 , le mot inventaireSimple est affect la variable BD. Si vous voulez obtenir le numro choisi par lutilisateur, vous le trouverez dans la variable $REPLY. Linstruction select est en ralit une boucle. Lorsque vous avez fait un choix, le corps de la boucle (entre do et done) est excut, puis vous revenez linvite afin dindiquer une nouvelle valeur. La liste nest pas raffiche chaque fois, mais uniquement si vous nindiquez aucun numro et appuyez simplement sur la touche Entre. Par consquent, si vous souhaitez revoir la liste, appuyez directement sur Entre. Le code plac aprs in nest pas rvalu. Autrement dit, la liste ne peut pas tre modifie une fois linstruction select excute. Si vous changez la valeur de $LISTE_BD lintrieur de la boucle, cela ne modifie en rien la liste des options du menu. La boucle sarrte lorsquelle atteint la fin du fichier, qui, dans une utilisation interactive, est reprsente par Ctrl-D. Si vous envoyez, via un tube, un ensemble de choix une boucle select, celle-ci se termine la fin de lentre. Vous navez pas la possibilit de mettre en forme la liste. Vous devez vous contenter de laffichage produit par linstruction select. En revanche, vous pouvez modifier linvite.

Voir aussi
la recette 3.7, Choisir dans une liste doptions, page 68 ; la recette 16.2, Personnaliser linvite, page 368 ; la recette 16.10, Utiliser les invites secondaires : $PS2, $PS3 et $PS4, page 390.

6.17. Modifier linvite des menus simples


Problme
Vous naimez pas linvite par dfaut des menus de select et vous souhaitez donc la changer.

Solution
La variable denvironnement $PS3 de bash contient linvite affiche par select. En modifiant cette variable, vous obtenez une nouvelle invite.

Discussion
Il sagit de la troisime invite de bash. La premire ($PS1) correspond celle affiche avant la plupart des commandes. (Nous avons utilis $ dans nos exemples, mais elle peut tre beaucoup plus labore et inclure, par exemple, un identifiant dutilisateur et des noms de rpertoires.) Si une commande est trs longue et se poursuit sur la ligne suivante, la deuxime invite ($PS2) est prsente.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

144

Chapitre 6 Logique et arithmtique

Les boucles select affichent la troisime invite ($PS3). Vous devez lui donner la valeur souhaite avant linstruction select. Vous pouvez mme la modifier lintrieur de la boucle pour la faire voluer en fonction du droulement du code. Voici un script, similaire celui de la recette prcdente, qui compte le nombre dentres valides traites :
#!/usr/bin/env bash # bash Le livre de recettes : init_bd.2 # LISTE_BD=$(sh ./listebd | tail +2) PS3="0 initialisations >" select BD in $LISTE_BD do if [ $BD ] then echo Initialisation de la base de donnes : $BD PS3="$((i++)) initialisations >" mysql -uuser -p $BD <monInit.sql fi done $

Nous avons ajout quelques espaces pour que le contenu de $PS3 soit plus clair. Linstruction if nous permet de comptabiliser uniquement les choix valides de lutilisateur. Cette vrification serait galement utile dans la version prcdente, mais nous lavions voulue simple.

Voir aussi
la recette 3.7, Choisir dans une liste doptions, page 68 ; la recette 6.17, Modifier linvite des menus simples, page 143 ; la recette 16.2, Personnaliser linvite, page 368 ; la recette 16.10, Utiliser les invites secondaires : $PS2, $PS3 et $PS4, page 390.

6.18. Crer une calculatrice NPI simple


Problme
Vous tes sans doute capable de convertir mentalement des valeurs binaires en dcimal, octal ou hexadcimal, mais vous ne parvenez plus effectuer des oprations arithmtiques simples et vous ne trouvez aucune calculatrice.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.18. Crer une calculatrice NPI simple

145

Solution
Crez votre propre calculatrice en utilisant larithmtique du shell et la notation polonaise inverse (NPI) :
#!/usr/bin/env bash # bash Le livre de recettes : calc_npi # # Calculatrice NPI simple (entire) en ligne de commande. # # Prend les arguments et effectue le calcul donn sous la forme # a b op. # Accepte le caractre x la place de *. # # Vrification du nombre d'arguments : if [ \( $# -lt 3 \) -o \( $(($# % 2)) -eq 0 \) ] then echo "usage : calc_npi nombre nombre oprateur [ nombre oprateur ] ..." echo "utiliser x ou '*' pour la multiplication" exit 1 fi RESULTAT=$(($1 ${3//x/*} $2)) shift 3 while [ $# -gt 0 ] do RESULTAT=$((RESULTAT ${2//x/*} $1)) shift 2 done echo $RESULTAT

Discussion
$(( )) effectue uniquement une arithmtique entire.

Lcriture NPI (ou postfixe) place les oprandes (les nombres) en premier, puis loprateur. Dans cette notation, nous ncrivons pas 5 + 4, mais 5 4 +. Si nous voulons multiplier le rsultat par 2, il suffit dajouter 2 * la fin. Lexpression globale est alors 5 4 + 2 *. Elle est parfaitement adapte un traitement informatique car le programme peut lire de gauche droite sans jamais avoir besoin de parenthses. Le rsultat de toute opration devient le premier oprande de lexpression suivante. Dans notre calculatrice bash simple, nous acceptons que le caractre x soit utilis la place du symbole de multiplication, car * a une signification spciale pour le shell. Mais, si vous lui appliquez lchappement, en crivant '*' ou \*, cela fonctionne galement. Quels sont les contrles de validit des arguments ? Nous considrons quil faut au moins trois arguments (deux oprandes et un oprateur, par exemple 6 3 /). Il est pos[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

146

Chapitre 6 Logique et arithmtique

sible davoir plus de trois arguments, mais, dans ce cas, leur nombre doit tre impair ; cest--dire au moins trois plus une ou plusieurs occurences de deux autres arguments (un deuxime oprande et loprateur suivant). Par consquent, le nombre darguments valides est 3 ou 5 ou 7 ou 9, etc. Nous vrifions ce point avec lexpression suivante, dont la valeur de retour doit tre gale 0 :
$(($# % 2)) -eq 0

La construction $(( )) indique une opration arithmtique. Loprateur % (appel reste) nous permet de savoir si le reste de la division de $# (le nombre darguments) par 2 est gal 0 (-eq 0). Une fois le nombre darguments valid, nous pouvons les employer dans le calcul du rsultat :
RESULTAT=$(($1 ${3//x/*} $2))

Cette expression calcule le rsultat et remplace en mme temps le caractre x par *. Lorsque vous invoquez le script, vous lui passez une expression NPI sur la ligne de commande, mais larithmtique effectue par le shell utilise une notation classique (infixe). Il est donc possible dvaluer lexpression lintrieur de $(( )), mais il faut rordonner les arguments. En ignorant la substitution x en *, pour le moment, nous avons :
RESULTAT=$(($1 $3 $2))

Loprateur est simplement dplac entre les deux oprandes. bash effectue la substitution des paramtres avant de procder aux calculs arithmtiques. Si $1 vaut 5, $2 vaut 4 et $3 est le signe +, nous obtenons alors lexpression suivante aprs la substitution les paramtres :
RESULTAT=$((5 + 4))

bash value lexpression et affecte la valeur obtenue, 9, RESULTAT. prsent que nous en avons termin avec ces trois arguments, linstruction shift 3 permet de les retirer et de laisser la place aux prochains. Puisque nous avons dj vrifi que le nombre darguments tait impair, nous savons quil en reste au moins deux (ou aucun). Sil nen restait quun, leur nombre serait pair (3+1=4). partir de l, nous entrons dans une boucle qui traite deux arguments la fois. Le rsultat prcdent devient le premier oprande, largument suivant ($1 suite au dcalage) constitue le deuxime oprande et loprateur, donn par $2, entre les deux oprandes. Lvaluation de lexpression se fait comme prcdemment. Lorsquil ny a plus darguments, le rsultat du calcul se trouve dans RESULTAT. Revenons la substitution. ${2} fait rfrence au deuxime argument. Cependant, nous omettons souvent {} pour crire simplement $2. Mais, dans ce cas, nous en avons besoin car nous demandons bash deffectuer dautres oprations sur largument. Lexpression ${2//x/*} utilise indique que nous voulons remplacer (//) un caractre x par (indiqu par le caractre / suivant) * avant de retourner la valeur de $2. Nous aurions pu crire cette opration en deux tapes en impliquant une autre variable :
OP=${2//x/*} RESULTAT=$((RESULTAT OP $1))

Cette variable supplmentaire pourrait vous tre utile dans vos premires utilisations de ces fonctionnalits de bash. Mais, une fois que ces expressions courantes vous seront

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

6.19. Crer une calculatrice en ligne de commande

147

devenues familires, vous les crirez automatiquement sur une seule ligne (bien quelles soient alors moins faciles lire). Vous vous demandez peut-tre pourquoi nous navons pas crit $RESULTAT et $OP dans lexpression qui ralise lvaluation. Nous navons pas besoin dutiliser le symbole $ avec les noms de variables placs lintrieur des expressions $(( )), lexception des paramtres positionnels (par exemple, $1, $2). En effet, ceux-ci doivent tre diffrencis des nombres littraux (par exemple, 1, 2).

Voir aussi
le chapitre 5, Variables du shell, page 85 ; la recette 6.19, Crer une calculatrice en ligne de commande, page 147.

6.19. Crer une calculatrice en ligne de commande


Problme
Larithmtique entire ne nous suffit plus et lcriture NPI ne vous a jamais vraiment passionn. Vous souhaitez donc une approche diffrente pour une calculatrice en ligne de commande.

Solution
Crez une calculatrice triviale qui utilise les expressions arithmtiques en virgule f lottante de la commande awk :
# bash Le livre de recettes : fonction_calculer # Calculatrice en ligne de commande triviale. function calculer { awk "BEGIN {print \"La rponse est : \" $* }"; }

Discussion
Vous pourriez tre tent dcrire echo La rponse est : $(( $* )), qui fonctionne parfaitement avec les entiers, mais qui tronquera le rsultat des oprations en virgule f lottante. Nous avons crit une fonction car les alias nautorisent pas lemploi des arguments. Vous la placerez probablement dans votre fichier global /etc/bashrc ou dans votre fichier local ~/.bashrc. Les oprateurs nont rien de mystrieux et sont les mmes quen C :
$ calc 2 + 3 + 4

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

148
La rponse est : 9 $ calc 2 + 3 + 4.5 La rponse est : 9.5

Chapitre 6 Logique et arithmtique

Faites attention aux caractres interprts par le shell. Par exemple :


$ calc (2+2-3)*4 -bash: syntax error near unexpected token `2+2-3'

Vous devez annuler la signification particulire des parenthses. Vous avez le choix entre placer lexpression entre apostrophes ou ajouter une barre oblique inverse devant chaque caractre spcial (pour le shell). Par exemple :
$ calc '(2+2-3)*4' La rponse est : 4 $ calc \(2+2-3\)\*4 La rponse est : 4 $ calc '(2+2-3)*4.5' La rponse est : 4.5

Le symbole de multiplication doit galement tre chapp, puisquil sagit dun caractre gnrique pour les noms de fichiers. Cest notamment le cas lorsque vous placez des espaces autour des oprateurs, comme dans 17 + 3 * 21, car * correspond alors tous les fichiers du rpertoire de travail. Cette liste de noms est insre sur la ligne de commande la place de lastrisque. Il est alors difficile dobtenir le rsultat attendu.

Voir aussi
man awk ; la section VALUATION ARITHMTIQUE de la page de manuel de bash ; la recette 6.18, Crer une calculatrice NPI simple, page 144 ; la recette 16.6, Raccourcir ou modifier des noms de commandes, page 385.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

7
Outils shell intermdiaires I
Il est temps prsent dtendre notre rpertoire. Les recettes de ce chapitre sappuient sur des utilitaires qui ne font pas partie du shell, mais leur intrt est indniable et il est difficile dimaginer le shell sans eux. La philosophie dUnix (et donc de Linux) est demployer de petits programmes ( la porte limite) et de les runir pour obtenir des rsultats plus grands. la place dun seul programme qui fait tout, nous avons de nombreux programmes diffrents qui ralisent chacun une seule tche. Cest galement lapproche de bash. Bien que ses possibilits soient de plus en plus nombreuses, il ne tente pas de tout accomplir. Il est parfois plus simple dutiliser des commandes externes pour raliser une tche, mme si bash pourrait y parvenir en faisant un effort. Prenons un exemple simple bas sur la commande ls. Vous navez pas besoin de cette commande pour obtenir le contenu de votre rpertoire courant. La commande echo * affiche tous les noms de fichiers. Vous pouvez mme tre plus fantaisiste, en utilisant la commande printf de bash et une mise en forme adapte. Mais ce nest pas rellement lobjectif dun interprteur de commandes et il existe dj un programme (ls) qui prend en charge les diverses informations du systme de fichiers. Mais le plus important est peut-tre quen ne demandant pas bash doffrir toutes les possibilits daffichage du contenu dun systme de fichiers, nous lui autorisons une certaine indpendance dvolution. De nouvelles versions de ls peuvent tre dveloppes sans que tous les utilisateurs mettent niveau leur interprteur de commandes. Assez de philosophie, revenons la pratique. Dans ce chapitre, nous nous intressons aux trois principaux utilitaires de manipulation de texte : grep, sed et awk. grep recherche des chanes, sed offre un mcanisme de modification du texte au travers dun tube et awk est assez remarquable, car il sagit un prcurseur de perl et une sorte de camlon (il peut tre assez diffrent en fonction de son utilisation). Ces utilitaires, ainsi que quelques autres que nous tudierons dans le chapitre suivant, sont employs par la plupart des scripts shell et la majorit des sessions de commandes
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

150

Chapitre 7 Outils shell intermdiaires I

avec le shell. Si votre script travaille sur une liste de fichiers, il est fort probable que find ou grep fournira cette liste et que sed et/ou awk analysera lentre ou mettra en forme la sortie quelque part dans le script. Autrement dit, si nos exemples de scripts doivent sattaquer des problmes rels, ils doivent sappuyer sur la diversit doutils emloyes par les utilisateurs et programmeurs bash.

7.1. Rechercher une chane dans des fichiers


Problme
Vous souhaitez trouver toutes les occurrences dune chane dans un ou plusieurs fichiers.

Solution
La commande grep examine les fichiers la recherche de lexpression indique :
$ grep printf *.c both.c: printf("Std Out message.\n", argv[0], argc-1); both.c: fprintf(stderr, "Std Error message.\n", argv[0], argc-1); good.c: printf("%s: %d args.\n", argv[0], argc-1); somio.c: // we'll use printf to tell us what we somio.c: printf("open: fd=%d\n", iod[i]); $

Les fichiers analyss dans cet exemple se trouvent tous dans le rpertoire de travail. Nous avons employ le motif simple *.c pour trouver tous les fichiers dont le nom se termine par .c, sans nom de chemin. Cependant, les fichiers examiner ne se trouveront sans doute pas dans un endroit aussi pratique. Dans ce cas, utilisez des noms de chemins. Par exemple :
$ grep printf ../lib/*.c ../server/*.c ../cmd/*.c */*.c

Discussion
Lorsque grep traite un fichier, il commence par afficher son nom, suivi dun caractre deux-points. Le texte ajout aprs ces deux-points reprsente ce que grep a trouv dans le fichier. La recherche retourne toutes les occurrences des caractres. La ligne qui contient la chane fprintf a t affiche car printf est inclus dans fprintf . Le premier argument (autre quune option) de grep peut tre une simple chane, comme dans cet exemple, ou une expression rgulire plus complexe. Ces expressions rgulires sont diffrentes de celles employes pour la correspondance de motifs dans le shell, mme si elles semblent parfois similaires. La correspondance de motifs est si puissante que vous risquez de ne plus pouvoir vous en passer. Les options de grep permettent de varier la sortie gnre. Si laffichage des noms de fichiers vous gne, dsactivez cette fonctionnalit laide de loption -h :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

7.2. Garder uniquement les noms de fichiers


$ grep -h printf *.c printf("Std Out message.\n", argv[0], argc-1); fprintf(stderr, "Std Error message.\n", argv[0], argc-1); printf("%s: %d args.\n", argv[0], argc-1); // we'll use printf to tell us what we printf("open: fd=%d\n", iod[i]); $

151

Si les lignes du fichier ne vous intressent pas et que seul le nombre doccurrences de lexpression est important, utilisez alors loption -c :
$ grep -c printf *.c both.c:2 good.c:1 somio.c:2 $
Lerreur classique est doublier de donner une entre grep. Par exemple, grep maVar. Dans ce cas, grep suppose que lentre provient de STDIN, alors que vous pensez quelle correspond un fichier. La commande grep attend donc patiemment et semble ne rien faire. En ralit, elle attend votre entre depuis le clavier. Cette erreur est assez difficile constater lorsque la recherche se fait dans une grande quantit de donnes et prend donc du temps.

Voir aussi
man grep ; man regex (Linux, Solaris, HP-UX) ou man re_format (BSD, Mac) pour tous les dtails concernant votre bibliothque dexpressions rgulires ; Matrise des expressions rgulires, 2e dition de Jeffrey E. F. Friedl (ditions OReilly) ; Introduction aux scripts shell de Nelson H.F. Beebe et Arnold Robbins (ditions OReilly), chapitre 3 ; le chapitre 9, Rechercher des fichiers avec find, locate et slocate, page 191 et lutilitaire find, pour dautres formes de recherche ; la recette 19.5, Sattendre pouvoir modifier les variables exportes, page 490.

7.2. Garder uniquement les noms de fichiers


Problme
Vous souhaitez trouver les fichiers qui contiennent une certaine chane. La ligne qui contient le texte ne vous intresse pas, seuls les noms des fichiers sont importants.

Solution
Utilisez loption -l de grep pour ne garder que les noms de fichiers :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

152
$ grep -l printf *.c both.c good.c somio.c

Chapitre 7 Outils shell intermdiaires I

Discussion
Si grep trouve plusieurs correspondances dans un mme fichier, le nom nest affich quune seule fois. Si aucune correspondance nest trouve, le nom nest pas prsent. Cette option est trs pratique lorsque vous souhaitez construire une liste de fichiers sur lesquels travailler ensuite. Placez la commande grep dans une construction $( ) et les noms de fichiers qui vous intressent peuvent alors tre ajouts la ligne de commande. Par exemple, voici une commande qui permet de supprimer les fichiers qui contiennent la phrase Ce fichier est obsolte :
$ rm -i $(grep -l 'Ce fichier est obsolte' * )

Nous avons ajout loption -i rm, afin que vous validiez chaque suppression de fichiers. tant donn le potentiel de cette combinaison de commande, cette prcaution nest pas superf lue. bash tend * afin de correspondre tous les fichiers du rpertoire de travail (mais sans aller dans les sous-rpertoires) et passe la liste obtenue en argument grep. Celui-ci produit ensuite la liste des noms de fichiers qui contiennent la chane indique. Enfin, cette liste est reue par la commande rm, qui supprime chaque chaque fichier.

Voir aussi
man grep ; man rm ; man regex (Linux, Solaris, HP-UX) ou man re_format (BSD, Mac) pour tous les dtails concernant votre bibliothque dexpressions rgulires ; Matrise des expressions rgulires, 2e dition de Jeffrey E. F. Friedl (ditions OReilly) ; la recette 2.15, Relier une sortie une entre, page 46 ; la recette 19.5, Sattendre pouvoir modifier les variables exportes, page 490.

7.3. Obtenir une rponse vrai/faux partir dune recherche


Problme
Vous souhaitez savoir si une chane se trouve dans un certain fichier, mais le contenu ne nous intresse pas, uniquement une rponse de type oui ou non.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

7.3. Obtenir une rponse vrai/faux partir dune recherche

153

Solution
Utilisez -q, loption tranquillit de grep. Pour une portabilit maximum, vous pouvez galement rediriger la sortie vers /dev/null. Dans les deux cas, la rponse se trouve dans la variable dtat de sortie $?. Vous pouvez donc vous en servir dans un test if :
$ grep -q chaine fichier.volumineux $ if [ $? -eq 0 ] ; then echo oui ; else echo non ; fi non $

Discussion
Dans un script shell, laffichage sur lcran des rsultats de la recherche nest pas toujours souhait. Vous voulez simplement savoir si une correspondance a t trouve afin que votre script prenne les bonnes dcisions. Comme pour la plupart des commandes Unix/Linux, le code de retour 0 indique un bon droulement. Dans ce cas, la commande a russi si elle a trouv la chane dans au moins lun des fichiers indiqus (dans cet exemple, la recherche se fait dans un seul fichier). La valeur de retour est stocke dans la variable $? du shell, que nous plaons dans une instruction if. Si nous donnons plusieurs noms de fichiers aprs grep -q, grep sarrte ds que la premire concurrence de la chane est trouve. Il ne traite pas tous les fichiers, car vous souhaitez uniquement savoir sil a trouv ou non une occurrence de la chane. Si vous voulez vraiment examiner tous les fichiers, la place de -q, utilisez la solution suivante :
$ grep chaine fichier.volumineux >/dev/null $ if [ $? -eq 0 ] ; then echo oui ; else echo non ; fi non $

La redirection vers /dev/null envoie la sortie vers un priphrique particulier, une benne bits, qui jette tout ce que vous lui donnez. La technique /dev/null savre galement utile pour crire des scripts shell portables avec les diffrentes variantes de grep disponibles sur les systmes Unix et Linux, si tant est quelles ne prennent pas toutes en charge loption -q.

Voir aussi
man grep ; man regex (Linux, Solaris, HP-UX) ou man re_format (BSD, Mac) pour tous les dtails concernant votre bibliothque dexpressions rgulires ; Matrise des expressions rgulires, 2e dition de Jeffrey E. F. Friedl (ditions OReilly) ; la recette 19.5, Sattendre pouvoir modifier les variables exportes, page 490.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

154

Chapitre 7 Outils shell intermdiaires I

7.4. Rechercher une chane en ignorant la casse


Problme
Vous souhaitez rechercher une chane (par exemple, erreur ) dans un fichier de journalisation, mais sans tenir compte de la casse, afin dobtenir toutes les occurrences.

Solution
Utilisez loption -i de grep, qui ignore la place :
$ grep -i erreur journal.msgs

Discussion
Cette forme de recherche trouvera les messages qui contiennent le mot ERREUR , erreur ou Erreur , mais galement ErrEUR ou eRrEUr . Cette option est particulirement utile pour rechercher des mots qui pourraient mlanger les minuscules et les majuscules, notamment ceux placs au dbut dune phrase ou dune adresse de messagerie.

Voir aussi
man grep ; man regex (Linux, Solaris, HP-UX) ou man re_format (BSD, Mac) pour tous les dtails concernant votre bibliothque dexpressions rgulires ; Matrise des expressions rgulires, 2e dition de Jeffrey E. F. Friedl (ditions OReilly) ; la prsentation de la commande find et de son option -iname au chapitre 9 ; la recette 19.5, Sattendre pouvoir modifier les variables exportes, page 490.

7.5. Effectuer une recherche dans un tube


Problme
Vous souhaitez rechercher du texte, mais il ne se trouve pas dans un fichier. En ralit, il sagit de la sortie dune commande ou mme de la sortie de commandes enchanes.

Solution
Envoyez les rsultats grep via un tube :
$ un tube | de commandes | grep

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

7.5. Effectuer une recherche dans un tube

155

Discussion
Lorsquaucun nom de fichier nest donn grep, cette commande lit depuis lentre standard. Cest le cas de la plupart des outils bien conus et destins une utilisation dans des scripts. Cest pour cette raison quils sont constituent des lments de base de lcriture de scripts shell. Si vous souhaitez que grep recherche des messages derreur issus de la commande prcdente, commencez par rediriger lerreur standard vers la sortie standard :
$ gcc mauvais_code.c 2>&1 | grep -i error

Cette ligne de commande tente de compiler du code rempli derreurs. Nous dirigeons lerreur standard vers la sortie standard (2>&1) avant denvoyer (|) la sortie grep, qui recherche, sans tenir compte de la casse (-i), la chane error. Vous avez galement la possibilit denvoyer la sortie de grep vers une autre commande grep. Cette solution permet de rduire les rsultats dune recherche. Par exemple, supposons que vous souhaitiez trouver ladresse lectronique de Bob Johnson :
$ grep -i johnson mail/* ... la sortie est trop longue pour tre intressante car les Johnsons sont nombreux sur Terre ... $ !! | grep -i robert grep -i johnson mail/* | grep -i robert ... la sortie devient plus intressante ... $ !! | grep -i "le bluesman" grep -i johnson mail/* | grep -i robert | grep -i "le bluesman" Robert M. Johnson, Le Bluesman <rmj@nullepart.org>

Vous auriez pu rpter la premire commande grep, mais cet exemple montre galement tout lintrt de loprateur !!. Il permet de rpter la commande prcdente sans la saisir nouveau. Vous pouvez poursuivre la ligne de commande aprs loprateur !!, comme nous lavons fait dans cet exemple. Puisque le shell affiche la commande quil excute, vous savez ce que gnre le remplacement de !! (voir la recette 18.2, page 477). Grce cette approche, vous pouvez construire trs rapidement et simplement un long tube grep. Vous pouvez examiner les rsultats des tapes intermdiaires et dcider daffiner la recherche laide dexpressions grep supplmentaires. La mme tche peut tre ralise avec une seule commande grep et une expression rgulire plus labore, mais nous pensons quil est plus facile de construire un tube tape par tape.

Voir aussi
man grep ; man regex (Linux, Solaris, HP-UX) ou man re_format (BSD, Mac) pour tous les dtails concernant votre bibliothque dexpressions rgulires ; Matrise des expressions rgulires, 2e dition de Jeffrey E. F. Friedl (ditions OReilly) ; la recette 2.15, Relier une sortie une entre, page 46 ; la recette 18.2, Rpter la dernire commande, page 477 ; la recette 19.5, Sattendre pouvoir modifier les variables exportes, page 490.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

156

Chapitre 7 Outils shell intermdiaires I

7.6. Rduire les rsultats de la recherche


Problme
La recherche retourne un trop grand nombre dinformations, y compris des rsultats inutiles.

Solution
Dirigez les rsultats vers grep -v avec une expression qui dcrit ce que vous souhaitez retirer. Supposons que vous recherchiez des messages dans un fichier de journalisation et que seuls ceux du mois de dcembre vous intressent. Vous savez que votre journal utilise labrviation Dec pour dcembre, mais vous ntes pas certain que ce mois est toujours crit ainsi. Pour tre certain sr de trouver toutes les versions, vous saisissez la commande suivante :
$ grep -i dec journal

Vous obtenez alors la sortie suivante :


... error on Jan 01: nombre non decimal error on Feb 13: base convertie en Decimal warning on Mar 22: utiliser uniquement des nombres decimaux error on Dec 16: le message que vous recherchez error on Jan 01: nombre non decimal ...

Une solution rapide ce problme consiste envoyer le premier rsultat vers un deuxime grep et dindiquer celui-ci dignorer les instances de decimal :
$ grep -i dec journal | grep -vi decimal

Il est assez frquent de combiner ainsi plusieurs commandes grep (lorsque de nouvelles correspondances inutiles sont dcouvertes) afin de filtrer les rsultats dune recherche. Par exemple :
$ grep -i dec journal | grep -vi decimal | grep -vi decimer

Discussion
Cette solution a pour inconvnient de retirer certains messages du mois de dcembre sils contiennent galement le mot decimal . Loption -v sera pratique si elle est utilise avec prudence. Vous devez tout simplement faire attention ce quelle peut exclure. Dans notre exemple, une meilleure solution consiste utiliser une expression ou rgulire plus labore qui trouve les correspondances avec le mois de dcembre suivi dune espace et de deux chiffres :
$ grep 'Dec [0-9][0-9]' journal

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

7.7. Utiliser des motifs plus complexes dans la recherche

157

Mais cela ne fonctionnera pas toujours car syslog utilise une espace pour combler les dates constitues dun seul chiffre. Nous devons donc ajouter une espace dans la premire liste [0-9] :
$ grep 'Dec [0-9 ][0-9]' journal

Lexpression est place entre apostrophes car elle contient des espaces. Cela permet galement dviter une interprtation des crochets par le shell (ils ne le seront pas, mais cest une question dhabitude). Il est prfrable de shabituer entourer dapostrophes tout ce qui pourrait perturber le shell. Nous aurions pu crire la ligne suivante :
$ grep Dec\ [0-9\ ][0-9] journal

Lespace est chappe avec une barre oblique inverse. Mais, sous cette forme, il est plus difficile de dterminer la fin de la chane et le dbut du nom du fichier.

Voir aussi
man grep ; man regex (Linux, Solaris, HP-UX) ou man re_format (BSD, Mac) pour tous les dtails concernant votre bibliothque dexpressions rgulires ; Matrise des expressions rgulires, 2e dition de Jeffrey E. F. Friedl (ditions OReilly) ; la recette 19.5, Sattendre pouvoir modifier les variables exportes, page 490.

7.7. Utiliser des motifs plus complexes dans la recherche


Les expressions rgulires de grep offrent des motifs trs puissants qui satisferont pratiquement tous vos besoins. Une expression rgulire dcrit les motifs servant la comparaison avec les chanes. Tout caractre alphabtique correspond ce caractre prcis dans la chane. Autrement dit, A correspond A , B B , etc. Mais les expressions rgulires dfinissent dautres caractres particuliers qui peuvent tre employs seuls ou en combinaison avec dautres caractres pour construire des motifs plus complexes. Nous avons dj expliqu que tout caractre sans signification particulire correspond simplement lui-mme ; A A , etc. La rgle suivante consiste combiner des lettres en fonction de leur emplacement. Ainsi AB correspond A suivi de B . Malgr tout, elle semble trop vidente. Le premier caractre particulier est le point (.). Un point correspond nimporte quel caractre unique. Par consquent, .... correspond quatre caractres, A. A suivi de nimporte quel caractre et .A. nimporte quel caractre, suivi de A , suivi de nimporte quel caractre (pas ncessairement le mme caractre que le premier). Un astrisque (*) indique la rptition daucune ou plusieurs occurrences du caractre prcdent. A* correspond donc zro ou plusieurs caractres A et .* signifie zro ou plusieurs caractres quelconques (par exemple abcdefg , aaaabc , sdfgf ;lkjhj ou mme une ligne vide)

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

158

Chapitre 7 Outils shell intermdiaires I

Alors, que signifie donc ..* ? Il sagit de tout caractre unique suivi de zro ou plusieurs caractres quelconques (autrement dit, un ou plusieurs caractres), mais non une ligne vide. Le caractre accent circonf lexe (^) correspond au dbut dune ligne de texte et le symbole dollar ($) correspond la fin de ligne. Par consquent, ^$ dsigne une ligne vide (le dbut suivi dune fin de ligne, sans rien entre les deux). Pour ajouter un point, un accent circonf lexe, un dollar ou tout autre caractre particulier dans le motif, faites-le prcder dune barre oblique inverse (\). Ainsi, ion. correspond aux lettres ion suivi de nimporte quelle autre lettre, mais ion\. correspond ion suivi dun point. Un jeu de caractres placs entre crochets, par exemple [abc], correspond nimporte lequel de ces caractres (par exemple, a ou b ou c ). Si le premier caractre lintrieur des crochets est un accent circonflexe, alors la correspondance se fait avec tout caractre qui ne se trouve pas dans le jeu indiqu. Par exemple, [AaEeIiOoUu] correspond nimporte quelle voyelle, tandis que [^AaEeIiOoUu] correspond nimporte quel caractre qui nest pas une voyelle. Notez que cela nest pas quivalent une correspondance avec les consonnes car [^AaEeIiOoUu] inclut galement les symboles de ponctuation et les autres caractres spciaux qui ne sont ni des voyelles ni des consonnes. Enfin, vous pouvez employer un mcanisme de rptition qui scrit sous la forme \{n,m\}, o n indique le nombre minimum de rptition et m le nombre maximum. Donn sous la forme \{n\}, il signifie exactement n fois , tandis qucrit \{n,\} , il reprsente au moins n fois . Par exemple, lexpression rgulire A\{5\} quivaut cinq lettres A majuscules la suite, tandis que A\{5,\} correspond cinq lettres A majuscules ou plus.

7.8. Rechercher un numro de scu


Problme
Vous avez besoin dune expression rgulire pour rechercher un numro de scurit sociale amricain1. Ces numros sont constitus de neuf chiffres, regroups en plusieurs parties (123-45-6789), avec ou sans les tirets, qui doivent tre facultatifs.

Solution
$ grep '[0-9]\{3\}-\{0,1\}[0-9]\{2\}-\{0,1\}[0-9]\{4\}' fichier

1. N.d.T. : Cela fonctionne galement avec les numros de scurit sociale franais, mais leur format est moins intressant pour lexemple.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

7.9. Rechercher dans les fichiers compresss

159

Discussion
Ces expressions rgulires sont souvent dites en criture seule, car, une fois crites, elles sont difficiles voire impossibles lire. Nous allons dcomposer celle-ci afin de bien la comprendre. Cependant, lorsque vous crivez un script bash qui sappuie sur les expressions rgulires, noubliez pas dajouter des commentaires qui dcrivent parfaitement les correspondances recherches par ces expressions. En ajoutant des espaces lexpression rgulire, nous pouvons amliorer sa lisibilit, mais nous modifions galement son sens. Cela signifierait que des espaces sont ncessaires l o elles sont indiques dans lexpression. Oublions cela pour le moment et ajoutons quelques espaces dans lexpression rgulire prcdente afin de la rendre plus lisible :
[0-9]\{3\} -\{0,1\} [0-9]\{2\} -\{0,1\} [0-9]\{4\}

Le premier groupe indique nimporte quel chiffre puis exactement 3 fois . Le groupe suivant prcise un tiret puis 0 ou 1 fois . Le troisime groupe signifie nimporte quel chiffre puis exactement 2 fois . Le quatrime groupe reprsente un tiret puis 0 ou 1 fois . Le dernier groupe indique nimporte quel chiffre puis exactement 4 fois

Voir aussi
man regex (Linux, Solaris, HP-UX) ou man re_format (BSD, Mac) pour tous les dtails concernant votre bibliothque dexpressions rgulires ; Introduction aux scripts shell de Nelson H.F. Beebe et Arnold Robbins (ditions OReilly), section 3.2, pour en savoir plus sur les expressions rgulires et les outils qui les utilisent ; Matrise des expressions rgulires, 2e dition de Jeffrey E. F. Friedl (ditions OReilly) ; la recette 19.5, Sattendre pouvoir modifier les variables exportes, page 490.

7.9. Rechercher dans les fichiers compresss


Problme
Vous souhaitez effectuer une recherche dans des fichiers compresss. Devez-vous commencer par les dcompresser ?

Solution
En aucun cas, si les commandes zgrep, zcat ou gzcat sont installes sur votre systme. zgrep est simplement une commande grep qui sait comment traiter les diffrents fichiers compresss ou non (les types reconnus varient dun systme lautre). Elle est souvent employe sous Linux pour des recherches dans les messages de syslog, puisque cet outil maintient une version non compresse du fichier de journalisation courant (pour pouvoir le consulter) et des archives compresses :
$ zgrep 'terme recherch' /var/log/messages*

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

160

Chapitre 7 Outils shell intermdiaires I

zcat est simplement une commande cat qui sait interprter les diffrents fichiers compresss ou non (les types reconnus varient dun systme lautre). Elle reconnat un plus grand nombre de formats que zgrep et elle est probablement installe par dfaut sur un plus grand nombre de systmes. Elle sert galement dans la rcupration de fichiers compresss endommags, puisquelle affiche tout ce quelle parvient lire au lieu de se terminer sur une erreur, comme gunzip ou dautres outils. gzcat est similaire zcat. Les diffrences concernent les variantes commerciales et gratuites dUnix, ainsi que la rtro-compatibilit :
$ zcat /var/log/messages.1.gz

Discussion
Le programme less peut galement tre configur pour prendre en charge diffrents fichiers compresss (voir la recette 8.15, page 189).

Voir aussi
la recette 8.6, Compresser les fichiers, page 178 ; la recette 8.7, Dcompresser des fichiers, page 180 ; la recette 8.15, Aller plus loin avec less, page 189.

7.10. Garder une partie de la sortie


Problme
Vous souhaitez conserver uniquement une partie de la sortie et carter le reste.

Solution
Le code suivant affiche le premier mot de chaque ligne en entre :
$ awk '{print $1}' monEntree.fichier

Les mots sont dlimits par des espaces. awk lit les donnes depuis le nom de fichier indiqu sur la ligne de commande ou depuis lentre standard si aucun nom nest donn. Par consquent, vous pouvez rediriger lentre depuis un fichier ainsi :
$ awk '{print $1}' < monEntree.fichier

Vous pouvez mme utiliser un tube :


$ cat monEntree.fichier | awk '{print $1}'

Discussion
Le programme awk peut tre employ de diffrentes manires. Sous sa forme la plus simple, il affiche simplement un ou plusieurs champs slectionns de lentre.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

7.11. Conserver une partie dune ligne de sortie

161

Les champs sont dlimits par des espaces (ou indiqus avec loption -F) et sont numrots partir de 1. Le champ $0 reprsente lintgralit de la ligne en entre. awk est un langage de programmation complet. Les scripts awk peuvent tre trs complexes. Cet exemple nest quun dbut.

Voir aussi
man awk ; http://www.faqs.org/faqs/computer-lang/awk/faq/ ; Effective awk Programming dArnold Robbins (OReilly Media) ; sed & awk dArnold Robbins et Dale Dougherty (OReilly Media).

7.11. Conserver une partie dune ligne de sortie


Problme
Vous souhaitez ne garder quune partie dune ligne de sortie, comme le premier et le dernier mots. Par exemple, vous aimeriez que ls affiche uniquement les noms de fichiers et les autorisations, sans les autres informations fournies par ls -l. Cependant, ls ne semble proposer aucune option qui configure ainsi la sortie.

Solution
Envoyez ls vers awk et conservez uniquement les champs ncessaires :
$ ls -l | awk '{print $1, $NF}' total 151130 -rw-r--r-- add.1 drwxr-xr-x art drwxr-xr-x bin -rw-r--r-- BuddyIcon.png drwxr-xr-x CDs drwxr-xr-x downloads drwxr-sr-x eclipse ... $

Discussion
Examinons le rsultat de la commande ls -l. Voici le format dune ligne :
drwxr-xr-x 2 utilisateur groupe 176 2006-10-28 20:09 bin

Elle est parfaitement adapte un traitement par awk, puisque, par dfaut, les espaces dlimitent les champs. Dans la sortie gnre par ls -l, les autorisations constituent le premier champ et le nom de fichier se trouve dans le dernier.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

162

Chapitre 7 Outils shell intermdiaires I

Nous utilisons une petite astuce pour afficher le nom de fichier. Dans awk, les champs sont rfrencs laide dun symbole dollar suivi du numro du champ (par exemple, $1, $2, $3). Dautre part, awk possde une variable interne nomme NF qui prcise le nombre de champs trouvs sur la ligne en cours. Par consquent, $NF fait toujours rfrence au dernier champ. Par exemple, une ligne du rsultat de ls est constitue de huit champs. La variable NF contient donc la valeur 8 et $NF fait rfrence au huitime champ de la ligne reue en entre, ce qui correspond au nom de fichier. Noubliez pas que la lecture dune variable awk se fait sans le symbole $ (contrairement aux variables de bash). NF est une rfrence de variable tout fait valide. Si vous y ajoutez un caractre $, sa signification nest plus le nombre de champs de la ligne en cours , mais le dernier champ de la ligne en cours .

Voir aussi
man awk ; http://www.faqs.org/faqs/computer-lang/awk/faq/ ; Effective awk Programming dArnold Robbins (OReilly Media) ; sed & awk dArnold Robbins et Dale Dougherty (OReilly Media).

7.12. Inverser les mots de chaque ligne


Problme
Vous souhaitez afficher les lignes dentre en inversant lordre des mots.

Solution
$ awk '{ > for (i=NF; i>0; i--) { > printf "%s ", $i; > } > printf "\n" > }'

Vous navez pas saisir les caractres >. Ils sont affichs par le shell en tant quinvite afin de vous indiquer que vous navez pas encore termin la commande (il attend que vous entriez lapostrophe finale). Puisque le programme awk est plac entre des apostrophes, bash vous laisse saisir plusieurs lignes, en affichant linvite secondaire, >, jusqu ce que lapostrophe fermante soit donne. Nous avons indent le programme pour des questions de lisibilit, mais vous pouvez lcrire sur une seule ligne :
$ awk '{for (i=NF; i>0; i--) {printf "%s ", $i;} printf "\n" }'

Discussion
En awk, la syntaxe dune boucle for est trs similaire celle du langage C. Il propose mme une instruction printf de mise en forme de la sortie calque sur celle du langage
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

7.13. Additionner une liste de nombres

163

C (ou celle de bash). La boucle for effectue un dcompte, partir du dernier champ vers le premier, et affiche chaque champ au fur et mesure. Nous navons pas ajout le caractre \n au premier printf, car nous voulons que les champs restent sur la mme ligne de sortie. Lorsque la boucle est termine, nous ajoutons un saut de ligne pour terminer la ligne en cours. Compare bash, la rfrence $i dans awk est trs diffrente. Dans bash, nous crivons $i pour obtenir la valeur stocke dans la variable nomme i. Mais, en awk, comme dans la plupart des langages de programmation, nous faisons rfrence la valeur de i en nommant simplement cette variable, autrement dit en crivant directement i. Par consquent, que signifie donc $i en awk ? La valeur de la variable i est convertie en un nombre et lexpression dollar-nombre est interprte comme une rfrence un champ (ou un mot) de lentre, cest--dire le champ lemplacement i. Ainsi, i va du numro du dernier champ au premier et la boucle affiche donc les champs dans lordre invers.

Voir aussi
man printf(1) ; man awk ; http://www.faqs.org/faqs/computer-lang/awk/faq/ ; Effective awk Programming dArnold Robbins (OReilly Media) ; sed & awk dArnold Robbins et Dale Dougherty (OReilly Media) ; la section printf, page 540.

7.13. Additionner une liste de nombres


Problme
Vous souhaitez additionner une liste de nombres, y compris ceux qui napparaissent pas au sein dune ligne.

Solution
Utilisez awk pour isoler les champs additionner et effectuer la somme. Voici comment additionner les tailles des fichiers qui se trouvent sur la sortie dune commande ls -l :
$ ls -l | awk '{somme += $5} END {print somme}'

Discussion
Nous additionnons le cinquime champ du rsultat de ls -l, qui ressemble la ligne suivante :
-rw-r--r-1 albing users 267 2005-09-26 21:26 lilmax

Les diffrents champs sont les autorisations, les liens, le propritaire, le groupe, la taille (en octets), la date, lheure et le nom de fichier. Seule la taille nous intresse et nous utilisons donc $5 dans le programme awk pour faire rfrence ce champ.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

164

Chapitre 7 Outils shell intermdiaires I

Les deux corps de notre programme awk sont placs entre accolades ({}). Un programme awk peut tre constitu de plusieurs corps (ou bloc) de code. Un bloc de code prcd du mot-cl END est excut une seule fois, lorsque le reste du programme est termin. De manire similaire, vous pouvez prfixer un bloc de code par BEGIN, qui sera alors excut avant la lecture de toute entre. Le bloc BEGIN sert gnralement initialiser des variables. Nous aurions pu lemployer dans notre exemple pour initialiser la somme, mais awk garantit que les variables sont initialement vides. Si vous examinez laffichage produit par une commande ls -l, vous noterez que la premire ligne reprsente un total et quelle ne correspond pas au format attendu pour les autres lignes. Nous avons deux manires de grer ce problme. Nous pouvons prtendre que cette premire ligne nexiste pas ; cest lapproche prise prcdemment. Puisque la ligne na pas de cinquime champ, la rfrence $5 est vide et la somme nest pas affecte. Une meilleure approche consiste liminer la ligne. Nous pouvons le faire avant de passer la sortie awk en utilisant grep :
$ ls -l | grep -v '^total' | awk '{somme += $5} END {print somme}'

Ou bien, lintrieur du programme awk :


$ ls -l | awk '/^total/{getline} {somme += $5} END {print somme}'

^total est une expression rgulire qui signifie les lettres t-o-t-a-l places en dbut de ligne (le caractre ^ ancre la recherche en dbut de ligne). Pour toutes les lignes qui correspondent cette expression rgulire, le bloc de code associ est excut. Le deuxime bloc de code (la somme) ne contient pas dinstructions initiales et awk lexcute donc pour chaque ligne dentre (quelle corresponde ou non lexpression rgulire). Lajout du cas particulier pour total a pour objectif dexclure ce type de lignes dans le calcul de la somme. Par consquent, dans le bloc ^total, nous avons ajout une commande getline qui passe la ligne dentre suivante. Lorsque le deuxime bloc de code est atteint, il reoit donc une nouvelle ligne dentre. La commande getline ne reprend pas les correspondances des motifs partir du dbut. En programmation awk, lordre des blocs de code est important.

Voir aussi
man awk ; http://www.faqs.org/faqs/computer-lang/awk/faq/ ; Effective awk Programming dArnold Robbins (OReilly Media) ; sed & awk dArnold Robbins et Dale Dougherty (OReilly Media).

7.14. Compter des chanes


Problme
Vous souhaitez comptabiliser les occurrences de diffrentes chanes, y compris des chanes dont vous ignorez la valeur. Autrement dit, vous ne voulez pas compter les occur-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

7.14. Compter des chanes

165

rences dun ensemble de chanes prdtermines, mais connatre le nombre de chanes contenues dans vos donnes.

Solution
Utilisez les tableaux associatifs de awk (galement appels tables de hachage) pour les compter. Dans notre exemple, nous comptons le nombre de fichiers appartenant aux diffrents utilisateurs du systme. Le nom dutilisateur se trouve dans le troisime champ de la sortie dune commande ls -l. Nous utilisons donc ce champ ($3) comme indice du tableau et incrmentons la valeur associe :
# # bash Le livre de recettes : entableau.awk # NF > 7 { utilisateur[$3]++ } END { for (i in utilisateur) { printf "%s dtient %d fichiers\n", i, utilisateur[i] } }

Dans cet exemple, linvocation de awk est lgrement diffrente. Puisque ce script awk est un peu plus complexe, nous lenregistrons dans un fichier spar. Nous utilisons loption -f pour indiquer awk o se trouve le fichier du script :
$ ls -lR /usr/local | awk -f asar.awk bin dtient 68 fichiers albing dtient 1801 fichiers root dtient 13755 fichiers man dtient 11491 fichiers $

Discussion
La condition NF > 7 nous permet de diffrencier les parties du script awk et dcarter les lignes qui ne contiennent aucun nom de fichier. Elles apparaissent dans la sortie de ls -lR et amliorent la lisibilit car il sagit de lignes vides qui sparent les diffrents rpertoires ainsi que de lignes de total pour chaque sous-rpertoire. Ces lignes contiennent peu de champs (ou mots). Lexpression NF>7 qui prcde laccolade ouvrante nest pas place entre des barres obliques car il ne sagit pas dune expression rgulire, mais dune expression logique, semblable celle des instructions if, qui svalue vrai ou faux. La variable interne NF fait rfrence au nombre de champs de la ligne dentre en cours. Si une ligne dentre contient plus de sept champs, elle est donc traite par les instructions places entre les accolades. Cependant, la ligne importante est la suivante :
utilisateur[$3]++

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

166

Chapitre 7 Outils shell intermdiaires I

Le nom dutilisateur (par exemple, bin) sert dindice du tableau. Il sagit dun tableau associatif car une table de hachage (ou un mcanisme similaire) permet dassocier chaque chane unique un indice numrique. awk effectue tout ce travail votre place et vous navez donc pas mettre en uvre des comparaisons de chanes ou des recherches. Une fois le tableau construit, vous pourriez penser que laccs aux valeurs est complexe. Pour cela, awk propose une version particulire de la boucle for. la place de la forme numrique for(i=0; i<max; i++), awk dispose dune syntaxe rserve aux tableaux associatifs :
for (i in utilisateur)

Dans cette expression, la variable i prend successivement les valeurs (dans un ordre quelconque) qui ont servi dindices au tableau utilisateur. Dans notre exemple, cela signifie que i aura des valeurs diffrentes (bin, albing, man, root) chaque itration de la boucle. Si vous naviez encore jamais rencontr de tableaux associatifs, nous esprons que vous tes surpris et impressionn, il sagit dune caractristique trs puissante de awk (et de Perl).

Voir aussi
man awk ; http://www.faqs.org/faqs/computer-lang/awk/faq/ ; Effective awk Programming dArnold Robbins (OReilly Media) ; sed & awk dArnold Robbins et Dale Dougherty (OReilly Media).

7.15. Afficher les donnes sous forme dhistogramme


Problme
Vous avez besoin dun histogramme rapide de certaines donnes.

Solution
Utilisez les tableaux associatifs de awk, comme nous lavons expliqu la recette prcdente :
# # bash Le livre de recettes : hist.awk # function max(tab, sup) { sup = 0; for (i in utilisateur) { if (utilisateur[i] > sup) { sup=utilisateur[i];} }

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

7.15. Afficher les donnes sous forme dhistogramme


return sup } NF > 7 { utilisateur[$3]++ } END { # Pour les proportions. maxm = max(utilisateur); for (i in utilisateur) { #printf "%s dtient %d fichiers\n", i, utilisateur[i] echelle = 60 * utilisateur[i] / maxm ; printf "%-10.10s [%8d]:", i, utilisateur[i] for (i=0; i<echelle; i++) { printf "#"; } printf "\n"; } }

167

Lexcution de ce programme sur la mme entre que la recette prcdente produit le rsultat suivant :
$ ls -lR bin albing root man $ /usr/local | awk -f hist.awk [ 68]:# [ 1801]:####### [ 13755]:################################################## [ 11491]:##########################################

Discussion
Nous aurions pu placer le code de max au dbut du bloc END, mais nous voulions montrer comment dfinir des fonctions en awk. Nous utilisons une version un peu plus labore de printf. Le format %-10.10s aligne droite la chane de caractres, mais la comble et la tronque galement 10 caractres. Le format numrique %8d sassure que lentier est affich dans un champ de 8 caractres. De cette manire, chaque histogramme commence au mme point, quels que soient le nom de lutilisateur et la taille de lentier. Comme toutes les oprations arithmtiques en awk, le calcul des proportions se fait en virgule flottante, except si le rsultat est explicitement tronqu par un appel la fonction interne int( ). Ce nest pas le cas dans notre exemple. La boucle for sexcutera donc au moins une fois et mme la plus petite quantit de donnes sera affiche sous forme dun seul symbole dise. Lordre des donnes renvoyes par la boucle for (i in utilisateur) nest pas prcis, mais il correspond probablement un ordre adapt la table de hachage sous-jacente. Si vous souhaitez trier lhistogramme, en fonction de la taille numrique ou des noms dutilisateurs, vous devez ajouter une forme de tri. Pour cela, vous pouvez dcouper ce

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

168

Chapitre 7 Outils shell intermdiaires I

programme en deux parties et envoyer la sortie de la premire vers la commande sort, dont la sortie doit tre redirige vers la deuxime partie du programme qui affiche lhistogramme.

Voir aussi
man awk ; http://www.faqs.org/faqs/computer-lang/awk/faq/ ; Effective awk Programming dArnold Robbins (OReilly Media) ; sed & awk dArnold Robbins et Dale Dougherty (OReilly Media) ; la recette 8.1, Trier votre affichage, page 171.

7.16. Afficher un paragraphe de texte aprs une phrase trouve


Problme
Vous recherchez une phrase dans un document et souhaitez afficher le paragraphe aprs la phrase trouve.

Solution
Dans le fichier texte, nous supposons quun paragraphe est dlimit par des lignes vides. En faisant cette hypothse, voici un court programme awk :
$ cat para.awk /phrase/ { indic=1 } { if (indic == 1) { print $0 } } /^$/ { indic=0 } $ $ awk -f para.awk < text.txt

Discussion
Ce programme est constitu de trois blocs de code simples. Le premier est invoqu lorsquune ligne dentre correspond lexpression rgulire (dans ce cas, uniquement le mot phrase ). Si phrase se trouve nimporte o dans la ligne dentre, une correspondance est trouve et le bloc de code est excut. Il fixe simplement la variable indic 1. Le deuxime bloc de code est invoqu pour chaque ligne dentre, car aucune expression rgulire ne prcde son accolade ouvrante. Mme une ligne qui contient le mot phrase est traite par ce bloc de code (si ce fonctionnement nest pas souhait, placez une instruction continue dans le premier bloc). Le code du deuxime bloc affiche simplement la ligne dentre complte, mais uniquement si la variable indic vaut 1.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

7.16. Afficher un paragraphe de texte aprs une phrase trouve

169

Le troisime bloc de code commence par une expression rgulire qui, si elle est satisfaite, rinitialise simplement la variable indic. Cette expression rgulire emploie deux caractres ayant une signification particulire. Laccent circonf lexe (^), utilis en premier caractre de lexpression rgulire, correspond au dbut de la ligne. Le symbole ($), utilis en dernier caractre, correspond la fin de la ligne. Lexpression rgulire ^$ signifie donc une ligne vide , car il ny a aucun caractre entre le dbut et la fin de la ligne. Nous pouvons crire une expression rgulire un peu plus complexe pour quune ligne contenant uniquement des espaces soit galement considre comme une ligne vide. Dans ce cas, la troisime ligne du script devient la suivante :
/^[:blank:]*$/ { indic=0 }

Les programmeurs Perl affectionnent particulirement le genre de problme et la solution dcrits dans cette recette, mais nous avons choisi awk car Perl sort (en grande partie) du cadre de ce livre. Si vous savez programmer en Perl, utilisez ce langage. Sinon, awk peut rpondre vos besoins.

Voir aussi
man awk ; http://www.faqs.org/faqs/computer-lang/awk/faq/ ; Effective awk Programming dArnold Robbins (OReilly Media) ; sed & awk dArnold Robbins et Dale Dougherty (OReilly Media).

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

8
Outils shell intermdiaires II
Une fois de plus, nous avons quelques outils utiles qui ne font pas partie du shell, mais qui servent tant de scripts que vous devez les connatre. Les tris sont des tches tellement habituelles, et utiles pour amliorer la lisibilit, quil est bon de connatre la commande sort. Dans la mme veine, la commande tr effectue des traductions et des remplacements caractre par caractre, de mme quelle peut en supprimer. Ces outils prsentent le point commun de ne pas tre crits comme des commandes autonomes, mais comme des filtres qui peuvent tre inclus dans des commandes enchanes avec des redirections. Ce type de commandes prend en gnral un ou plusieurs noms de fichier en paramtre (ou argument) mais, en absence de nom de fichier, ces commandes utiliseront lentre standard. Elles crivent sur la sortie standard. Cette combinaison facilite les connexions entre ces commandes grce des tubes, comme dans quelquechose | sort | autrechose. Cela les rend particulirement pratiques et vite la confusion rsultant de trop nombreux fichiers temporaires.

8.1. Trier votre affichage


Problme
Vous aimeriez que laffichage apparaisse tri. Mais vous ne voulez pas crire (une fois de plus) une fonction de tri personnelle dans votre programme ou votre script. Cette fonctionnalit existe-t-elle dj ?

Solution
Utilisez loutil sort. Vous pouvez trier un ou plusieurs fichiers en plaant leur nom sur la ligne de commande :
$ sort fichier1.txt fichier2.txt monautrefichier.xyz
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

172

Chapitre 8 Outils shell intermdiaires II

Sans nom de fichier sur la ligne de commande, sort lira depuis lentre standard et vous pourrez injecter le rsultat dune autre commande dans sort :
$ commandes | sort

Discussion
Il peut tre pratique de disposer dun affichage tri sans avoir ajouter du code vos programmes et scripts pour effectuer ce tri. Les tubes du shell vous permettent dinsrer sort la sortie standard de tous les programmes. sort comporte plusieurs options, mais deux dentre elles sont les plus importantes :
$ sort -r

pour inverser lordre du tri (o, comme le dit la citation, les premiers seront les derniers et les derniers seront les premiers) et
$ sort -f

pour regrouper 1 les minuscules et les majuscules ensemble. En dautres termes, cette option ignore la casse. Il est aussi possible dutiliser loption au format GNU long :
$ sort --ignore-case

Nous avons dcid de faire durer le suspens, consultez la recette suivante pour connatre la troisime option de sort.

Voir aussi
man sort ; la recette 8.2, Trier les nombres, page 172.

8.2. Trier les nombres


Problme
Lorsque vous triez des donnes numriques, vous remarquez que lordre du tri semble incorrect :
$ sort nombres.txt 2 200 21 250 $

Solution
Vous devez indiquer sort que les donnes trier sont des nombres. Pour cela, utilisez loption -n :
1. N.d.T. : f pour le verbe to fold en anglais pourrait aussi se traduire par replier .

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

8.3. Trier des adresses IP


$ sort -n nombres.txt 2 21 200 250 $

173

Discussion
Lordre de tri initial nest pas faux ; loutil effectue un tri alphabtique sur les donnes (21 se situe aprs 200 car 1 se trouve aprs 0, dans un tri alphabtique). Bien sr, vous prfrerez certainement avoir un tri numrique et vous devrez utiliser loption -n. sort -rn peut tre particulirement pratique pour donner une liste dcroissante de frquences quelconque en le couplant uniq -c. Par exemple, affichons les shells les plus populaires de ce systme :
$ cut -d':' -f7 /etc/passwd | sort | uniq -c | sort -rn 20 /bin/sh 10 /bin/false 2 /bin/bash 1 /bin/sync

cut -d':' -f7 /etc/passwd isole la chane indiquant le shell dans le fichier /etc/passwd. Puis nous devons effectuer un premier pr-tri pour que uniq puisse compter les doublons conscutifs. Ensuite, sort -rn retourne une liste dcroissante, trie numriquement, avec les interprteurs de commandes les plus populaires en tte. Si vous navez pas besoin de compter les occurrences et que vous ne voulez que la liste des valeurs uniques (sans doublons), vous pouvez utiliser loption -u de la commande sort (et omettre la commande uniq). Ainsi, pour connatre la liste des diffrents interprteurs utiliss sur le systme, vous pouvez excuter :
cut -d':' -f7 /etc/passwd | sort -u

Voir aussi
man sort ; man uniq ; man cut.

8.3. Trier des adresses IP


Problme
Vous voulez trier une liste dadresses IP numriques mais vous voudriez que le tri se fasse uniquement sur la dernire portion de ladresse ou sur ladresse globale.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

174

Chapitre 8 Outils shell intermdiaires II

Solution
Pour trier en fonction du dernier octet (ancienne syntaxe) :
$ sort -t. -n +3.0 adressesIP.lst 10.0.0.2 192.168.0.2 192.168.0.4 10.0.0.5 192.168.0.12 10.0.0.20 $

Pour trier en fonction de la totalit de ladresse (syntaxe POSIX) :


$ sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n adressesIP.lst 10.0.0.2 10.0.0.5 10.0.0.20 192.168.0.2 192.168.0.4 192.168.0.12 $

Discussion
Nous savons quil sagit de donnes numriques et nous utilisons donc loption -n. Loption -t indique le caractre reconnatre comme sparateur entre les champs (dans notre cas, un point), ainsi nous pouvons prciser lordre selon lequel trier les champs. Dans le premier exemple, nous commenons le tri avec le troisime champ (la numrotation commence 0) partir de la gauche et par son tout premier caractre (la numrotation commence, ici aussi, 0) de ce champ, soit +3.0. Dans le second exemple, nous avons utilis la nouvelle spcification POSIX la place de la mthode traditionnelle (et obsolte) +pos1 -pos2. Contrairement lancienne mthode, la numrotation ne commence pas 0 mais 1.
$ sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n adressesIP.lst

Comme cest compliqu ! Voici la mme commande avec lancienne syntaxe : sort -t. +0n -1 +1n -2 +2n -3 +3n -4, qui nest gure plus lisible ! Lutilisation de -t. pour dfinir le dlimiteur de champ est la mme, mais les champs employer sont indiqus autrement. Dans ce cas, -k 1,1n signifie la cl de tri commence au dbut du premier champ (1) et (,) sarrte la fin du premier champ (1) en faisant un tri numrique (n) . Une fois que vous aurez acquis cela, le reste sera enfantin. Lors de lutilisation de plusieurs champs, il est trs important dindiquer sort o sarrter. Le comportement par dfaut place la fin de la cl de tri la fin de la ligne, ce qui nest pas obligatoirement ce que vous souhaitez et cela peut vous poser problme si vous ne connaissez pas ce comportement.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

8.3. Trier des adresses IP

175

L'ordre que sort utilise est influenc par les rglages de localisation (les paramtres rgionaux). Si vos rsultats ne sont pas ceux attendus, vrifiez ces paramtres.

Lordre du tri change dun systme un autre selon que la commande sort utilise un algorithme de tri stable par dfaut ou non. Un tel algorithme prserve lordre original dans les donnes tries lorsque les champs de tri sont gaux. Linux et Solaris nutilisent pas un algorithme stable par dfaut au contraire de NetBSD. De plus, si loption -S dsactive le tri stable sous NetBSD, elle configure la taille du tampon dans les autres versions de sort. Si nous excutons cette commande sort sur un systme Linux ou Solaris :
$ sort -t. -k4n ipaddr.list

ou celle-ci sur un systme NetBSD :


$ sort -t. -S -k4n ipaddr.list

nous obtiendrons des donnes tries comme celles de la premire colonne du tableau 8-1. Retirez le -S sur un systme NetBSD et sort triera les donnes comme celles de la deuxime colonne. Tableau 8-1. Comparaison de l'ordre de tri sous Linux, Solaris et NetBSD
Linux et Solaris (par dfaut) et NetBSD (-S)
10.0.0.2 192.168.0.2 10.0.0.4 192.168.0.4 192.168.0.12 10.0.0.20 # # # # # # sluggish laptop mainframe office speedy lanyard

NetBSD (par dfaut)


192.168.0.2 10.0.0.2 192.168.0.4 10.0.0.4 192.168.0.12 10.0.0.20 # # # # # # laptop sluggish office mainframe speedy lanyard

Si notre fichier dentre, adressesIP.lst, avait toutes les adresses 192.168 en premier, suivies par toutes les adresses 10., le tri stable devrait laisser les adresses 192.168 en premier lorsquil trouve une galit entre les cls de deux lments. Nous pouvons voir dans le tableau 8-1 que cette situation se produit pour les ordinateurs laptop et sluggish, dont les adresses se terminent par le chiffre 2 et pour les machines mainframe et office, dont les adresses se terminent par un 4. Avec le tri par dfaut sous Linux (ou sous NetBSD avec loption -S), lordre nest pas garanti. Pour revenir une solution simple, et pour sentraner un peu, trions alphabtiquement notre liste dadresses IP. Nous voulons utiliser le caractre # comme sparateur et effectuer le tri sur le second champ :
$ sort -t'#' -k2 adressesIP.lst 10.0.0.20 # lanyard 192.168.0.2 # laptop 10.0.0.5 # mainframe 192.168.0.4 # office 10.0.0.2 # sluggish 192.168.0.12 # speedy $

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

176

Chapitre 8 Outils shell intermdiaires II

La cl de tri commencera au second champ et, dans ce cas, ira jusqu la fin de la ligne. Avec un seul sparateur (#) par ligne, nous navons pas besoin dindiquer une fin de cl, mme si nous aurions pu ajouter -k2,2.

Voir aussi
man sort ; lexemple ./functions/inetaddr de lannexe B, tel que fourni dans larchive des sources bash.

8.4. Couper des parties de la sortie


Problme
Vous avez besoin de consulter uniquement une partie de donnes longueur fixe ou en colonnes. Vous aimeriez conserver uniquement un sous-ensemble de ces donnes, en fonction de la position horizontale (colonne).

Solution
Utilisez la commande cut avec loption -c pour slectionner certaines colonnes. Remarquez que, dans notre exemple, la commande 'ps' ne fonctionne quavec certains systmes (en loccurence, sous CentOS-4, Fedora Core 5 et Ubuntu mais pas sous Red Hat 8, NetBSD, Solaris et Mac OS X qui utilisent des colonnes diffrentes) :
$ ps -l | cut -c12-15 PID 5391 7285 7286 $

ou :
$ ps -elf | cut -c58(affichage non copi)

Discussion
Avec la commande cut, nous indiquons la portion conserver dans chaque ligne. Dans le premier exemple, nous gardons les colonnes de la douzime la quinzime, incluses. Dans le second exemple, nous conservons les colonnes partir de la cinquante-huitime, car nous indiquons une colonne de dbut, mais pas de colonne de fin. La plupart des donnes manipuler que nous avons rencontres sappuient sur des champs, dont la position relative est donnes grce des dlimiteurs. La commande cut peut aussi effectuer des slections sur de telles structures, mais cest une des seules commandes que vous utiliserez avec bash qui puisse aussi effectuer des slections dans des donnes largeur de colonne fixe (avec loption -c).

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

8.5. Retirer les lignes identiques

177

Lutilisation de cut pour afficher des champs plutt que des colonnes est possible, bien que plus limite que dautres outils comme awk. Le dlimiteur par dfaut est le caractre de tabulation, mais vous pouvez en indiquer un autre laide de loption -d. Voici un exemple de commande cut utilisant des champs :
$ cut -d'#' -f2 < adressesIP.lst

et la commande awk quivalente :


$ awk -F'#' '{print $2}' < adressesIP.lst

Vous pouvez aussi utiliser cut pour quil prenne en compte des dlimiteurs variables en employant plusieurs commandes cut. Il serait prfrable dutiliser une expression rgulire avec awk pour cela, mais, dans certains cas, quelques commandes cut enchanes sont plus vite dveloppes et tapes. Voici comment vous pouvez obtenir le champ se trouvant entre crochets. Remarquez que le premier cut utilise un crochet ouvrant (-d'[') comme dlimiteur et conserve le second champ (-f2). Comme il a dj retir les caractres antrieurs au crochet ouvrant, le second cut utilise un crochet fermant comme dlimiteur (-d']') et ne conserve que le premier champ (-f1).
$ cat Ligne Ligne Ligne donnes_dlimites [l1]. [l2]. [l3].

$ cut -d'[' -f2 donnes_dlimites | cut -d']' -f1 l1 l2 l3

Voir aussi
man cut ; man awk.

8.5. Retirer les lignes identiques


Problme
Aprs avoir slectionn et/ou tri des donnes, vous remarquez que le rsultat comporte de nombreuses lignes dupliques. Vous aimeriez les liminer de manire ne conserver quune seule occurrence de chaque ligne.

Solution
Vous avez deux possibilits. Si vous avez utilis la commande de tri sort, vous pouvez ajouter loption -u :
$ commandes | sort -u

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

178

Chapitre 8 Outils shell intermdiaires II

Si vous nutilisez pas sort, redirigez la sortie standard dans uniq (en supposant que le rsultat est tri et que les lignes dupliques sont donc adjacentes :
$ commandes > monfichier $ uniq monfichier

Discussion
Comme uniq ncessite que les donnes soient pralablement tries, nous utiliserons donc plus volontiers loption -u de sort moins que nous devions aussi compter le nombre de doublons (option -c, voir la recette 8.2, page 172), ou ne conserver que les lignes dupliques (-d), ce que uniq peut faire.
N'crasez pas un fichier important par erreur ; les paramtres de la commande uniq sont un peu surprenants. Alors que la plupart des commandes Unix/Linux peuvent prendre plusieurs noms de fichier en entre, uniq ne le peut pas. En fait, le premier argument ( ne pas confondre avec les options) est utilis comme seul et unique fichier dentre et un second argument (sil y en a un) est interprt comme fichier de sortie. Ainsi, si vous fournissez deux noms de fichier, le second sera cras sans avertissement.

Voir aussi
man sort ; man uniq ; la recette 8.2, Trier les nombres, page 172.

8.6. Compresser les fichiers


Problme
Vous devez compresser certains fichiers et vous ntes pas certain de la meilleure mthode employer.

Solution
Tout dabord, vous devez comprendre que, sous Unix, larchivage et la compression des fichiers sont deux oprations distinctes utilisant deux outils diffrents, contrairement au monde DOS et Windows dans lequel cela ne correspond qu une seule opration effectue par un unique outil. Un fichier tarball est cr en combinant plusieurs fichiers et/ou rpertoires grce la commande tar (tape archive), puis compress laide des outils compress, gzip ou bzip2 ; ce qui gnre des fichiers comme tarball.tar.Z, tarball.tar.gz, tarball.tgz ou tarball.tar.bz2. Cependant, de nombreux autres outils, tels que zip, sont aussi supports. Pour utiliser le format correct, vous devez savoir o et comment les donnes seront utilises. Si vous ne faites que compresser des fichiers pour vous-mme, utilisez ce qui vous semble le plus simple. Si dautres personnes ont besoin de vos donnes, prenez aussi en compte leur plateforme.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

8.6. Compresser les fichiers

179

Historiquement, les tarball sous Unix taient compresss avec compress (tarball.tar.Z), mais gzip est maintenant bien plus rpandu et bzip2 (qui offre un meilleur taux de compression que gzip) gagne du terrain. Une question doutil se pose galement. Certaines versions de tar vous permettent dutiliser automatiquement la compression de votre choix lors de la cration de larchive, dautres ne le permettent pas. Le format universel accept par Unix ou Linux utilise gzip (tarball.tar.gz ou tarball.tgz) ; il est cr comme ceci :
$ tar cf nom_du_tarball.tar rpertoire_de_fichiers $ gzip nom_du_tarball.tar

Si vous disposez de la version GNU de tar, vous pourriez indiquer -Z pour utiliser compress (ne le faites pas, ce format est obsolte), -z pour recourir gzip (le choix le plus sr) ou -j pour employer bzip2 (meilleure compression). Noubliez pas dutiliser un nom de fichier cohrent avec le format de compression, ce nest pas automatique.
$ tar czf nom_du_tarball.tgz rpertoire_de_fichiers

Alors que tar et gzip sont disponibles sur de nombreuses plateformes, si vous devez partager vos donnes avec une plateforme Windows, vous devriez plutt utiliser zip, qui est presque universel. zip et unzip sont fournis par le paquetage InfoZip sous Unix et presque toutes les plateformes pouvant vous venir lesprit. Malheureusement, ils ne sont pas toujours installs par dfaut. Comme ces outils ne viennent pas du monde Unix, pour obtenir des informations daide lutilisation lancez la commande sans argument ni redirection. Notez la prsence de loption -l pour convertir les fins de ligne du format Unix au format Microsoft et loption -ll pour faire le contraire.
$ zip -r nom_du_fichier_zip rpertoire_de_fichiers

Discussion
Il existe de trop nombreux algorithmes et outils de compression pour tous les traiter ici ; on peut citer : AR, ARC, ARJ, BIN, BZ2, CAB, CAB, JAR, CPIO, DEB, HQX, LHA, LZH, RAR, RPM, UUE et ZOO. Avec tar, nous vous recommandons fortement dutiliser des chemins relatifs comme argument pour lister les fichiers archiver. Si vous prenez un nom de rpertoire absolu, vous pourriez craser involontairement quelque chose sur le systme utilis lors de la dcompression et si vous nindiquez pas de rpertoire du tout, vous allez polluer le rpertoire courant dans lequel la commande de dcompression sera lance (voir la recette 8.8, page 182). Habituellement, il est recommand dutiliser le nom et ventuellement la version des donnes archiver comme nom de rpertoire. Le tableau 8-2 donne quelques exemples. Tableau 8-2. Bons et mauvais exemples pour nommer les rpertoires archiver avec tar
Bon ./monappli_1.0.1 Mauvais monappli.c monappli.h monappli.man /usr/local/bin

./bintools

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

180

Chapitre 8 Outils shell intermdiaires II

Il faut noter que les fichiers du gestionnaire de paquetages de Red Hat (Red Hat Package Manager, RPM) sont en fait des fichiers CPIO avec un en-tte. Vous pouvez obtenir un script shell ou Perl appel rpm2cpio (http://fedora.redhat.com/docs/drafts/rpm-guide-en/chextra-packaging-tools.html) pour liminer ces en-ttes et extraire les fichiers de larchive ainsi :
$ rpm2cpio un.rpm | cpio -i

Les fichiers Debian .deb sont, quant eux, des archives ar contenant des archives tar compresses avec gzip ou bzip2. Ils peuvent tre extraits avec les outils standard ar, tar et gunzip ou bunzip2. De nombreux outils sous Windows tels que WinZip, PKZIP, FilZip et 7-Zip peuvent grer tous les formats ci-dessus ou presque, voire mme dautres formats tels que .tar ou .rpm.

Voir aussi
man tar ; man gzip ; man bzip2 ; man compress ; man zip ; man rpm ; man ar ; man dpkg ; http://www.info-zip.org/ ; http://fedora.redhat.com/docs/drafts/rpm-guide-en/ch-extra-packaging-tools.html ; http://en.wikipedia.org/wiki/Deb_(file_format) ; http://www.rpm.org/ ; http://en.wikipedia.org/wiki/RPM_Package_Manager ; la recette 7.9, Rechercher dans les fichiers compresss, page 159 ; la recette 8.7, Dcompresser des fichiers, page 180 ; la recette 8.8, Vrifier les rpertoires contenus dans une archive tar, page 182 ; la recette 17.3, Dzipper plusieurs archives ZIP, page 432.

8.7. Dcompresser des fichiers


Problme
Vous devez dcompresser un ou plusieurs fichiers portant une extension telle que tar, tar.gz, gz, tgz, Z ou zip.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

8.7. Dcompresser des fichiers

181

Solution
Dterminez le format des fichiers et utilisez loutil appropri. Le tableau 8-3 fait correspondre les extensions les plus courantes aux programmes capables de les manipuler. Tableau 8-3. Extensions courantes et outils associs
Extensions des fichiers .tar .tar.gz, .tgz .tar.bz2 .tar.Z .zip Commandes tar tf (liste le contenu), tar xf (extrait) GNU tar : tar tzf (liste le contenu), tar xzf (extrait) ou : gunzip fichier && tar xf fichier GNU tar : tar tjf (liste le contenu), tar xjf (extrait) ou : gunzip2 fichier && tar xf fichier GNU tar : tar tZf (liste le contenu), tar xZf (extrait) ou : uncompress fichier && tar xf fichier unzip (rarement install par dfaut)

Vous devriez aussi essayer la commande file :


$ file fichier_inconnu.* fichier_inconnu.1: GNU tar archive fichier_inconnu.2: gzip compressed data, from Unix $ gunzip fichier_inconnu.2 gunzip: fichier_inconnu.2: unknown suffix -- ignored $ mv fichier_inconnu.2 fichier_inconnu.2.gz $ gunzip fichier_inconnu.2.gz $ file fichier_inconnu.2 fichier_inconnu.2: GNU tar archive

Discussion
Si lextension ne correspond aucune de celles listes dans le tableau 8-3, que la commande file ne vous aide pas, mais que vous tes certain quil sagit bien dune archive, vous devriez alors effectuer une recherche sur le Web.

Voir aussi
la recette 7.9, Rechercher dans les fichiers compresss, page 159 ; la recette 8.6, Compresser les fichiers, page 178.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

182

Chapitre 8 Outils shell intermdiaires II

8.8. Vrifier les rpertoires contenus dans une archive tar


Problme
Vous voulez dsarchiver une archive .tar, mais vous voulez dabord connatre les rpertoires dans lesquels elle va crire les fichiers. Vous pouvez consulter la table des matires de larchive avec tar -t, mais le rsultat peut tre trs volumineux et il est facile de passer ct dune ligne importante.

Solution
Utilisez un script awk pour filtrer les noms des rpertoires partir de la table des matires de larchive tar, puis avec sort -u liminez les doublons :
$ tar tf archive.tar | awk -F/ '{print $1}' | sort -u

Discussion
Loption t affiche la table des matires du fichier dont le nom est indiqu par loption f. La commande awk utilise un sparateur de champs non-standard laide de loption -F/ (le caractre barre oblique). Ainsi, linstruction print $1 affichera le premier nom de rpertoire du chemin. Enfin, tous les noms de rpertoires seront tris et ne seront affichs quune seule fois. Si une ligne de laffichage contient uniquement un simple point, cela signifie que certains fichiers seront extraits dans le rpertoire courant lorsque vous dcompresserez le fichier tar, vrifiez donc que vous vous trouvez bien dans le rpertoire souhait. De mme, si les noms des fichiers situs dans larchive sont tous locaux, sans ./ au dbut, vous obtiendrez la liste des noms de fichier qui seront crs dans le rpertoire courant. Si laffichage contient une ligne vide, cela signifie que certains fichiers ont t archivs partir dun chemin absolu (commenant par le caractre /). Une fois de plus, soyez prudent lors de lextraction partir dune telle archive car vous pourriez craser des fichiers involontairement.

Voir aussi
man tar ; man awk ; la recette 8.1, Trier votre affichage, page 171 ; la recette 8.2, Trier les nombres, page 172 ; la recette 8.3, Trier des adresses IP page 173. ,

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

8.9. Substituer des caractres

183

8.9. Substituer des caractres


Problme
Vous devez remplacer un caractre par un autre dans tout votre texte.

Solution
Utilisez la commande tr pour remplacer un caractre par un autre. Par exemple :
$ tr ';' ',' < fichier.avant > fichier.apres

Discussion
Dans sa forme la plus simple, une commande tr remplace les occurrences du premier (uniquement) caractre du premier argument par le premier (uniquement) caractre du second argument. Dans lexemple de la solution, nous avons inject le contenu dun fichier appel fichier.avant dans le filtre, redirig la sortie vers un fichier appel fichier.apres et nous avons remplac tous les points-virgules par des virgules. Pourquoi utilisons-nous des apostrophes autour du point-virgule et de la virgule ? Un point-virgule a une signification particulire pour le shell bash. Si nous ne le protgeons pas, bash va linterprter comme un sparateur de commande et va considrer que la ligne comporte deux commandes, ce qui va entraner une erreur. La virgule na pas de sens particulier, mais nous lavons protge par habitude pour viter toute interprtation laquelle nous naurions pas pense ; il est plus prudent dutiliser toujours des apostrophes, ainsi on ne les oublie pas lorsquelles sont ncessaires. La commande tr peut faire bien plus dune substitution la fois en plaant plusieurs caractres traduire dans le premier argument et leurs caractres de substitution respectifs dans le second argument. Noubliez pas quil sagit toujours dun remplacement unpour-un. Par exemple :
$ tr ';:.!?' ',' < ponctuations.txt > virgules.txt

substituera une virgule tous les caractres de ponctuation rencontrs (point-virgule, deux-points, point, point dexclamation et point dinterrogation). Comme le second argument est plus court que le premier, son dernier (et unique) caractre est rpt pour que sa longueur soit gale celle du premier argument, ainsi chaque caractre remplacer correspond une virgule. Ce type de transformation peut aussi tre effectu par la commande sed, cependant la syntaxe de cette dernire est un peu plus complexe. La commande tr nest pas aussi puissante car elle ne reconnat pas les expressions rgulires mais elle dispose dune syntaxe particulire pour les plages de caractres et cela peut savrer bien pratique comme nous le verrons dans la recette 8.10, page 184.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

184

Chapitre 8 Outils shell intermdiaires II

Voir aussi
man tr.

8.10. Changer la casse des caractres


Problme
Vous devez liminer les diffrences de casse dans texte.

Solution
Vous pouvez convertir tous les caractres majuscules (A-Z) en minuscules (a-z) en utilisant la commande tr et en lui transmettant une plage de caractres comme le montre lexemple suivant :
$ tr 'A-Z' 'a-z' < fichier_avec_majuscules.txt > fichier_sans_majuscules.txt

tr dispose aussi dune syntaxe particulire pour convertir ce type de plages :


$ tr '[:upper:]' '[:lower:]' < avec_majuscules.txt > sans_majuscules.txt

Discussion
Mme si la commande tr ne gre pas les expressions rgulires, elle prend en charge les plages de caractres. Vrifiez bien que les deux arguments comportent le mme nombre de caractres. Si le second argument est plus court, son dernier caractre sera rpt autant de fois que ncessaire pour atteindre une longueur gale celle du premier. En revanche, si le premier argument est le plus court, le second sera tronqu la mme taille que le premier. Voici un codage trs simpliste dun message textuel laide dun chiffrement par substitution qui dcale chaque caractre de treize places (connu sous le nom de ROT13). Une des proprits intressantes de ROT13 est que le mme processus sert la fois chiffrer et dchiffrer le texte :
$ cat /tmp/plaisanterie Q: Pourquoi le poulet a-t-il travers la route ? R: Pour aller de l'autre ct. $ tr 'A-Za-z' 'N-ZA-Mn-za-m' < /tmp/plaisanterie D: Cbhedhbv yr cbhyrg n-g-vy geniref yn ebhgr ? E: Cbhe nyyre qr y'nhger pg. $ tr 'A-Za-z' 'N-ZA-Mn-za-m' < /tmp/plaisanterie | \ tr 'A-Za-z' 'N-ZA-Mn-za-m' Q: Pourquoi le poulet a-t-il travers la route ? R: Pour aller de l'autre ct.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

8.11. Convertir les fichiers DOS au format Linux/Unix

185

Voir aussi
man tr ; http://fr.wikipedia.org/wiki/ROT13.

8.11. Convertir les fichiers DOS au format Linux/Unix


Problme
Vous devez convertir des fichiers texte du format DOS au format Linux ou Unix. Sous DOS, chaque ligne se termine par un couple de caractres (retour-chariot et passage la ligne). Sous Linux, chaque ligne se termine uniquement par le caractre de passage la ligne. Comment donc effacer ces caractres DOS superflus ?

Solution
Utilisez loption -d de tr pour effacer les caractres fournis dans la liste. Par exemple, pour supprimer tous les retours-chariot DOS (\r), saisissez la commande :
$ tr -d '\r' <fichier_dos.txt >fichier_unix.txt
Cela va supprimer tous les caractres \r du fichier, y compris ceux qui ne sont pas la fin des lignes. Habituellement, les fichiers texte comportent rarement ailleurs de tels caractres, mais cela reste possible. Vous pouvez aussi vous tourner vers les programmes dos2unix et unix2dos si ce comportement vous drange.

Discussion
Loutil tr dispose de quelques squences dchappement parmi lesquelles on peut citer \r pour les retours-chariot et \n pour les passages la ligne. Les autres squences spciales sont listes dans le tableau 8-4. Tableau 8-4. Les squences d'chappement de l'outil tr
Squence
\ooo \\ \a \b \f \n

Signification Caractre dont la valeur octale est ooo Un caractre barre oblique inverse (chappement de la barre oblique inverse elle-mme) Bip audible , le caractre ASCII BEL ( b est dj occup par la suppression arrire) Suppression arrire (correction du dernier caractre) Saut de page Passage la ligne

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

186

Chapitre 8 Outils shell intermdiaires II

Tableau 8-4. Les squences d'chappement de l'outil tr


Squence
\r \t \v

Signification Retour-chariot Tabulation horizontale Tabulation verticale

Voir aussi
man tr.

8.12. Supprimer les guillemets


Problme
Vous voulez obtenir un texte en ASCII pur partir dun document MS Word mais, lorsque vous le sauvegardez en tant que texte, certains caractres spciaux subsistent.

Solution
Convertir les caractres spciaux en caractres ASCII classiques :
$ tr '\221\222\223\224\226\227' '\047\047""--' < source.txt > destination.txt

Discussion
De tels guillemets typographiques viennent du jeu de caractres Windows-1252 et peuvent aussi apparatre dans des courriels enregistrs au format texte. Pour citer Wikipdia sur le sujet :
Quelques clients de messagerie envoient des guillemets en utilisant les codes Windows1252 mais ils indiquent que le texte est encod avec le jeu de caractres ISO-8859-1 ce qui cause des problmes aux dcodeurs qui ne dduisent pas automatiquement que le code de contrle C1 en ISO-8859-1 correspond en fait des caractres Windows-1252.

Pour nettoyer de tels textes, nous pouvons utiliser la commande tr. Les caractres guillemets (cods 221 et 222 en octal) seront convertis en apostrophes doubles (guillemets anglais prsents dans le jeu de caractres ASCII). Nous spcifions ces apostrophes doubles en octal (047) pour des raisons de facilit car linterprteur de commande Unix utilise les apostrophes comme dlimiteurs. Les codes 223 et 224 (octal) correspondent aux guillemets ouvrants et fermants et seront convertis. Les caractres doubles apostrophes peuvent tre saisis en second argument car les apostrophes les entourant les protgent de toute interprtation par le shell. Les caractres 226 et 227 (octal) sont des tirets longs et seront convertis en trait dunion (la seconde occurrence du trait dunion dans la ligne de commande nest pas indispensable car tr rpte le dernier caractre pour complter le second argument la mme longueur que le premier argument, mais il est prfrable dtre exhaustif).

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

8.13. Compter les lignes, les mots ou les caractres dans un fichier

187

Voir aussi
man tr ; http://en.wikipedia.org/wiki/Curved_quotes pour savoir tout ce quil faut et plus sur les guillemets et les problmes quils posent (lien en anglais).

8.13. Compter les lignes, les mots ou les caractres dans un fichier
Problme
Vous avez besoin de connatre le nombre de lignes, de mots ou de caractres prsents dans un fichier donn.

Solution
Utilisez la commande wc (word count) associe awk. Laffichage standard de wc ressemble ceci :
$ wc fichier_donnes 5 15 60 fichier_donnes # Compte uniquement les lignes $ wc -l fichier_donnes 5 fichier_donnes # Compte uniquement les mots $ wc -w fichier_donnes 15 fichier_donnes # Compte uniquement les caractres (souvent gal au nombre d'octets) $ wc -c fichier_donnes 60 fichier_donnes # Taille du fichier en octets $ ls -l fichier_donnes -rw-r--r-- 1 jp users 60B Dec 6 03:18 fichier_donnes

Vous pourriez tre tent par une ligne comme celle-ci :


fichier_donnes_lines=$(wc -l "$fichier_donnes")

Elle ne fera pas ce que vous espriez, vous obtiendrez quelque chose du genre de 5 fichier_donnes comme rsultat. Essayez plutt :
fichier_donnes_lines=$(wc -l "$fichier_donnes" | awk '{print $1}')

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

188

Chapitre 8 Outils shell intermdiaires II

Discussion
Si votre version de wc est localise2, le nombre de caractres pourra tre diffrent du nombre doctets, avec certains jeux de caractres.

Voir aussi
man wc ; la recette 15.7, Dcouper lentre si ncessaire, page 345.

8.14. Reformater des paragraphes


Problme
Vous avez des textes dont certaines lignes sont trop longues ou trop courtes, vous aimeriez les reformater pour rendre le texte plus lisible.

Solution
Utilisez la commande fmt, ventuellement avec une longueur de ligne minimale et maximale :
$ fmt texte $ fmt 55 60 texte

Discussion
Une particularit de fmt est quelle sattend trouver des lignes vierges pour sparer les en-ttes et les paragraphes. Si votre fichier ne contient pas ces lignes, il ny a aucun moyen de diffrencier les changements de paragraphes des retours la ligne placs lintrieur dun paragraphe. Vous obtiendrez donc un paragraphe gant dont la longueur des lignes sera homogne. La commande pr peut aussi savrer intressante pour formater du texte.

Voir aussi
man fmt ; man pr.

2. N.d.T. : elle prend en charge les paramtres spcifiques votre rgion gographique.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

8.15. Aller plus loin avec less

189

8.15. Aller plus loin avec less


less is more! 3

Problme
Vous aimeriez exploiter mieux les possibilits de lafficheur less.

Solution
Consultez la page de manuel de less et utilisez la variable $LESS et les fichiers ~/.lessfilter et ~/.lesspipe. less prend ses options partir de la variable $LESS alors, plutt que de crer un alias avec vos options favorites, placez-les dans cette variable. Elle peut aussi bien contenir des options longues que courtes et des options passes en ligne de commande surchargent celles dclares dans la variable. Nous recommandons dutiliser les options longues dans la variable $LESS car elles sont plus lisibles. Par exemple :
export LESS="--LONG-PROMPT --LINE-NUMBERS --ignore-case --QUIET"

Mais ce nest quun dbut. less est extensible grce aux prprocesseurs d'entre, qui ne sont que de simples programmes ou scripts pour pr-traiter le fichier que less est sur le point dafficher. Cette fonctionnalit est gre par les variables denvironnement $LESSOPEN et $LESSCLOSE. Vous pouvez construire votre propre prprocesseur, mais conomisez du temps et consultez le script lesspipe.sh de Wolfgang Friebel disponible sur http://www-zeuthen.desy.de/ ~friebel/unix/lesspipe.html (mais commencez par lire la discussion ci-dessous). Le script fonctionne en initialisant et en exportant la variable denvironnement $LESSOPEN lorsquil est excut seul :
$ ./lesspipe.sh LESSOPEN="|./lesspipe.sh %s" export LESSOPEN

Vous pouvez donc lexcuter simplement dans une instruction eval, telle que eval $(/path/to/lessfilter.sh) ou eval `/path/ to/lessfilter.sh` avant dutiliser less comme votre habitude. La liste des formats supports pour la version 1.53 est :
gzip, compress, bzip2, zip, rar, tar, nroff, archive ar, pdf, ps, dvi, bibliothques partages, programmes, rpertoires, RPM, Microsoft Word, formats OpenOffice 1.x et OASIS (OpenDocument), Debian, fichiers MP3, formats d'image (png, gif, jpeg, tiff, ...), textes utf-16, images iso et des systmes de fichiers sur support amovible travers /dev/xxx

Mais il souffre dun inconvnient : le traitement de ces formats ncessite diffrents outils externes, les fonctionnalits de lexemple dutilisation de lesspipe.sh ne pourront

3. N.d.T. : loutil habituel pour afficher du texte est more , qui se traduit en franais par plus . less , qui est une alternative more se traduit, quant lui par moins . Le jeu de mot se traduit donc par moins est plus !

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

190

Chapitre 8 Outils shell intermdiaires II

pas toutes fonctionner si vous ne disposez pas des outils associs aux formats. Le paquetage contient aussi des scripts ./configure (ou make) pour gnrer une version spcifique du filtre fonctionnant avec les outils disponibles sur votre systme.

Discussion
less est unique dans le sens quil sagit dun outil GNU qui tait dj install par dfaut sur chaque systme de test que nous avons utilis, vraiment tous ! Mme bash nest pas dans cette situation. En mettant les diffrences de version de ct, toutes les installations fonctionnaient de la mme manire. Quel succs ! Cependant, nous ne pouvons pas en dire de mme pour lesspipe* et les filtres de less. Nous avons trouv diffrentes versions, avec des fonctionnalits variables par rapport celles dcrites ci-dessus. Red Hat dispose dun /usr/bin/lesspipe.sh qui ne peut pas tre utilis avec cette syntaxe : eval `lesspipe`. Debian offre un /usr/bin/lesspipe qui peut tre evalu et qui prend aussi en charge des filtres supplmentaires grce un fichier ~/.lessfilter. SUSE Linux dispose dun /usr/bin/lessopen.sh qui ne peut pas tre evalu. FreeBSD propose un /usr/bin/lesspipe.sh rudimentaire (pas devaluation, de traitement des fichiers .Z, .gz ou .bz2). Solaris, HP-UX, les autres BSD et Mac nen disposent pas du tout par dfaut.

Pour voir si vous avez dj de lune de ces versions, essayez ce qui suit sur votre systme. Ce systme Debian propose lesspipe, mais il nest pas activ (la variable $LESSOPEN nest pas dfinie) :
$ type lesspipe.sh; type lesspipe; set | grep LESS -bash3: type: lesspipe.sh: not found lesspipe is /usr/bin/lesspipe

Ce systme Ubuntu dispose du lesspipe Debian et il est utilis :


$ type lesspipe.sh; type lesspipe; set | grep LESS -bash: type: lesspipe.sh: not found lesspipe is hashed (/usr/bin/lesspipe) LESSCLOSE='/usr/bin/lesspipe %s %s' LESSOPEN='| /usr/bin/lesspipe %s'

Nous vous recommandons de tlcharger, configurer et dutiliser la version de lesspipe.sh crite par Wolfgang Friebel car cest la plus complte. Nous vous recommandons aussi de consulter la page de manuel de less car elle est trs instructive.

Voir aussi
man less ; man lesspipe ; man lesspipe.sh ; http://www.greenwoodsoftware.com/less/ ; http://www-zeuthen.desy.de/~friebel/unix/lesspipe.html.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

9
Rechercher des fichiers avec find, locate et slocate
Parvenez-vous retrouver facilement vos donnes dans tous vos systmes de fichiers ? En gnral, il est assez facile de mmoriser les noms et les emplacements des premiers fichiers crs. Ensuite, face laugmentation de leur nombre, vous crez des sous-rpertoires (ou dossiers) pour regrouper les fichiers connexes. Puis, des sous-rpertoires arrivent lintrieur des premiers sous-rpertoires et vous avez de plus en plus de mal vous souvenir de lemplacement des donnes. Bien entendu, avec des disques durs de plus en plus vastes, il est de moins en moins ncessaire de supprimer les fichiers devenus obsoltes ou superflus. Dans ce cas, comment pouvez-vous retrouver le fichier que vous avez modifi la semaine dernire ? Ou la pice jointe que vous avez enregistre dans un sous-rpertoire (dont le choix tait pourtant logique ce moment-l) ? Votre systme de fichiers est peut-tre encombr de fichiers MP3 stocks dans de nombreux dossiers. Diffrents outils graphiques ont t dvelopps pour faciliter la recherche de fichiers. Mais, comment pouvez employer le rsultat de ces recherches graphiques en entre dautres commandes ? bash et les outils GNU peuvent vous aider. Ils apportent des possibilits de recherche tendues qui permettent de retrouver des fichiers en fonction de leur nom, de leur date de cration ou de modification et mme de leur contenu. Ils envoient les rsultats sur la sortie standard, ce qui convient parfaitement une utilisation dans dautres commandes ou scripts. Ne vous inquitez plus, voici les informations dont vous avez besoin.

9.1. Retrouver tous vos fichiers MP3


Problme
Vous disposez dun grand nombre de fichiers audio MP3 parpills sur votre systme de fichiers. Vous aimeriez les regrouper tous en un seul endroit afin de les organiser et de les copier sur votre lecteur audio.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

192

Chapitre 9 Rechercher des fichiers avec find, locate et slocate

Solution
La commande find peut retrouver tous les fichiers, puis excuter une commande qui les dplace au bon endroit. Par exemple :
$ find . -name '*.mp3' -print -exec mv '{}' ~/chansons \;

Discussion
La syntaxe de find ne ressemble pas celle des autres outils Unix. Les options ne sont pas employes de manire classique, avec un tiret et un ensemble de lettres uniques suivies des arguments. la place, les options sont de courts mots donns dans une suite logique qui dcrit la recherche des fichiers, puis le traitement leur appliquer, si ncessaire. Ces options, semblables des mots, sont souvent appeles prdicats. Les premiers arguments de la commande find reprsentent les rpertoires dans lesquels doit se faire la recherche. Vous indiquerez, en gnral, uniquement le rpertoire de travail (.). Mais vous pouvez donner une liste de rpertoires ou mme effectuer la recherche sur lintgralit du systme de fichiers (selon vos autorisations) en utilisant la racine (/) comme point de dpart. Dans notre exemple, la premire option (le prdicat -name) prcise le motif recherch. Sa syntaxe est quivalente celle de la correspondance de motifs de bash. Par consquent, *.mp3 correspondra tous les noms de fichiers qui se terminent par les caractres .mp3 . Tout fichier conforme ce motif donne le rsultat vrai et lexcution se poursuit avec le prdicat suivant de la commande. Vous pouvez imaginer le processus de la manire suivante. find parcourt le systme de fichiers et chaque nom de fichier trouv est prsent lensemble des conditions qui doivent tre satisfaites. Lorsquune condition est remplie, la suivante est teste. Si une condition nest pas remplie, le fichier est alors immdiatement cart et le suivant est analys. La condition -print est simple. Elle vaut toujours vrai et a pour effet dafficher le nom du fichier sur la sortie standard. Par consquent, tout fichier ayant satisfait lensemble des conditions prcdentes voit son nom affich. Loption -exec est un peu plus trange. Lorsquun nom de fichier arrive jusqu elle, il est insr dans une commande qui sera excute. La suite de la ligne, jusquaux caractres \;, constitue cette commande. Les accolades {} sont remplaces par le nom du fichier trouv. Par consquent, dans notre exemple, si find rencontre un fichier nomm mhsr.mp3 dans le sous-rpertoire ./musique/jazz, la commande excute est alors :
mv ./musique/jazz/mhsr.mp3 ~/chansons

La commande concerne chaque fichier qui correspond au motif. Si le nombre de ces fichiers est trs grand, autant de commandes seront excutes. Parfois, cela ncessite des ressources systme trop importantes. Il peut alors tre prfrable dutiliser find uniquement pour trouver les fichiers et de placer leur nom dans un fichier de donnes, puis dexcuter dautres commandes en runissant plusieurs arguments sur une ligne. Cependant, les ordinateurs tant de plus en plus rapides, ce problme est de moins en moins rel. Il se pourrait mme que votre processeur double ou quadruple cur ait enfin de quoi soccuper.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

9.2. Traiter les noms de fichiers contenant des caractres tranges

193

Voir aussi
man find ; la recette 1.3, Chercher et excuter des commandes, page 6 ; la recette 1.4, Obtenir des informations sur des fichiers, page 8 ; la recette 9.2, Traiter les noms de fichiers contenant des caractres tranges, page 193.

9.2. Traiter les noms de fichiers contenant des caractres tranges


Problme
Vous utilisez une commande find comme celle de la recette 9.1, page 191, mais les rsultats ne sont pas ceux que vous attendiez car plusieurs fichiers ont des noms qui contiennent des caractres tranges.

Solution
Tout dabord, vous devez savoir que, pour les Unixiens, trange signifie tout ce qui nest pas une lettre minuscule, voire un chiffre . Par consquent, les lettres majuscules, les espaces, les symboles de ponctuation et les caractres accentus sont tous des caractres tranges. Nanmoins, ils apparaissent trs souvent dans les noms de chansons et dartistes. En fonction des caractres prsents dans les noms, de votre systme, de vos outils et de votre objectif, il peut tre suffisant de placer la chane de remplacement entre apostrophes. Autrement dit, mettez des apostrophes autour de {} ('{}') . Si cela ne change rien, utilisez largument -print0 de find et largument -0 de xargs. -print0 indique find demployer le caractre nul (\0) et non lespace comme sparateur lors de laffichage des noms de chemins trouvs. -0 prcise ensuite xargs le sparateur de lentre. Cette solution fonctionne toujours, mais elle peut ne pas tre prise en charge par votre systme. La commande xargs prend des noms de chemins spars par des espaces (except lorsque loption -0 est utilise) sur lentre standard et excute la commande indique pour le plus grand nombre de noms possible (elle sarrte juste avant datteindre la valeur ARG_MAX de votre systme ; voir la recette 15.13, page 357). Puisque linvocation dautres commandes implique un surcot important, lutilisation de xargs permet dacclrer lopration car les invocations de cette commande sont aussi rduites que possible (elle nest pas appele pour chaque nom de chemin trouv). Voici donc comment modifier la solution de la recette 9.1, page 191, pour prendre en charge les caractres incongrus :
$ find . -name '*.mp3' -print0 | xargs -i -0 mv '{}' ~/chansons

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

194

Chapitre 9 Rechercher des fichiers avec find, locate et slocate

Voici un exemple similaire illustrant lutilisation de xargs pour la prise en charge des espaces dans les noms de chemins ou de fichiers lors de la localisation et de la copie de fichiers :
$ locate P1100087.JPG PC220010.JPG PA310075.JPG PA310076.JPG | \ > xargs -i cp '{}' .

Discussion
Cette approche pose deux problmes. Premirement, il est possible que votre version de xargs ne reconnaisse pas loption -i. Deuximement, loption -i interdit le regroupement des arguments et a donc un impact ngatif sur lamlioration des performances. Le problme vient du fait que la commande mv attend le rpertoire cible en dernier argument, alors que la commande xargs classique prend simplement son entre et lajoute la fin de la commande indique, jusqu ce quil ny ait plus de place ou que lentre soit vide. Certaines versions de xargs offrent donc une option -i qui utilise par dfaut {} (comme find). Cependant, -i impose que la commande soit excute individuellement pour chaque lment de lentre. Son seul avantage par rapport au prdicat -exec de find rside dans la prise en charge des caractres tranges. La commande xargs est, cependant, plus efficace lorsquelle est employe conjointement find et une commande comme chmod, qui attend simplement la liste des arguments traiter. Vous constaterez une nette amlioration des performances si vous manipulez un grand nombre de noms de chemins. Par exemple :
$ find un_repertoire -type f -print0 | xargs -0 chmod 0644

Voir aussi
man find ; man xargs ; la recette 9.1, Retrouver tous vos fichiers MP3, page 191 ; la recette 15.13, Contourner les erreurs liste darguments trop longue , page 357.

9.3. Acclrer le traitement des fichiers trouvs


Problme
Vous utilisez une commande find comme celle de la recette 9.1, page 191, mais le traitement des fichiers trouvs prend beaucoup de temps car ils sont nombreux. Vous souhaitez donc acclrer cette opration.

Solution
Consultez la prsentation de la commande xargs la recette 9.2, page 193.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

9.4. Suivre les liens symboliques

195

Voir aussi
la recette 9.1, Retrouver tous vos fichiers MP3, page 191 ; la recette 9.2, Traiter les noms de fichiers contenant des caractres tranges, page 193.

9.4. Suivre les liens symboliques


Problme
Vous utilisez une commande find pour rechercher vos fichiers .mp3, mais elle ne les trouve pas tous. Il manque ceux enregistrs dans un autre systme de fichiers et rfrencs par des liens symboliques. La commande find est-elle incapable de franchir ce type de barrire ?

Solution
Utilisez le prdicat -follow. Notre exemple prcdent devient alors :
$ find . -follow -name '*.mp3' -print0 | xargs -i -0 mv '{}' ~/chansons

Discussion
Il arrive parfois que le passage dun systme de fichiers lautre ne soit pas voulu. Cest pourquoi, par dfaut, la commande find ne suit pas les liens symboliques. Si vous souhaitez les prendre en compte, utilisez -follow en premire option de la commande find.

Voir aussi
man find.

9.5. Retrouver des fichiers sans tenir compte de la casse


Problme
Certains de vos fichiers MP3 se terminent par lextension .MP3 la place de .mp3. Comment pouvez-vous les inclure galement dans la recherche ?

Solution
Utilisez le prdicat -iname (si votre version de find le reconnat), la place de -name, pour effectuer une recherche insensible la casse. Par exemple :
$ find . -follow -iname '*.mp3' -print0 | xargs -i -0 mv '{}' ~/chansons

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

196

Chapitre 9 Rechercher des fichiers avec find, locate et slocate

Discussion
La casse des noms de fichiers est parfois importante. Utilisez loption -iname lorsque ce nest pas le cas, par exemple comme dans notre exemple o .mp3 et .MP3 dsignent tous deux des fichiers trs probablement de type MP3. Nous prcisons probablement car, sur les systmes de type Unix, vous pouvez nommer un fichier comme bon vous semble. Il nest pas oblig de possder une extension prcise. Le problme des lettres minuscules et majuscules est plus frquent lorsque vous manipulez des systmes de fichiers Microsoft Windows, notamment dun type ancien. Notre appareil photo numrique enregistre ses fichiers avec des noms de la forme PICT001.JPG, en incrmentant le nombre chaque image. La commande suivante trouvera peu dimages :
$ find . -name '*.jpg' -print

Dans ce cas, vous pouvez galement essayer la suivante :


$ find . -name '*.[Jj][Pp][Gg]' -print

En effet, lexpression rgulire trouvera une correspondance avec nimporte quelle lettre place entre les crochets. Cependant, cette forme de la commande est moins facile saisir, en particulier si le motif est long. En pratique, -iname constitue une meilleure solution. En revanche, toutes les versions de find ne prennent pas en charge ce prdicat. Si cest le cas de votre systme, vous pouvez toujours employer des expressions rgulires, utiliser plusieurs options -name avec des variantes de la casse ou installer la version GNU de find.

Voir aussi
man find.

9.6. Retrouver des fichiers daprs une date


Problme
Supposez que vous ayez reu un fichier JPEG il y a plusieurs mois et que vous layez enregistr sur votre systme de fichiers, mais vous avez totalement oubli dans quel rpertoire. Comment pouvez-vous le retrouver ?

Solution
Utilisez une commande find avec le prdicat -mtime, qui vrifie la date de dernire modification. Par exemple :
find . -name '*.jpg' -mtime +90 -print

Discussion
Le prdicat -mtime attend un argument qui fixe la plage temporelle de la recherche. La valeur 90 reprsente 90 jours. En ajoutant le signe plus au nombre (+90), vous indiquez
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

9.7. Retrouver des fichiers daprs leur type

197

que le fichier doit avoir t modifi il y a plus de 90 jours. En crivant -90 (avec un signe moins), la modification doit avoir eu lieu il y a moins de 90 jours. Sans le signe plus ou moins, la date de modification est exactement 90 jours. Plusieurs prdicats permettent deffectuer la recherche en fonction de la date de modification dun fichier et chacun attend un argument de quantit. Un signe plus, un signe moins ou aucun signe reprsente, respectivement, une date suprieure , infrieure ou gale cette valeur. find dispose galement des constructions logiques ET, OU et NON. Par consquent, si vous savez que le fichier date dau moins une semaine (7 jours), mais pas plus de 14 jours, vous pouvez alors combiner les prdicats de la manire suivante :
$ find . -mtime +7 -a -mtime -14 -print

Des combinaisons plus complexes de OU, de ET et de NON sont mme possibles :


$ find . -mtime +14 -name '*.text' -o \( -mtime -14 -name '*.txt' \) -print

Cette commande affiche les fichiers dont le nom se termine par .text et qui datent de plus de 14 jours, ainsi que les fichiers qui datent de moins de 14 jours et dont le nom se termine par .txt. Les parenthses seront sans doute ncessaires pour dfinir la priorit adquate. Deux prdicats la suite quivalent un ET logique et sont prioritaires sur un OU (dans find comme dans la plupart des langages). Utilisez les parenthses pour lever lambigut de vos conditions. Puisque les parenthses ont une signification particulire dans bash, vous devez les chapper, en les crivant \( et \) ou avec des apostrophes, '(' et ')'. Cependant, vous ne devez pas placer lintgralit de lexpression entre des apostrophes car cela perturbe la commande find. Elle attend chacun de ses prdicats comme des mots spars.

Voir aussi
man find.

9.7. Retrouver des fichiers daprs leur type


Problme
Vous recherchez un rpertoire dont le contient le mot java . Vous essayez donc la commande suivante
$ find . -name '*java*' -print

Elle trouve un grand nombre de fichiers, y compris les fichiers sources Java enregistrs sur le systme de fichiers.

Solution
Utilisez le prdicat -type pour slectionner uniquement les rpertoires :
$ find . -type d -name '*java*' -print

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

198

Chapitre 9 Rechercher des fichiers avec find, locate et slocate

Discussion
Nous avons plac le prdicat -type d avant -name *java*. Lordre inverse produit le mme ensemble de fichiers. Cependant, en commenant par -type d, la recherche sera lgrement plus efficace car pour chaque fichier rencontr, la commande find commence par vrifier sil sagit dun rpertoire et, uniquement dans ce cas, compare son nom au motif. Si tous les fichiers ont un nom, peu dentre eux sont des rpertoires. En choisissant cet ordre, la plupart des fichiers ne sont pas concerns par la comparaison de chane. Est-ce vraiment un problme ? Les processeurs tant de plus en plus rapides, ce point perd de limportance. Les disques durs tant de plus en plus volumineux, ce point gagner de limportance. Le tableau 9-1 rcapitule les diffrents types de fichiers que vous pouvez tester, en prcisant la lettre correspondante. Tableau 9-1. Caractres utiliss par le prdicat -type de find
Lettre b c d p f l s D Signification Fichier spcial en mode bloc. Fichier spcial en mode caractre. Rpertoire. Tube (ou fifo ). Fichier normal. Lien symbolique. Socket. (Solaris uniquement) door .

Voir aussi
man find.

9.8. Retrouver des fichiers daprs leur taille


Problme
Vous souhaitez faire un peu de mnage sur le disque dur, en commenant par trouver les fichiers les plus volumineux et dcider si vous devez les supprimer ou non. Comment pouvez-vous retrouver ces fichiers ?

Solution
Utilisez le prdicat -size de la commande find pour slectionner les fichiers dont la taille est suprieure, infrieure ou gale celle indique. Par exemple :
find . -size +3000k -print

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

9.9. Retrouver des fichiers daprs leur contenu

199

Discussion
Tout comme celui du prdicat -mtime, largument numrique de -size peut tre prcd dun signe moins, dun signe plus ou daucun signe, afin dindiquer une taille infrieure , suprieure ou gale largument. Dans notre exemple, nous recherchons les fichiers dont la taille est suprieure celle prcise. Nous avons galement indiqu une unit de taille, k pour kilo-octets. La lettre c dsigne des octets (ou caractres). Si vous utilisez b, ou aucune unit, la taille correspond des blocs. Un bloc quivaut 512 octets (une valeur courante sur les systmes Unix). Nous recherchons donc des fichiers de taille suprieure 3 Mo.

Voir aussi
man find ; man du.

9.9. Retrouver des fichiers daprs leur contenu


Problme
Comment pouvez-vous retrouver un fichier partir dun contenu dtermin ? Supposons que vous ayez crit une lettre importante et que vous layez enregistre sous forme dun fichier texte, en lui donnant lextension .txt. En dehors de cela, vous savez uniquement que la lettre contient quelque part le mot prsage .

Solution
Si le fichier se trouve dans le rpertoire de travail, vous pouvez commencer par un simple grep :
grep -i prsage *.txt

Grce loption -i, grep ignore la casse. Cette commande ne permettra peut-tre pas de trouver ce que vous recherchez, mais commenons simplement. Bien entendu, si vous pensez que le fichier se trouve dans lun de vos nombreux sous-rpertoires, vous pouvez tenter datteindre tous les fichiers contenus dans les sous-rpertoires du rpertoire courant laide de la commande suivante :
grep -i prsage */*.txt

Avouons-le, cette solution est assez limite. Si elle ne convient pas, passez une solution plus labore, fonde sur la commande find. Utilisez loption -exec de find afin dexcuter, si toutes les conditions sont vrifies, une commande sur chaque fichier trouv. Voici comment invoquer grep ou nimporte quel autre utilitaire :
find . -name '*.txt' -exec grep -Hi prsage '{}' \;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

200

Chapitre 9 Rechercher des fichiers avec find, locate et slocate

Discussion
Nous employons la construction -name '*.txt' pour rduire le champ de recherche. Ce type de test conduit une meilleure efficacit, car lexcution dun programme spar pour chaque fichier trouv est trs coteuse en temps et en processeur. Vous avez peut-tre galement une ide gnrale de lanciennet du fichier. Dans ce cas, servezvous galement du prdicat -mdate. Lors de lexcution de la commande, '{}' est remplac par le nom de fichier. Les caractres \; indiquent la fin de la commande. Vous pouvez la faire suivre par dautres prdicats. Les accolades et le point-virgule doivent tre chapps. Nous plaons les premires entre apostrophes et faisons prcder le second dune barre oblique inverse. Lchappement choisi na pas dimportance, vous devez simplement viter que bash les interprte de manire errone. Sur certains systmes, loption -H affiche le nom du fichier uniquement si grep a trouv quelque chose. Normalement, lorsquun seul nom de fichier lui est donn, grep ne sembte pas prsenter le nom du fichier, mais uniquement la ligne trouve. Puisque notre recherche concerne de nombreux fichiers, il nous est important de connatre celui qui contient la chane. Si votre version de grep ne prend pas en charge loption -H, ajoutez simplement /dev/null comme nom de fichier pass la commande grep. Puisquelle reoit ainsi plusieurs fichiers examiner, elle affiche le nom de celui qui contient la chane.

Voir aussi
man find.

9.10. Retrouver rapidement des fichiers ou du contenu


Problme
Vous souhaitez pouvoir retrouver des fichiers sans avoir attendre la fin dune longue commande find ou vous devez retrouver un fichier avec du contenu prcis.

Solution
Si votre systme dispose de locate, slocate, Beagle, Spotlight ou de tout autre systme dindexation, vous tes par. Dans le cas contraire, installez-les. Comme nous lavons expliqu la recette 1.3, page 6, locate et slocate consultent une base de donnes stockant des informations sur le systme (gnralement compile et actualise par une tche cron) afin de trouver un fichier ou une commande quasi instantanment. Lemplacement de la base de donnes, les informations quelle contient et la frquence dactualisation peuvent varier dun systme lautre. Pour plus de dtails, consultez les pages de manuel de votre systme.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

9.10. Retrouver rapidement des fichiers ou du contenu


$ locate apropos /usr/bin/apropos /usr/share/man/de/man1/apropos.1.gz /usr/share/man/es/man1/apropos.1.gz /usr/share/man/it/man1/apropos.1.gz /usr/share/man/ja/man1/apropos.1.gz /usr/share/man/man1/apropos.1.gz

201

locate et slocate nindexent pas le contenu. Pour cela, voyez la recette 9.9, page 199. Beagle et Spotlight sont des exemples dune technologie assez rcente appele moteur de recherche locale. Google Desktop Search et Copernic Desktop Search en sont deux exemples pour Microsoft Windows. Les outils de recherche locale emploient une forme dindexation pour trouver, analyser et indexer les noms et le contenu de tous les fichiers (et, en gnral, les messages lectroniques) de votre espace de stockage personnel ; autrement dit, votre rpertoire personnel sur un systme Unix ou Linux. Ces informations sont disponibles presquinstantanment lorsque vous en avez besoin. Ces outils offrent de nombreuses possibilits de configuration et une interface graphique, oprent de manire spcifique chaque utilisateur et indexent le contenu de vos fichiers.

Discussion
slocate mmorise les informations dautorisation (en plus des noms de fichiers et des chemins) afin de ne pas prsenter les donnes auxquelles lutilisateur na pas accs. Sur la plupart des systmes Linux, locate est un lien symbolique vers slocate. Dautres systmes peuvent disposer de programmes distincts ou omettre slocate. Ces deux outils en ligne de commande examinent et indexent lintgralit (plus ou moins) du systme de fichiers, mais ne contiennent que des noms et des emplacements.

Voir aussi
man locate ; man slocate ; http://beagle-project.org/ ; http://www.apple.com/fr/macosx/features/spotlight/ ; http://desktop.google.fr/ ; http://www.copernic.com/fr/products/desktop-search/ ; la recette 1.3, Chercher et excuter des commandes, page 6 ; la recette 9.9, Retrouver des fichiers daprs leur contenu, page 199.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

202

Chapitre 9 Rechercher des fichiers avec find, locate et slocate

9.11. Retrouver un fichier partir dune liste demplacements possibles


Problme
Vous devez excuter ou lire un fichier, mais il peut se trouver en diffrents emplacements, inclus ou non dans votre variable $PATH.

Solution
Si vous voulez lire et excuter les commandes contenues dans un fichier qui se trouve dans lun des rpertoires mentionn dans la variable $PATH, invoquez tout simplement la commande source. Cette commande interne bash (galement connue sous le nom POSIX plus court mais plus difficile lire . ) examine la variable $PATH si loption sourcepath du shell est fixe, ce qui est le cas par dfaut :
$ source monFichier

Pour excuter un fichier uniquement sil existe dans la variable $PATH et quil est excutable, vous pouvez, avec bash version 2.05b ou ultrieure, utiliser la commande type -P pour effectuer une recherche dans $PATH. Contrairement la commande which, type -P affiche un rsultat uniquement si elle trouve le fichier. Elle est ainsi plus facile employer dans le cas suivant :
LS=$(type -P ls) [ -x $LS ] && $LS # --OU-LS=$(type -P ls) if [ -x $LS ]; then : commandes impliquant $LS fi

Si la recherche doit se faire dans diffrents emplacements, y compris ou non ceux de $PATH, utilisez une boucle for. Pour examiner le contenu de $PATH, servez-vous de loprateur de substitution de variables ${variable/motif/remplacement} afin de remplacer le sparateur : par une espace et passer le rsultat une instruction for normale. Pour effectuer une recherche dans $PATH et dautres emplacements, il suffit de les indiquer :
for chemin in ${PATH//:/ }; do [ -x "$chemin/ls" ] && $chemin/ls done # --OU-for chemin in ${PATH//:/ } /opt/foo/bin /opt/bar/bin; do [ -x "$chemin/ls" ] && $chemin/ls done

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

9.11. Retrouver un fichier partir dune liste demplacements possibles

203

Si le fichier ne se trouve pas dans les rpertoires de $PATH, mais sil peut tre dans une liste demplacements connus, prcisez les chemins et le nom complets :
for fichier in /usr/local/bin/inputrc /etc/inputrc ~/.inputrc; do [ -f "$fichier" ] && bind -f "$fichier" && break # Utiliser le # premier trouv. done

Ajoutez tous les tests supplmentaires ncessaires. Par exemple, pour invoquer screen lors de louverture de session uniquement si ce programme est prsent sur le systme, procdez de la manire suivante :
for chemin in ${PATH//:/ }; do if [ -x "$chemin/screen" ]; then # Si screen(1) existe et est excutable : for chemin in /opt/bin/settings/run_screen ~/settings/run_screen; do [ -x "$chemin" ] && $chemin && break # Excuter le # premier trouv. done fi done

Consultez la recette 16.20, page 416, pour de plus amples informations sur ce code.

Discussion
Une boucle for pour parcourir chaque emplacement possible peut sembler quelque peu exagre, mais cette solution savre trs souple. Elle permet deffectuer nimporte quelle recherche, dappliquer tous les tests appropris et de manipuler le fichier trouv comme bon vous semble. En remplaant : par une espace dans le contenu de $PATH, nous obtenons une liste spare par des espaces telle que lattend for (mais, comme nous lavons vu, toute liste spare par des espaces conviendra parfaitement). Vous pouvez adapter cette technique de manire crire des scripts shell trs souples, portables et tolrants vis--vis de lemplacement dun fichier. Vous pourriez tre tent de fixer la variable $IFS ':' pour analyser directement le contenu de $PATH au lieu de le prparer dans $chemin. Cette solution fonctionne mais demande un travail supplmentaire sur les variables et nest pas aussi souple. Vous pourriez pensez crire une ligne telle que la suivante :
[ "$(which monFichier)" ] && bind -f $(which monFichier)

Dans ce cas, un problme se pose lorsque le fichier nexiste pas. La commande which se comporte diffremment sur chaque systme. La version de Red Hat possde un alias pour fournir des dtails supplmentaires lorsque largument est un alias et pour appliquer diffrentes options de la ligne de commande. Par ailleurs, elle retourne un message indiquant que le fichier na pas t trouv (contrairement la version de which sur les systmes Debian ou FreeBSD). Si vous excutez cette ligne sur NetBSD, la commande bind reoit en argument la liste no monFichier in /sbin /usr/sbin /bin /usr/bin /usr/pkg/sbin /usr/pkg/bin /usr/X11R6/bin /usr/local/sbin /usr/local/bin. Ce nest pas vraiment ce que vous vouliez.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

204

Chapitre 9 Rechercher des fichiers avec find, locate et slocate

La commande command est galement intressante dans ce contexte. Elle existe depuis plus longtemps que type -P et peut savrer utile dans certains cas. Red Hat Enterprise Linux 4.x se comporte de la manire suivante :
$ alias which alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde' $ which rd alias rd='rmdir' /bin/rmdir $ which ls alias ls='ls --color=auto -F -h' /bin/ls $ which cat /bin/cat $ which cattt /usr/bin/which: no cattt in (/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/jp/bin ) $ command -v rd alias rd='rmdir' $ command -v ls alias ls='ls --color=auto -F -h' $ command -v cat /bin/cat

Sous Debian et FreeBSD (mais non NetBSD ou OpenBSD), le rsultat est lgrement diffrent :
$ alias which -bash3: alias: which: not found $ which rd $ which ls /bin/ls $ which cat /bin/cat $ which cattt $ command -v rd -bash: command: rd: not found

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

9.11. Retrouver un fichier partir dune liste demplacements possibles


$ command -v ls /bin/ls $ command -v cat /bin/cat $ command -v ll alias ll='ls -l'

205

Voir aussi
help type ; man which ; help source ; man source ; la recette 16.20, Commencer une configuration personnalise, page 416 ; la recette 17.4, Restaurer des sessions dconnectes avec screen, page 433.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

10
Autres fonctionnalits pour les scripts
De nombreux scripts sont crits pour un objectif prcis et ne sont utiliss que par leur auteur. Ils sont constitus de quelques lignes, voire mme dune seule boucle. En revanche, dautres scripts ont un dveloppement de niveau industriel et peuvent servir diffrents utilisateurs. Ce type de script doit souvent sappuyer sur des fonctionnalits qui permettent un meilleur partage et rutilisation du code. Ces techniques labores de dveloppement bnficient diffrentes types de scripts et se retrouvent dans les systmes de scripts plus vastes, comme le rpertoire /etc/init.d de nombreuses distributions Linux. Mme sans tre administrateur systme, vous pouvez apprcier et employer ces techniques. Elles profiteront tout dveloppement de scripts un tant soit peu complexes.

10.1. Convertir un script en dmon


Problme
Vous souhaitez quun script sexcute comme un dmon, cest--dire en arrire-plan et sans jamais sarrter. Pour que cela fonctionne, vous devez tre en mesure de dtacher le script de son terminal de contrle (tty) qui a servi lancer le dmon. Placer une esperluette la fin de la commande ne suffit pas. Si vous lancez le script dmon sur un systme distant via une session SSH (ou similaire), vous noterez que lors de la dconnexion, la session SSH ne se ferme pas et que la fentre reste bloque en lattente de la terminaison du script (ce qui ne se produit pas puisquil est un dmon).

Solution
Utilisez la commande suivante pour invoquer le script, lexcuter en arrire-plan et garder la possibilit de fermer votre session :
nohup monScriptDemon 0<&- 1>/dev/null 2>&1 & <&- &

Ou bien :
nohup monScriptDemon >>/var/log/myadmin.log 2>&1
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

208

Chapitre 10 Autres fonctionnalits pour les scripts

Discussion
Vous devez fermer le terminal de contrle (tty), qui est associ de trois manires toute tche (dont la vtre) : par lentre standard (STDIN), par la sortie standard (STDOUT) et par lerreur standard (STDERR). Pour fermer STDOUT et STDERR, vous pouvez les rediriger vers un autre fichier, en gnral un fichier de journalisation, afin de pouvoir examiner plus tard les rsultats du script, ou /dev/null, pour vous dbarrasser de la sortie gnre. Dans notre exemple, nous utilisons pour cela loprateur de redirection >. Mais, quen est-il de STDIN ? La solution la plus propre consiste fermer le descripteur de fichier de STDIN. Avec la syntaxe de bash, cette opration ressemble une redirection, mais le nom de fichier est remplac par un tiret (0<&- ou <&-). La commande nohup permet dexcuter le script sans quil soit interrompu par un signal darrt lors de la fermeture de la session. Dans le premier exemple, nous utilisons explicitement les numros de descripteur de fichier (0, 1 et 2) dans les trois redirections. Puisquils sont facultatifs pour STDIN et STDOUT, nous les avons omis dans le second exemple. Nous plaons galement la direction de lentre la fin de la deuxime commande plutt quau dbut, car lordre nest pas important ici. En revanche, pour la redirection de STDERR, lordre est important et les numros de descripteur de fichier sont indispensables.

Voir aussi
les chapitres 2 et 3 pour plus dinformations sur la redirection de lentre et de la sortie.

10.2. Rutiliser du code


Problme
Vous souhaitez que linitialisation de certaines variables du shell soit commune plusieurs scripts. Vous placez ces informations de configuration dans leur propre script. Lorsque que vous excutez ce script partir dun autre, les valeurs ne sont pas conserves. Linitialisation est ralise dans un autre shell et, lorsque celui-ci disparat, vos valeurs disparaissent galement. Existe-t-il un moyen dexcuter ce script de configuration au sein du shell courant ?

Solution
Utilisez la commande source du shell bash ou le point de POSIX (.) pour lire le contenu du fichier de configuration. Les lignes de ce fichier sont traites comme si elles se trouvaient dans le script en cours. Voici un exemple de donnes de configuration :
$ cat mesPrefs.cfg REP_TEMP=/var/tmp

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

10.2. Rutiliser du code


FORMAT_IMAGE=png FORMAT_AUDIO=ogg $

209

Ce script simple est constitu de trois affectations. En voici un autre qui utilise ces valeurs :
# # Utiliser les prfrences de lutilisateur. # source $HOME/mesPrefs.cfg cd ${REP_TEMP:-/tmp} echo Vous prfrez les fichiers dimage au format $FORMAT_IMAGE echo Vous prfrez les fichiers audio au format $FORMAT_AUDIO

Discussion
Le script qui sappuie sur le fichier de configuration invoque la commande source pour lire ce fichier. Vous pouvez galement remplacer le mot source par un point (.). Si le point est plus facile et plus rapide saisir, il est galement plus difficile remarquer dans un script ou une capture dcran :
. $HOME/mesPrefs.cfg

Vous ne serez pas le premier passer outre le point et penser que le script est tout simplement excut. bash propose galement une troisime syntaxe issue du processeur dentre readline, mais nous naborderons pas ce sujet ici. Sachez simplement que vous pouvez obtenir le mme rsultat avec la commande suivante :
$include $HOME/mesPrefs.cfg

Le fichier doit se trouver dans votre chemin de recherche (ou inclure un chemin explicite), possder les droits dexcution et de lecture. Le symbole dollar ne reprsente pas linvite de commande, mais fait partie de la directive $include. La commande source est une fonctionnalit puissante et dangereuse. Elle vous permet de crer un fichier de configuration et de le partager entre plusieurs scripts. Grce ce mcanisme, vous pouvez ajuster la configuration en ne modifiant quun seul fichier. Cependant, le contenu du fichier de configuration nest pas restreint des affectations de variables. Toute commande shell valide est accepte. En effet, lorsque vous lisez un fichier de cette manire, ses commandes sont interprtes par le shell bash. Quelles que soient les commandes places dans le fichier lu, par exemple des boucles ou linvocation dautres commandes, le shell les accepte et les excute comme si elles faisaient partie de votre script. Voici une version modifie du fichier de configuration :
$ cat mesPrefs.cfg REP_TEMP=/var/tmp FORMAT_IMAGE=$(cat $HOME/mesImages.pref) if [ -e /media/mp3 ] then FORMAT_AUDIO=mp3

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

210

Chapitre 10 Autres fonctionnalits pour les scripts

else FORMAT_AUDIO=ogg fi echo fichier de configuration charg $

Il va un peu plus loin quune simple configuration de variables. Il excute une autre commande (cat) et utilise une instruction if pour varier son droulement. Lorsque vous utilisez la commande source, vous devez avoir conscience quelle reprsente une porte grande ouverte dans votre script. Cette approche est trs utile lorsque vous dfinissez des fonctions bash (voir la recette 10.3, page 210). En effet, ces fonctions peuvent alors tre partages comme une bibliothque commune tous les scripts qui lisent le script de dfinition des fonctions.

Voir aussi
la page de manuel de bash pour en savoir plus sur readline ; la recette 10.3, Utiliser des fichiers de configuration dans un script, page 210 ; la recette 10.4, Dfinir des fonctions, page 211.

10.3. Utiliser des fichiers de configuration dans un script


Problme
Vous souhaitez disposer dun ou plusieurs fichiers de configuration pour un ou plusieurs scripts.

Solution
Vous pourriez crire un long code qui analyse un format propre aux fichiers de configuration, mais vitez cette approche. Transformez simplement le fichier de configuration en un script shell et utilisez la solution de la recette 10.2, page 208.

Discussion
Il sagit simplement dune application particulire de la lecture et de lexcution dun fichier. Cependant, il vaut mieux rf lchir la manire dassurer que la configuration correspond une syntaxe valide de bash. Vous pouvez, notamment, employer les indicateurs boolens et les variables facultatives (voir le chapitre 5, Variables du shell, et la recette 15.11, page 354).
# Dans le fichier de configuration. BAVARD=0 # '' pour inactif, 1 pour actif. UTILISATEUR_SSH='jbagadonutz@' # Noter le @ la fin, mettre '' pour # lutilisateur actuel.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

10.4. Dfinir des fonctions

211

# Dans le script. [ "$BAVARD" ] || echo "Les messages de $) vont sur STDERR" >&2 [...] ssh $UTILISATEUR_SSH$HOTE_DISTANT [...]

Cet exemple suppose que lutilisateur va lire les commentaires du script et crire ainsi des instructions de configuration valides. Cette hypothse nest pas vraiment fiable. Par consquent, au lieu de lui demander de consulter les notes et dajouter le caractre @ de fin, vous pourriez complter le script par les lignes suivantes :
# Si $UTILISATEUR_SSH est fixe et ne contient pas le @ final, lajouter : [ -n "$UTILISATEUR_SSH" -a "$UTILISATEUR_SSH" = "${UTILISATEUR_SSH%@}" ] && UTILISATEUR_SSH="$UTILISATEUR_SSH@"

Ou, plus simplement, en oprant directement la substitution :


ssh ${UTILISATEUR_SSH:+${UTILISATEUR_SSH}@}${HOTE_DISTANT} [...]

Loprateur :+ de bash fonctionne de la manire suivante. Si $UTILISATEUR_SSH contient une valeur, il retourne la valeur sa droite (dans ce cas, nous avons indiqu la variable elle-mme avec un @ supplmentaire). Si aucune valeur ne lui a t affecte ou si elle est vide, il ne retourne rien.

Voir aussi
le chapitre 5 ; la recette 10.2, Rutiliser du code, page 208 ; la recette 15.11, Obtenir lentre depuis une autre machine, page 354.

10.4. Dfinir des fonctions


Problme
plusieurs endroits de votre script, vous aimeriez fournir lutilisateur un message dutilisation (un message qui dcrit la bonne syntaxe de la commande), mais vous ne voulez pas reproduire plusieurs fois la mme instruction echo. Si vous placez le message dutilisation dans son propre script, vous pouvez linvoquer depuis nimporte quel endroit du script dorigine, mais il faut alors deux scripts. Existe-t-il un meilleur moyen dcrire le code une fois et de le rutiliser par la suite ?

Solution
Utilisez une fonction bash. Au dbut du script, ajoutez du code similaire au suivant :
function utilisation () { printf "usage : %s [ -a | - b ] }

fichier1 ... fichiern\n" $0

> &2

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

212

Chapitre 10 Autres fonctionnalits pour les scripts

Puis, diffrents endroits du script, vous pouvez invoquer la fonction qui affiche le message dutilisation :
if [ $# -lt 1] then utilisation fi

Discussion
Il existe diffrentes manires de dfinir des fonctions ([ function ] nom () commandecombine [ redirections ]). Voici plusieurs variantes de la dfinition dune fonction :
function utilisation () { printf "usage : %s [ -a | - b ] }

fichier1 ... fichiern\n" $0

> &2

function utilisation { printf "usage : %s [ -a | - b ] }

fichier1 ... fichiern\n" $0

> &2

usage () { printf "usage : %s [ -a | - b ] }

fichier1 ... fichiern\n" $0

> &2

utilisation () { printf "usage : %s [ -a | - b ] }

fichier1 ... fichiern\n" $0

> &2

Il faut au moins le mot rserv function ou la construction () finale. Si function est utilis, les parenthses () sont facultatives. Nous prfrons employer le mot function car il a lavantage dtre clair et lisible. Par ailleurs, il est facile rechercher. Par exemple, grep '^function' script affiche la liste des fonctions du script. La dfinition dune fonction doit se trouver au dbut du script shell, tout au moins avant lendroit o elle est invoque. Elle nest, en un sens, quune instruction bash normale. Cependant, une fois excute, la fonction est alors dfinie. Si vous invoquez la fonction avant de lavoir dfinie, vous recevrez une erreur du type commande non trouve . Cest pour cette raison que nous plaons toujours nos dfinitions de fonctions avant toute autre commande du script. Notre fonction contient simplement une instruction printf. Puisquil ny a quun seul message dutilisation afficher, nous navons pas modifier plusieurs instructions en cas, par exemple, dajout dune nouvelle option notre script. Outre la chane de format, le seul argument de printf est $0, cest--dire le nom dinvocation du script shell. Vous pouvez galement employer lexpression $(basename $0) afin que seule la dernire partie du nom de chemin soit incluse.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

10.5. Utiliser des fonctions : paramtres et valeur de retour

213

Puisque le message dutilisation constitue un message derreur, nous redirigeons la sortie de printf vers lerreur standard. Cette redirection peut tre place aprs la dfinition de la fonction afin que toute sortie quelle peut produire soit galement redirige :
function utilisation () { printf "usage : %s [ -a | - b ] } > &2

fichier1 ... fichiern\n" $0

Voir aussi
la recette 7.1, Rechercher une chane dans des fichiers, page 150 ; la recette 16.13, Concevoir une meilleure commande cd, page 396 ; la recette 16.14, Crer un nouveau rpertoire et y aller avec une seule commande, page 398 ; la recette 19.14, viter les erreurs commande non trouve avec les fonctions, page 502.

10.5. Utiliser des fonctions : paramtres et valeur de retour


Problme
Vous souhaitez utiliser une fonction, mais vous avez besoin de lui passer quelques valeurs. Comment pouvez-vous dfinir des paramtres la fonction ? Comment pouvezvous obtenir des valeurs en retour ?

Solution
Vous ne devez pas ajouter de parenthses autour des arguments, comme cest le cas dans dautres langages de programmation. Les paramtres dune fonction bash sont ajouts directement aprs le nom de la fonction, spars par des espaces, comme pour linvocation dun script shell ou dune commande. Noubliez pas les guillemets, si ncessaire !
# Dfinir la fonction : function max () { ... } # # Appeler la fonction : # max 128 $SIM max $VAR $TOTAL

Il existe deux manires de retourner des valeurs dune fonction. Vous pouvez les affecter des variables lintrieur du corps de la fonction. Elles seront globales au script, sauf si vous les dclarez explicitement locales (avec local) la fonction :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

214

Chapitre 10 Autres fonctionnalits pour les scripts

# bash Le livre de recettes : fonction_max.1 # Dfinir la fonction : function max () { local TEMPO if [ $1 -gt $2 ] then PLUS_GRAND=$1 else PLUS_GRAND=$2 fi TEMPO=5 }

Par exemple :
# Appeler la fonction : max 128 $SIM # Utiliser le rsultat : echo $PLUS_GRAND

Lautre solution sappuie sur les commandes echo ou printf pour afficher le rsultat sur la sortie standard. Dans ce cas, linvocation de la fonction doit se faire lintrieur dune construction $( ), en capturant la sortie et en utilisant le rsultat, ou bien il sera perdu sur lcran :
# bash Le livre de recettes : fonction_max.2 # Dfinir la fonction : function max () { if [ $1 -gt $2 ] then echo $1 else echo $2 fi }

Par exemple :
# Appeler la fonction : PLUS_GRAND=$(max 128 # Utiliser le rsultat echo $PLUS_GRAND $SIM)

Discussion
En ajoutant des paramtres dans linvocation de la fonction, elle ressemble un appel de script shell. Les paramtres sont simplement des mots sur la ligne de commande. Dans la fonction, les rfrences aux paramtres se font comme pour les arguments de la ligne de commande, cest--dire avec $1, $2, etc. En revanche, $0 nest pas affect. Il con-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

10.6. Intercepter les signaux

215

serve toujours le nom dinvocation du script. Au retour de la fonction, $1, $2, etc. contiennent nouveau les paramtres dappel du script. Nous devons galement mentionner le tableau $FUNCNAME. $FUNCNAME est en lui-mme une rfrence llment zro du tableau, qui contient le nom de la fonction en cours dexcution. Autrement dit, $FUNCNAME est aux fonctions ce que $0 est aux scripts, lexception de linformation de chemin. Les autres lments du tableau constituent une pile des appels, avec lappel principal en dernier lment. Cette variable nexiste que pendant lexcution dune fonction. $TEMPO illustre simplement la porte locale dune variable. Mme si nous pouvons lui affecter une valeur lintrieur de la fonction, cette valeur nest plus disponible dans les autres parties du script. Il sagit dune variable dont la valeur est locale la fonction. Elle dbute son existence lors de lappel la fonction et disparat lorsque la fonction se termine. Le retour de valeur au travers de variables est plus efficace et permet de grer un grand nombre de donnes (autant de variables que ncessaire), mais cette approche a ses inconvnients. Elle oblige la fonction et les autres parties du script saccorder sur les noms des variables. Cette forme de couplage conduit des problmes de maintenance. La deuxime approche, qui utilise la sortie pour envoyer des valeurs, allge ce couplage, mais son utilit est limite. En effet, le script doit faire beaucoup defforts pour analyser le rsultat de la fonction. Le choix entre ces deux mthodes est, comme toujours, une question de compromis et de besoins prcis.

Voir aussi
la recette 1.6, Protger la ligne de commande, page 12 ; la recette 16.4, Modifier temporairement $PATH, page 377.

10.6. Intercepter les signaux


Problme
Vous souhaitez crire un script qui intercepte les signaux et apporte une rponse adquate.

Solution
Utilisez trap pour dfinir des gestionnaires de signaux. Premirement, excutez trap -l (ou kill -l) pour obtenir la listes des signaux disponibles. Elle varie dun systme lautre :
# NetBSD $ trap -l 1) SIGHUP 5) SIGTRAP 9) SIGKILL 13) SIGPIPE

2) 6) 10) 14)

SIGINT SIGABRT SIGBUS SIGALRM

3) 7) 11) 15)

SIGQUIT SIGEMT SIGSEGV SIGTERM

4) 8) 12) 16)

SIGILL SIGFPE SIGSYS SIGURG

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

216
17) 21) 25) 29) SIGSTOP SIGTTIN SIGXFSZ SIGINFO 18) 22) 26) 30)

Chapitre 10 Autres fonctionnalits pour les scripts


SIGTSTP SIGTTOU SIGVTALRM SIGUSR1 19) 23) 27) 31) SIGCONT SIGIO SIGPROF SIGUSR2 20) 24) 28) 32) SIGCHLD SIGXCPU SIGWINCH SIGPWR

# Linux $ trap -l 1) SIGHUP 5) SIGTRAP 9) SIGKILL 13) SIGPIPE 18) SIGCONT 22) SIGTTOU 26) SIGVTALRM 30) SIGPWR 35) SIGRTMIN+2 39) SIGRTMIN+6 43) SIGRTMIN+10 47) SIGRTMIN+14 51) SIGRTMAX-13 55) SIGRTMAX-9 59) SIGRTMAX-5 63) SIGRTMAX-1

2) 6) 10) 14) 19) 23) 27) 31) 36) 40) 44) 48) 52) 56) 60) 64)

SIGINT SIGABRT SIGUSR1 SIGALRM SIGSTOP SIGURG SIGPROF SIGSYS SIGRTMIN+3 SIGRTMIN+7 SIGRTMIN+11 SIGRTMIN+15 SIGRTMAX-12 SIGRTMAX-8 SIGRTMAX-4 SIGRTMAX

3) 7) 11) 15) 20) 24) 28) 33) 37) 41) 45) 49) 53) 57) 61)

SIGQUIT SIGBUS SIGSEGV SIGTERM SIGTSTP SIGXCPU SIGWINCH SIGRTMIN SIGRTMIN+4 SIGRTMIN+8 SIGRTMIN+12 SIGRTMAX-15 SIGRTMAX-11 SIGRTMAX-7 SIGRTMAX-3

4) 8) 12) 17) 21) 25) 29) 34) 38) 42) 46) 50) 54) 58) 62)

SIGILL SIGFPE SIGUSR2 SIGCHLD SIGTTIN SIGXFSZ SIGIO SIGRTMIN+1 SIGRTMIN+5 SIGRTMIN+9 SIGRTMIN+13 SIGRTMAX-14 SIGRTMAX-10 SIGRTMAX-6 SIGRTMAX-2

Ensuite, dfinissez vos gestionnaires de signaux. Notez que le code de retour de votre script sera 128+numro de signal si la commande sest termine par le signal de ce numro. Voici un cas simple dans lequel seule la rception dun signal, quel quil soit, nous intresse. Si nous avions utilis uniquement trap '' ABRT EXIT HUP INT TERM QUIT, ce script aurait t trs difficile tuer car tous ces signaux seraient simplement ignors.
$ cat dur_a_tuer #!/bin/bash trap ' echo "Je suis mort ! $?" ' ABRT EXIT HUP INT TERM QUIT trap ' echo "Plus tard... $?"; exit ' USR1 sleep 120 $ ./dur_a_tuer ^Je suis mort ! 130 Je suis mort ! 130 $ ./dur_a_tuer & [1] 26354 $ kill -USR1 %1 Signal #1 dfini par lusager Plus tard... 158 Je suis mort ! 0 [1]+ Done ./dur_a_tuer $ ./dur_a_tuer & [1] 28180

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

10.6. Intercepter les signaux


$ kill %1 Je suis mort ! 0 [1]+ Terminated

217

./dur_a_tuer

Voici un exemple plus intressant :


#!/usr/bin/env bash # bash Le livre de recettes : dur_a_tuer function intercepte { if [ "$1" = "USR1" ]; then echo "Tu m'as eu avec un signal $1 !" exit else echo "J'ai vit ton signal $1 -- nah nah nre" fi } trap trap trap trap trap trap trap trap "intercepte "intercepte "intercepte "intercepte "intercepte "intercepte "intercepte "intercepte ABRT" EXIT" HUP" INT" KILL" QUIT" TERM" USR1" ABRT EXIT HUP INT KILL QUIT TERM USR1

# Celui-ci ne fonctionne pas.

# Celui-ci est particulier.

# Attendre sans rien faire, sans introduire un comportement annexe # avec les signaux, par exemple en utilisant 'sleep'. while (( 1 )); do : # : est une instruction qui ne fait rien. done

Nous invoquons le script prcdent et essayons de le tuer :


$ ./dur_a_tuer ^CJ'ai vit ton signal -- nah nah nre ^CJ'ai vit ton signal -- nah nah nre ^CJ'ai vit ton signal -- nah nah nre ^Z [1]+ Stopped ./dur_a_tuer $ kill -TERM %1 [1]+ Stopped ./dur_a_tuer J'ai vit ton signal TERM -- nah nah nre $ jobs [1]+ Stopped $ bg [1]+ ./dur_a_tuer &

./dur_a_tuer

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

218
$ jobs [1]+ Running

Chapitre 10 Autres fonctionnalits pour les scripts

./dur_a_tuer &

$ kill -TERM %1 J'ai vit ton signal TERM -- nah nah nre $ kill -HUP %1 J'ai vit ton signal HUP -- nah nah nre $ kill -USR1 %1 Tu m'as eu avec un signal USR1 ! J'ai vit ton signal EXIT -- nah nah nre [1]+ Done ./dur_a_tuer

Discussion
Tout dabord, vous devez savoir quil est impossible dintercepter le signal -SIGKILL (-9). Ce signal tue immdiatement les processus, sans que vous puissiez vous y opposer. Nos exemples ntaient donc pas rellement difficiles tuer. Cependant, noubliez pas que ce signal ne permet pas au script ou au programme de sarrter proprement, en faisant le mnage. Ce fonctionnement est gnralement dconseill et vous devez donc viter dutiliser kill -KILL, moins de navoir dautres solutions. trap semploie de la manire suivante :
trap [-lp] [arg] [signal [signal]]

Le premier argument, en dehors des options, indique trap le code excuter lorsque le signal prcis est reu. Comme vous lavez vu prcdemment, le code complet peut se trouver dans cet argument ou faire appel une fonction. Ds quil devient un tantinet complexe, il est prfrable dutiliser une ou plusieurs fonctions de traitement, puisque cela permet galement une terminaison propre. Si largument est une chane vide, le ou les signaux indiqus sont ignors. Si largument est - ou sil est absent, alors quun ou plusieurs signaux sont donns, ceux-ci sont rinitialiss leur traitement par dfaut du shell. -l affiche la liste des noms de signaux, comme nous lavons vu prcdemment, tandis que -p affiche les signaux courants et leurs gestionnaires. Si vous utilisez plusieurs gestionnaires de signaux, nous vous conseillons de trier par ordre alphabtique les noms des signaux car la lecture et la maintenance en sont alors facilites. Comme nous lavons signal, le code de sortie du script sera 128+numro de signal si la commande se termine pas le signal de numro indiqu. Il existe trois pseudo signaux jouant un rle particulier. Le signal DEBUG est similaire EXIT mais il est utilis avant chaque commande des fins de dbogage. Le signal RETURN est dclench lorsque lexcution reprend aprs un appel une fonction ou source (.). Le signal ERR apparat lorsquune commande choue. Consultez le manuel de rfrence de bash pour des informations dtailles et des mises en garde, notamment propos des fonctions qui utilisent la commande interne declare ou loption set -o functrace.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

10.7. Redfinir des commandes avec des alias

219

Vous devez savoir que POSIX conduit des diffrences dans le fonctionnement de trap. Comme le note le manuel de rfrence, le lancement de bash avec loption de ligne de commande --posix ou linvocation de set -o posix pendant que bash est en cours dexcution conduit un fonctionnement de bash plus conforme la norme POSIX 1003.2, notamment en modifiant son comportement dans les domaines o il diffre par dfaut des spcifications POSIX . Plus prcisment, les commandes kill et trap affichent alors uniquement les noms de signaux sans le prfixe SIG et la sortie de kill -l est diffrente. Par ailleurs, trap gre alors ses arguments de manire plus stricte, notamment en imposant un - initial pour rinitialiser les signaux leur traitement par dfaut du shell. Autrement dit, vous devez utiliser trap -USR1 et non simplement trap USR1. Nous vous conseillons dinclure systmatiquement le -, mme si ce nest pas ncessaire, car cela permet de clarifier les objectifs du code.

Voir aussi
help trap ; la recette 1.16, Documentation de bash, page 26 ; la recette 10.1, Convertir un script en dmon, page 207 ; la recette 14.11, Utiliser des fichiers temporaires scuriss, page 304 ; la recette 17.7, Effacer lcran lorsque vous vous dconnectez, page 438.

10.7. Redfinir des commandes avec des alias


Problme
Vous souhaitez modifier lgrement la dfinition dune commande, peut-tre pour inclure systmatiquement une option prcise (par exemple, toujours utiliser loption -a avec la commande ls ou -i avec rm).

Solution
Utilisez les alias de bash pour les shells interactifs (uniquement). La commande alias est suffisamment intelligente pour ne pas entrer dans une boucle infinie lorsque vous dfinissez lalias comme le suivant :
alias ls='ls -a'

En saisissant simplement alias, sans autres arguments, vous obtenez la liste des alias dfinis par dfaut dans la session bash. Sur certaines distributions, il est possible que les alias que vous cherchez dfinir le soit dj.

Discussion
Les alias fonctionnent par un remplacement direct du texte. Cette substitution se produit au tout dbut du traitement de la ligne de commande et toutes les autres substitu-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

220

Chapitre 10 Autres fonctionnalits pour les scripts

tions se font ensuite. Par exemple, si vous voulez que la lettre h soit un alias dune commande qui affiche le contenu de votre rpertoire personnel, saisissez la dfinition suivante :
alias h='ls $HOME'

Ou bien celle-ci :
alias h='ls ~'

Les apostrophes sont significatives dans la premire version, pour que la variable $HOME ne soit pas value lors de la dfinition de lalias. Ce nest que lors de lexcution de la commande que la substitution doit tre ralise et que la variable $HOME doit tre value. Si, par la suite, vous modifiez la dfinition de $HOME, lalias en tiendra compte. En remplaant les apostrophes par des guillemets, la substitution de la valeur de la variable se fait immdiatement et lalias contient la valeur de $HOME au moment de sa dfinition. Vous pouvez le constater en saisissant alias sans argument. bash affiche alors toutes les dfinitions dalias, notamment la suivante :
... alias h='ls /home/votreCompte' ...

Si les dfinitions de certains alias ne vous plaisent pas, vous pouvez les supprimer avec la commande unalias et le nom de lalias concern. Par exemple :
unalias h

Cette commande supprime notre dfinition prcdente. unalias -a retire toutes les dfinitions dalias dans la session du shell en cours. Et si quelquun avait cr un alias pour unalias ? La solution est trs simple. Il suffit de prfixer la commande par une barre oblique inverse et le dveloppement de lalias nest pas effectu : \unalias -a. Les alias nacceptent pas les arguments. Par exemple, la ligne suivante est invalide :
# Ne fonctionne PAS car les arguments ne sont PAS autoriss. $ alias='mkdir $1 && cd $1'

Les variables $1 et $HOME sont diffrentes car $HOME est dj dfinie (dune manire ou dune autre) lorsque lalias est lui-mme dfini, alors que $1 est suppos arriver lors de lexcution. Pour contourner ce problme, utilisez une fonction.

Voir aussi
lannexe C, Analyse de la ligne de commande, page 569, pour plus dinformations sur le traitement de la ligne de commande ; la recette 10.4, Dfinir des fonctions, page 211 ; la recette 10.5, Utiliser des fonctions : paramtres et valeur de retour, page 213 ; la recette 14.4, Effacer tous les alias, page 296 ; la recette 16.14, Crer un nouveau rpertoire et y aller avec une seule commande, page 398.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

10.8. Passer outre les alias ou les fonctions

221

10.8. Passer outre les alias ou les fonctions


Problme
Vous avez crit un alias ou une fonction pour remplacer une commande, mais vous souhaitez prsent excuter la version relle.

Solution
Utilisez la commande interne builtin de bash. Elle permet dignorer les fonctions et les alias du shell de manire excuter les commandes internes relles. La commande command joue le mme rle, mais pour les commandes externes. Si vous souhaitez contourner uniquement le dveloppement des alias, tout en gardant les dfinitions de fonctions, prfixez la commande par une barre oblique inverse (\). Servez-vous de la commande type (avec -a) pour savoir ce que vous faites. Voici quelques exemples :
$ alias echo='echo ~~~' $ echo test ~~~ test $ \echo test test $ builtin echo test test $ type echo echo is aliased to `echo ~~~' $ unalias echo $ type echo echo is a shell builtin $ type -a echo echo is a shell builtin echo is /bin/echo $ echo test test

Voici la dfinition dune fonction qui mritera quelques explications :


function cd () { if [[ $1 = "..." ]] then builtin cd ../..

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

222
else builtin cd $1 fi }

Chapitre 10 Autres fonctionnalits pour les scripts

Discussion
La commande alias est suffisamment labore pour ne pas entrer dans une boucle infinie lorsque crivez des dfinitions du type alias ls='ls -a' ou alias echo='echo ~~~'. Dans notre premier exemple, nous navons donc pas besoin dune syntaxe particulire sur le ct droit de la dfinition de lalias pour faire rfrence la commande echo relle. Lorsquun alias decho existe, la commande type nous indique non seulement quil sagit bien dun alias mais nous en montre galement la dfinition. De manire similaire, pour des dfinitions de fonctions, cette commande affichera le corps de la fonction. type -a une_commande affiche tout (alias, commandes internes, fonctions et programmes externes) ce qui contient une_commande (except lorsque loption -p est galement prsente). Dans notre dernier exemple, la fonction supplante la dfinition de cd afin de crer un raccourci simple. Nous souhaitons que la fonction interprte cd ... comme un dplacement vers deux niveaux suprieurs de rpertoire ; cest--dire cd ../.. (voir la recette 16.13, page 396). Les autres arguments conservent leur signification habituelle. Notre fonction recherche simplement une correspondance avec ... et remplace cet argument par sa signification relle. Mais, comment, depuis lintrieur de la fonction, pouvezvous invoquer la version relle de cd afin de changer de rpertoire ? La commande interne builtin demande bash de considrer que la commande en argument est celle dfinie en interne et non un alias ou une fonction. Nous nous en servons dans la fonction, mais elle peut tre utilise tout moment pour faire rfrence, de manire non ambigu, une commande relle et passer outre toute fonction qui pourrait la supplanter. Si le nom de votre fonction est celui dun programme excutable, comme ls, et non dune commande interne, vous pouvez contourner les dfinitions dalias et/ou de fonctions en prcisant le chemin complet de lexcutable, par exemple /bin/ls la place de ls. Si vous ne connaissez pas le nom de chemin complet, prfixez la commande par le motcl command et bash ignore toute dfinition dalias et de fonctions du nom de la commande et utilise la version relle. Cependant, sachez que la variable $PATH est toujours consulte pour dterminer lemplacement de la commande. Si la mauvaise version de ls est excute parce que votre $PATH contient des chemins inadquats, lajout de command ne sera daucune aide.

Voir aussi
help builtin ; help command ; help type ; la recette 14.4, Effacer tous les alias, page 296 ; la recette 16.13, Concevoir une meilleure commande cd, page 396.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

11
Dates et heures
La manipulation des dates et les heures devrait tre simple, mais ce nest absolument pas le cas. Que vous criviez un script shell ou un programme plus important, la gestion du temps savre complexe : diffrents formats daffichage de lheure et de la date, prise en charge des heures dt et dhiver, annes bissextiles, secondes intercalaires, etc. Par exemple, imaginez que vous disposiez dune liste de contrats et des dates auxquelles ils ont t signs. Vous aimeriez en calculer les dates dexpiration. Ce problme nest pas aussi simple quil y parat. Une anne bissextile intervient-elle entre les deux dates ? Les horaires dt et dhiver sont-ils importants dans ces contrats ? Quel format faut-il donner aux dates afin quelles ne soient pas ambigus ? 7/4/07 signifie-t-il 4 juillet 2007 ou 7 avril ? Les dates et les heures se rencontrent dans tous les aspects de linformatique. Tt ou tard, vous devrez les manipuler : dans les journaux du systme, dune application ou des transactions, dans les scripts de traitement des donnes, dans les tches utilisateur ou administratives, etc. Ce chapitre va vous aider les grer de manire aussi simple et nette que possible. Les ordinateurs conservent les dates de manire trs prcise, notamment lorsquils utilisent le protocole NTP (Network Time Protocol) pour rester synchroniser avec les valeurs nationales et internationales. Ils prennent galement bien en charge les passages aux horaires dt et dhiver en fonction des pays. Pour manipuler les dates dans un script shell, vous avez besoin de la commande Unix date (ou, mieux encore, de la version GNU de la commande date, disponible en standard avec Linux). date est capable dafficher des dates en diffrents formats et mme deffectuer correctement des calculs sur les dates. Notez que gawk (la version GNU de awk) emploie la mme mise en forme strftime que la commande date de GNU. Nous nallons pas dtailler ici lutilisation de gawk, mais nous verrons un exemple simple. Nous vous recommandons la variante GNU de date car elle est plus facile employer et dispose de lindispensable option -d. Mais noubliez pas gawk lorsque le systme dispose de cet outil mais pas de la version GNU de date.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

224

Chapitre 11 Dates et heures

11.1. Formater les dates en vue de leur affichage


Problme
Vous souhaitez mettre en forme des dates ou des heures avant de les afficher.

Solution
Utilisez la commande date avec une spcification de format strftime. Pour la liste des spcifications de format reconnues, consultez la section Mettre en forme la date et lheure avec strftime, page 544, ou la page de manuel de strftime.
# Dfinir des variables denvironnement est utile dans les scripts : # Voir le site http://greenwichmeantime.com/info/iso.htm $ STRICT_ISO_8601='%Y-%m-%dT%H:%M:%S%z' # Presque ISO-8601, mais dans une forme plus lisible. $ ISO_8601='%Y-%m-%d %H:%M:%S %Z' $ ISO_8601_1='%Y-%m-%d %T %Z' # Format adapt aux noms de fichiers. $ DATE_FICHIER='%Y%m%d%H%M%S' $ date "+$ISO_8601" 2006-05-08 14:36:51 CEST gawk "BEGIN {print strftime(\"$ISO_8601\")}" 2006-12-07 04:38:54 CEST # Identique $ISO_8601. $ date '+%Y-%m-%d %H:%M:%S %Z' 2006-05-08 14:36:51 CEST $ date -d '2005-11-06' "+$ISO_8601" 2005-11-06 00:00:00 CEST $ date "+Programme dmarr : $ISO_8601" Programme dmarr : 2006-05-08 14:36:51 CEST $ printf "%b" "Programme dmarr : $(date '+$ISO_8601')\n" Programme dmarr : $ISO_8601 $ echo "Je peux renommer le fichier ainsi : \ > mv fic.log fic_$(date +$DATE_FICHIER).log" Je peux renommer le fichier ainsi : mv fic.log fic_20060508143724.log # %T quivaut %H:%M:%S

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

11.2. Fournir une date par dfaut

225

Discussion
Vous pourriez tre tent de placer le caractre + dans la variable denvironnement afin de simplifier ensuite la commande. Sur certains systmes, la commande date est assez pointilleuse quant lexistence et au placement du +. Nous vous conseillons donc de lajouter explicitement la commande date elle-mme. Il existe dautres options de mise en forme. Pour en connatre la liste complte, consultez la page de manuel de date ou celle de la fonction C strftime() (man 3 strftime). Sauf mention contraire, le fuseau horaire correspond celui dfini par votre systme. Le format %z est une extension non standard disponible dans la version GNU de la commande date ; elle peut ne pas tre reconnue par votre systme. Le format recommand pour laffichage des dates et des heures est le format ISO 8601. Vous devez lutiliser autant que possible. Voici ses avantages par rapport aux autres formats daffichage : il sagit dun standard reconnu ; il est dpourvu de toute ambigut ; il est facile lire tout en restant simple analyser par programme (par exemple avec awk ou cut) ; il est tri de manire adquate dans les donnes en colonne ou dans les noms de fichiers.

Essayez dviter les formats MM/JJ/AA ou JJ/MM/AA, ou pire encore M/J/AA ou J/M/AA. Leur tri nest pas adapt, ils sont ambigus, puisque le jour ou le mois peut arriver en premier en fonction du pays, et difficiles analyser. De mme, utilisez de prfrence un format horaire sur 24 heures afin dviter dautres problmes dambigut et danalyse.

Voir aussi
man date ; http://www.cl.cam.ac.uk/~mgk25/iso-time.html ; http://www.qsl.net/g1smd/isopdf.htm ; http://greenwichmeantime.com/info/iso.htm ; http://195.141.59.67/iso/fr/prods-services/popstds/datesandtime.html ; la section Mettre en forme la date et lheure avec strftime, page 544.

11.2. Fournir une date par dfaut


Problme
Vous souhaitez que votre script fournisse une date par dfaut sense et invite lutilisateur la vrifier.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

226

Chapitre 11 Dates et heures

Solution
En utilisant la commande date de GNU, affectez la date probable une variable, puis faites en sorte que lutilisateur puisse la corriger si ncessaire :
#!/usr/bin/env bash # bash Le livre de recettes : date_par_defaut # Utiliser midi afin d'viter que le script ne s'excute aux environs # de minuit avec un dcalage de quelques secondes qui peuvent conduire # des erreurs d'une journe. DATE_DEBUT=$(date -d 'last week Monday 12:00:00' '+%Y-%m-%d') while [ 1 ]; do printf "%b" "La date de dbut est le $DATE_DEBUT, est-ce correct? (O/autre date) " read reponse # Toute valeur autre que Entre, "O" ou "o" est comprise comme # une nouvelle date. On pourrait utiliser "[Oo]*" pour accepter # la saisie de "oui". La vrification de la nouvelle date se # fait avec le format CCYY-MM-DD. case "$reponse" in [Oo]) break ;; [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]) printf "%b" "$DATE_DEBUT est remplace par $reponse\n" DATE_DEBUT="$reponse" ;; *) esac done DATE_FIN=$(date -d "$DATE_DEBUT +7 days" '+%Y-%m-%d') echo "DATE_DEBUT: $DATE_DEBUT" echo "DATE_FIN: $DATE_FIN" printf "%b" "Date invalide, veuillez recommencer...\n" ;;

Discussion
Si elle est reconnue par la version GNU de date, loption -d nest pas universellement prise en charge. Nous vous conseillons dobtenir et dutiliser la version GNU, si possible. Vous devez retirer le code de vrification si votre script sexcute sans surveillance ou un moment dtermin (par exemple, partir de cron). Pour plus dinformations sur la mise en forme des dates et des heures, consultez la recette 11.1, page 224.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

11.3. Calculer des plages de dates

227

Nous utilisons un code similaire celui-ci dans des scripts qui gnrent des requtes SQL. Le script sexcute une heure donne et cre une requte SQL pour une plage de dates prcise afin de gnrer un rapport.

Voir aussi
man date ; la recette 11.1, Formater les dates en vue de leur affichage, page 224 ; la recette 11.3, Calculer des plages de dates, page 227.

11.3. Calculer des plages de dates


Problme
Vous disposez dune premire date (peut-tre issue de la recette 11.2, page 225) et vous souhaitez gnrer automatiquement la seconde.

Solution
La commande date de GNU est trs puissante et adaptable, mais les possibilits de son option -d ne sont pas trs bien documentes. Sa documentation se trouve peut-tre dans la page de manuel de getdate. Voici quelques exemples :
$ date '+%Y-%m-%d %H:%M:%S %z' 2005-11-05 01:03:00 -0500 $ date -d 'today' '+%Y-%m-%d %H:%M:%S %z' 2005-11-05 01:04:39 -0500 $ date -d 'yesterday' '+%Y-%m-%d %H:%M:%S %z' 2005-11-04 01:04:48 -0500 $ date -d 'tomorrow' '+%Y-%m-%d %H:%M:%S %z' 2005-11-06 01:04:55 -0500 $ date -d 'Monday' '+%Y-%m-%d %H:%M:%S %z' 2005-11-07 00:00:00 -0500 $ date -d 'this Monday' '+%Y-%m-%d %H:%M:%S %z' 2005-11-07 00:00:00 -0500 $ date -d 'last Monday' '+%Y-%m-%d %H:%M:%S %z' 2005-10-31 00:00:00 -0500 $ date -d 'next Monday' '+%Y-%m-%d %H:%M:%S %z' 2005-11-07 00:00:00 -0500

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

228
$ date -d 'last week' '+%Y-%m-%d %H:%M:%S %z' 2005-10-29 01:05:24 -0400 $ date -d 'next week' '+%Y-%m-%d %H:%M:%S %z' 2005-11-12 01:05:29 -0500 $ date -d '2 weeks' '+%Y-%m-%d %H:%M:%S %z' 2005-11-19 01:05:42 -0500 $ date -d '-2 weeks' '+%Y-%m-%d %H:%M:%S %z' 2005-10-22 01:05:47 -0400 $ date -d '2 weeks ago' '+%Y-%m-%d %H:%M:%S %z' 2005-10-22 01:06:00 -0400 $ date -d '+4 days' '+%Y-%m-%d %H:%M:%S %z' 2005-11-09 01:06:23 -0500 $ date -d '-6 days' '+%Y-%m-%d %H:%M:%S %z' 2005-10-30 01:06:30 -0400

Chapitre 11 Dates et heures

$ date -d '2000-01-01 +12 days' '+%Y-%m-%d %H:%M:%S %z' 2000-01-13 00:00:00 -0500 $ date -d '3 months 1 day' '+%Y-%m-%d %H:%M:%S %z' 2006-02-06 01:03:00 -0500

Discussion
Loption -d permet dindiquer une date prcise la place de maintenant, mais elle nest pas reconnue par toutes les commandes date. La version GNU la prend en charge et nous vous conseillons de linstaller et de lemployer autant que possible. Lutilisation de loption -d peut tre complexe. Les arguments suivants fonctionnent comme attendu :
$ date '+%a %Y-%m-%d' sam 2005-11-05 $ date -d 'today' '+%a %Y-%m-%d' sam 2005-11-05 $ date -d 'Saturday' '+%a %Y-%m-%d' sam 2005-11-05 $ date -d 'last Saturday' '+%a %Y-%m-%d' sam 2005-10-29 $ date -d 'this Saturday' '+%a %Y-%m-%d' sam 2005-11-05

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

11.3. Calculer des plages de dates

229

En revanche, si vous excutez la commande suivante le samedi, vous nobtenez pas le samedi suivant mais la date du jour mme :
$ date -d 'next Saturday' '+%a %Y-%m-%d' sam 2005-11-05

Faites attention galement this week et aux JOURS, car sils font rfrence au pass, la semaine en cours devient la semaine suivante. Si vous invoquez la commande ci-dessous le samedi 5 novembre 2005, vous obtenez un rsultat sans doute diffrent de ce que vous attendiez :
$ date -d 'this week Friday' '+%a %Y-%m-%d' ven 2005-11-11

Loption -d peut savrer extrmement utile, mais vous devez tester votre code et inclure le contrle derreur adquat. Si vous ne disposez pas de la version GNU de date, vous serez sans doute intress par les cinq fonctions dcrites dans larticle Shell Corner: Date-Related Shell Functions du magazine UnixReview du mois de septembre 2005 : pn_month Le xme mois avant ou aprs le mois indiqu. end_month La fin du mois indiqu. pn_day Le xme jour avant ou aprs le jour indiqu. cur_weekday Le jour de la semaine correspondant au jour indiqu. pn_weekday Le xme de la semaine avant ou aprs le jour indiqu. Les fonctions suivantes ont t ajoutes peu avant la publication de cet ouvrage : pn_day_nr (Non rcursive) Le xme jour avant ou aprs le jour indiqu. days_between Nombre de jours entre deux dates. Notez que pn_month, end_month et cur_weekday sont indpendantes des autres fonctions. En revanche, pn_day sappuie sur pn_month et end_month, tandis que pn_weekday repose sur pn_day et cur_weekday.

Voir aussi
man date ; man getdate ; http://www.unixreview.com/documents/s=9884/ur0509a/ur0509a.html ; http://www.unixlabplus.com/unix-prog/date_function/ ; la recette 11.2, Fournir une date par dfaut, page 225.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

230

Chapitre 11 Dates et heures

11.4. Convertir des dates et des heures en secondes depuis lorigine


Problme
Vous souhaitez convertir une date et une heure en secondes depuis lorigine (epoch) afin de faciliter les calculs arithmtiques sur les dates et les heures.

Solution
Utilisez la commande date de GNU avec loption -d et un format %s standard :
# Pour "maintenant", cest facile. $ date '+%s' 1131172934 # Les autres dates ont besoin de loption non standard -d. $ date -d '2005-11-05 12:00:00 +0000' '+%s' 1131192000

Discussion
Si vous ne disposez pas de la version GNU de la commande date, le problme est plus difficile rsoudre. Nous vous conseillons donc dinstaller et dutiliser la version GNU de date. Si cela ne vous est pas possible, vous serez peut-tre en mesure demployer Perl. Voici trois manires dafficher linstant prsent en secondes depuis lorigine :
$ perl -e 'print time, qq(\n);' 1154158997 # Identique la prcdente. $ perl -e 'use Time::Local; print timelocal(localtime( )) . qq(\n);' 1154158997 $ perl -e 'use POSIX qw(strftime); print strftime("%s", localtime( )) . qq(\n);' 1154159097

La structure de donnes des dates et des heures en Perl complique la conversion dune date autre que linstant prsent. Les annes dbutent en 1900 et les mois (mais pas les jours) commencent zro et non un. Le format de la commande est timelocal(sec, min, heure, jour, mois-1, anne-1900). Par consquent, voici comment convertir linstant 2005-11-05 06:59:49 en secondes depuis lorigine :
# Lheure indique est une heure locale. $ perl -e 'use Time::Local; print timelocal("49", "59", "06", "05", "10", "105") . qq(\n);' 1131191989

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

11.5. Convertir des secondes depuis lorigine en dates et heures


# Lheure indique est une heure UTC. $ perl -e 'use Time::Local; print timegm("49", "59", "06", "05", "10", "105") . qq(\n);' 1131173989

231

Voir aussi
man date ; la recette 11.5, Convertir des secondes depuis lorigine en dates et heures, page 231 ; la section Mettre en forme la date et lheure avec strftime, page 544.

11.5. Convertir des secondes depuis lorigine en dates et heures


Problme
Vous souhaitez convertir des secondes depuis lorigine en une date et une heure lisibles.

Solution
Utilisez la commande date de GNU avec lun des formats de la recette 11.1, page 224 :
ORIGINE='1131173989' $ date -d "1970-01-01 UTC $ORIGINE seconds" +"%Y-%m-%d %T %z" 2005-11-05 01:59:49 -0500 $ date --utc --date "1970-01-01 $ORIGINE seconds" +"%Y-%m-%d %T %z" 2005-11-05 06:59:49 +0000

Discussion
Puisque les secondes correspondent simplement une valeur depuis lorigine (fixe au 1er janvier 1970 minuit, ou 1970-01-01T00:00:00), cette commande dbute lorigine, ajoute les secondes de lorigine et affiche la date et lheure au format indiqu. Sans la version GNU de date, vous pouvez essayer les lignes de commandes Perl suivantes :
ORIGINE='1131173989' $ perl -e "print scalar(gmtime($ORIGINE)), qq(\n);" Sat Nov 5 06:59:49 2005 $ perl -e "print scalar(localtime($ORIGINE)), qq(\n);" Sat Nov 5 01:59:49 2005 # UTC

# Lheure locale.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

232

Chapitre 11 Dates et heures

$ perl -e "use POSIX qw(strftime); print strftime('%Y-%m-%d %H:%M:%S', localtime($ORIGINE)), qq(\n);" 2005-11-05 01:59:49

Voir aussi
man date ; la recette 11.1, Formater les dates en vue de leur affichage, page 224 ; la recette 11.4, Convertir des dates et des heures en secondes depuis lorigine, page 230 ; la section Mettre en forme la date et lheure avec strftime, page 544.

11.6. Dterminer hier ou demain en Perl


Problme
Vous avez besoin de la date dhier ou de demain. La version GNU de date nest pas installe sur votre systme, mais vous disposez de Perl.

Solution
Utilisez la commande Perl suivante, en ajustant le nombre de secondes ajoutes ou soustraites de time :
# Hier, la mme heure (noter la soustraction). $ perl -e "use POSIX qw(strftime); print strftime('%Y-%m-%d', localtime(time - 86400)), qq(\n);" # Demain, la mme heure (noter laddition). $ perl -e "use POSIX qw(strftime); print strftime('%Y-%m-%d', localtime(time + 86400)), qq(\n);"

Discussion
Il sagit simplement dune version particulire des recettes prcdentes, mais elle est tellement classique quelle mritait dtre mentionne sparment. La recette 11.7, page 233, contient un tableau de valeurs qui pourra vous tre utile.

Voir aussi
la recette 11.2, Fournir une date par dfaut, page 225 ; la recette 11.3, Calculer des plages de dates, page 227 ; la recette 11.4, Convertir des dates et des heures en secondes depuis lorigine, page 230 ; la recette 11.5, Convertir des secondes depuis lorigine en dates et heures, page 231 ; la recette 11.7, Calculer avec des dates et des heures, page 233 ; la section Mettre en forme la date et lheure avec strftime, page 544.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

11.7. Calculer avec des dates et des heures

233

11.7. Calculer avec des dates et des heures


Problme
Vous souhaitez effectuer des calculs arithmtiques sur des dates et des heures.

Solution
Si vous ne pouvez obtenir la rponse adquate laide de la commande date (voir la recette 11.3, page 227), convertissez les dates et les heures existantes en secondes depuis lorigine (voir la recette 11.4, page 230), effectuez vos calculs, puis convertissez les secondes rsultantes au format souhait (voir la recette 11.5, page 231).
Si la version GNU de date nexiste pas sur votre systme, vous pouvez vous tourner vers les fonctions du shell dcrites dans larticle Shell Corner: Date-Related Shell Functions de Unix Review de septembre 2005 (voir la recette 11.3, page 227).

Par exemple, supposons que vous ayez des donnes de journalisation provenant dune machine dont lhorloge est dcale. Aujourdhui, le protocole NTP (Network Time Protocol) est largement utilis et cette situation ne devrait donc pas se produire, mais faisons malgr tout cette hypothse :
CORRECTION='172800' # 2 jours en secondes.

# Placer ici le code qui extrait la partie date des donnes dans # la variable $date_erronee. # Supposer que la date est la suivante : date_erronee='Jan 2 05:13:05' # Date au format syslog. # Convertir la date en secondes depuis lorigine avec date de GNU : origine_erronee=$(date -d "$date_erronee" '+%s') # Appliquer la correction. origine_correcte=$(( origine_erronee + $CORRECTION )) # Convertir la date corrige en un format lisible. date_correcte=$(date -d "1970-01-01 UTC $origine_correcte seconds") # GNU Date. date_correcte_iso=$(date -d "1970-01-01 UTC $origine_correcte seconds" +'%Y-%m-%d %T') echo echo echo echo echo "Date errone : "Origine errone : "Correction : "Origine valide : "Date valide : $date_erronee" $origine_erronee" +$CORRECTION" $origine_correcte" $date_correcte"

# GNU Date.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

234
echo "Date valide ISO : $date_correcte_iso"

Chapitre 11 Dates et heures

# Placer ici le code pour rinsrer $date_correcte dans les donnes.


Attention aux annes ! Certaines commandes Unix, comme ls et syslog tentent de produire une sortie plus facile lire et omettent, dans certains cas, lanne. Vous devrez prendre en compte cet aspect lors du calcul du facteur de correction. Si les dates sont dans un intervalle important ou correspondent des fuseaux horaires diffrents, vous devrez placer les donnes dans des fichiers spars et les traiter individuellement.

Discussion
Pour effectuer des calculs arithmtiques sur des dates, il est beaucoup plus facile demployer les secondes coules depuis lorigine que nimporte quel autre format. En effet, vous navez alors pas vous occuper des heures, des jours, des semaines ou des annes, mais simplement additionner ou soustraire des valeurs. Cette approche vite galement des oprations complexes dues aux annes bissextiles, aux secondes intercalaires et aux fuseaux horaires. Le tableau 11-1 liste quelques valeurs qui pourraient vous servir. Tableau 11-1. Table de conversion des principales valeurs depuis lorigine
Secondes 60 300 600 3 600 18 000 36 000 86 400 172 800 604 800 1 209 600 2 592 000 31 536 000 Minutes 1 5 10 60 300 600 1 440 2 880 10 080 20 160 43 200 525 600 1 5 10 24 48 168 336 720 8 760 1 2 7 14 30 365 Heures Jours

Voir aussi
http://www.jpsdomain.org/networking/time.html ; la recette 11.3, Calculer des plages de dates, page 227 ; la recette 11.4, Convertir des dates et des heures en secondes depuis lorigine, page 230 ; la recette 11.5, Convertir des secondes depuis lorigine en dates et heures, page 231 ; la recette 13.12, Extraire certains champs des donnes, page 273.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

11.8. Grer les fuseaux horaires, les horaires dt et les annes bissextiles

235

11.8. Grer les fuseaux horaires, les horaires dt et les annes bissextiles
Problme
Vous devez tenir compte des fuseaux horaires, des horaires dt et dhiver, ainsi que des annes bissextiles ou des secondes intercalaires.

Solution
Nous vous le dconseillons fortement. Ces paramtres demandent une gestion beaucoup plus complexe quil ny parat. Laissez-la au code existant et test depuis des annes. Optez pour un outil qui est en mesure de rpondre vos besoins. Il est fort probable que lune des recettes de ce chapitre ait dj prsent une solution adquate, probablement base sur la version GNU de date. Dans le cas contraire, il existe trs certainement un outil qui fera laffaire. Par exemple, de nombreux modules Perl permettent de manipuler des dates et des heures. Nous ne plaisantons pas. Il est extrmement compliqu de prendre en compte ces particularits des dates. vitez-vous dintenses maux de tte en utilisant simplement un outil adapt.

Voir aussi
la recette 11.1, Formater les dates en vue de leur affichage, page 224 ; la recette 11.3, Calculer des plages de dates, page 227 ; la recette 11.4, Convertir des dates et des heures en secondes depuis lorigine, page 230 ; la recette 11.5, Convertir des secondes depuis lorigine en dates et heures, page 231 ; la recette 11.7, Calculer avec des dates et des heures, page 233.

11.9. Utiliser date et cron pour excuter un script au nme jour


Problme
Vous devez excuter un script un certain jour de chaque mois (par exemple, le deuxime mercredi), mais les outils cron ne vous offrent pas cette possibilit.

Solution
Ajoutez un peu de code shell la commande excute. Dans la crontab de Vixie Cron pour Linux, adaptez lune des lignes suivantes. Si vous vous servez dun autre programme cron, vous devrez sans doute convertir les noms des jours de la semaine en nombres

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

236

Chapitre 11 Dates et heures

conformes au modle de votre version de cron (06 ou 17) et utiliser +%w (jour de la semaine en version numrique) la place de +%a (nom de la semaine abrg dpendant des paramtres rgionaux) :
# Vixie Cron # Min Heure JduM Mois JdelaS Programme # 0-59 0-23 1-31 1-12 0-7 # excuter le premier mercredi 23:00. 00 23 1-7 * Wed [ "$(date '+%a')" == "mer" ] && /chemin/de/la/commande arguments de la commande # excuter le deuxime jeudi 23:00. 00 23 8-14 * Thu [ "$(date '+%a')" == "jeu" ] && /chemin/de/la/commande # excuter le troisime vendredi 23:00. 00 23 15-21 * Fri [ "$(date '+%a')" == "ven" ] && /chemin/de/la/commande # excuter le quatrime samedi 23:00. 00 23 22-27 * Sat [ "$(date '+%a')" == "sam" ] && /chemin/de/la/commande # excuter le cinquime dimanche 23:00. 00 23 28-31 * Sun [ "$(date '+%a')" == "dim" ] && /chemin/de/la/commande
Notez quun jour de la semaine nest pas systmatiquement prsent cinq fois dans un mois. Vous devez donc bien rflchir ce que vous souhaitez faire avant de planifier une tche pour la cinquime semaine du mois.

Discussion
La plupart des versions de cron (y compris Vixie Cron pour Linux) ne permettent pas de planifier une tche pour le Nme jour du mois. Pour contourner ce problme, nous prvoyons lexcution de la tche lintrieur de la plage de jours qui inclut le Nme jour. Ensuite, nous vrifions si le jour courant correspond celui du lancement de la tche. Le deuxime mercredi du mois se trouve entre le 8e et le 14e jour du mois. Nous planifions donc lexcution de la tche tous les jours de cet intervalle, mais vrifions que le jour courant est bien un mercredi. Dans ce cas, la commande est excute. Le tableau 11-2 prsente les intervalles employs prcdemment. Tableau 11-2. Intervalles des semaines dun mois
Semaine Premire Deuxime Troisime Quatrime Cinquime (voir lavertissement) Plages de jours 17 8 14 15 21 22 27 28 31

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

11.9. Utiliser date et cron pour excuter un script au nme jour


Si cela vous parat trop simple, consultez un calendrier pour vous convaincre :
$ cal 10 2006 octobre 2006 lu ma me je ve sa di 2 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

237

Voir aussi
man 5 crontab ; man cal.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

12
Tches utilisateur sous forme de scripts shell
Jusqu prsent, vous avez t confront un grand nombre de petits scripts et de formes syntaxiques. Par ncessit, les exemples avaient une porte et une taille limites. Nous voudrions maintenant vous prsenter des exemples plus vastes, mais qui ne sont pas pour autant plus longs. Il sagit dexemples de scripts shell rels qui ne se limitent pas aux tches dadministration systme. Vous les trouverez probablement utiles et pratiques. Par ailleurs, leur tude vous permettra den apprendre plus sur bash et vous les adapterez peut-tre vos propres besoins.

12.1. Afficher une ligne de tirets


Problme
Afficher une ligne de tirets avec une commande simple peut sembler facile (cest le cas). Mais, ds quun script simple est crit, il a tendance vouloir grandir. Vous voulez varier la longueur de la ligne de tirets. Vous voulez que le tiret puisse tre remplac par un caractre indiqu par lutilisateur. Ltendue des fonctionnalits peut ainsi augmenter trs facilement. Pouvez-vous crire un script simple qui tient compte de toutes ces extensions sans devenir trop complexe ?

Solution
Envisagez le script suivant :
1 2 3 4 5 6 7 8 9 #!/usr/bin/env bash # bash Le livre de recettes : tirets # tirets - affiche une ligne de tirets # options : # longueur de la ligne (72 par dfaut) # -c X utiliser le caractre X la place du tiret # function utiliserquitter () { printf "usage : %s [-c X] [#]\n" $(basename $0)

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

240
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 exit 2 } >&2

Chapitre 12 Tches utilisateur sous forme de scripts shell

LONGUEUR=72 CARACTERE='-' while (( $# > 0 )) do case $1 in [0-9]*) LONGUEUR=$1;; -c) shift CARACTERE=$1;; *) utiliserquitter;; esac shift done if (( LONGUEUR > 4096 )) then echo "trop long" >&2 exit 3 fi # Construire la chane la longueur exacte. TIRETS="" for ((i=0; i<LONGUEUR; i++)) do TIRETS="${TIRETS}${CARACTERE}" done printf "%s\n" "$TIRETS"

Discussion
Le cur du script consiste construire une chane compose du nombre demand de tirets (ou du caractre indiqu) et lafficher sur la sortie standard (STDOUT). Cela ne demande que six lignes de code (3035). Les lignes 12 et 13 fixent des valeurs par dfaut. Toutes les autres lignes concernent lanalyse des arguments, le contrle des erreurs, les messages dutilisation et les commentaires. Tous ces aspects sont indispensables dans les scripts destins lutilisateur final. Moins de 20 % du code ralise plus de 80 % du travail. Cependant, ce sont ces 80 % du code qui font que le script est robuste et facile dutilisation. la ligne 9, nous utilisons basename pour retirer le chemin lors de laffichage du nom du script. Ainsi, quelle que soit la manire dont lutilisateur a invoqu le script (par exemple, ./tirets, /home/nom_utilisateur/bin/tirets ou mme ../../par/ici/tirets), le message dutilisation affiche uniquement tirets. Lanalyse des arguments se fait tant quil en reste sur la ligne de commande (ligne 14). Lorsquun argument a t trait, une instruction shift dcrmente le nombre darguments restants et la boucle while finit par se terminer. Seuls deux arguments sont accepts : un nombre prcisant la longueur de la chane (ligne 17) et une option -c sui-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

12.2. Prsenter des photos dans un album

241

vie dun caractre (lignes 1819). Tout autre argument (ligne 20) conduit laffichage du message dutilisation et la terminaison du script. Nous pourrions tre plus prcis dans le traitement de -c et de son argument. Sans une analyse plus sophistique (par exemple base sur getopt ; voir la recette 13.1, page 257), loption et son argument doivent tre spars par une espace. Pour lexcution du script, vous devez crire -c n et non -cn. Par ailleurs, nous ne vrifions pas que le deuxime argument est bien prsent, ni son contenu ; il peut trs bien tre une chane ou une seule lettre. (Pouvez-vous imaginer une manire simple de traiter ce cas, en prenant uniquement le premier caractre de largument ? Avez-vous besoin de le prendre en compte ? Pourquoi ne pas laisser lutilisateur indiquer une chane la place dun caractre ?) Lanalyse de largument numrique pourrait galement sappuyer sur des techniques plus labores. Les motifs dune instruction case se conforment aux rgles de lexpansion des noms de chemin et ne sont en aucun cas des expressions rgulires. Vous pourriez croire que le motif [0-9]* de case reprsente uniquement des chiffres, mais cette signification est celle des expressions rgulires. Dans une instruction case, il indique simplement une chane qui commence par un chiffre. En ninterceptant pas les entres invalides, comme 9.5 ou 612etplus, les erreurs surgissent plus loin dans le script. Une instruction if avec une expression rgulire plus sophistique serait bien utile ici. Enfin, vous aurez remarqu que le script fixe, en ligne 24, une taille maximum, mme si elle est totalement arbitraire. Devez-vous conserver ou supprimer cette contrainte ? partir de cet exemple, vous pouvez constater que mme les scripts simples peuvent devenir plus complexes, principalement cause de la gestion des erreurs, de lanalyse des arguments et des autres oprations connexes. Pour les scripts utiliss uniquement par leur auteur, la gestion de ces aspects peut tre rduite ou mme omise. En effet, en tant que seul utilisateur de script, son crateur en connat le bon usage et accepte tout message derreur en cas de dysfonctionnement. En revanche, si les scripts doivent tre partags avec dautres utilisateurs, il est important de faire des efforts pour les rendre plus robustes et faciles dutilisation.

Voir aussi
la recette 5.8, Parcourir les arguments dun script, page 96 ; la recette 5.11, Compter les arguments, page 101 ; la recette 5.12, Extraire certains arguments, page 103 ; la recette 6.15, Analyser les arguments de la ligne de commande, page 139 ; la recette 13.1, Analyser les arguments dun script, page 257.

12.2. Prsenter des photos dans un album


Problme
Vous avez enregistr toutes les photos de votre appareil numrique dans un rpertoire et vous souhaitez disposer dun moyen rapide et facile de les visualiser toutes afin de ne conserver que les plus russies.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

242

Chapitre 12 Tches utilisateur sous forme de scripts shell

Solution
crivez un script shell qui gnre un ensemble de pages HTML permettant de visualiser vos photos dans un navigateur. Nommez ce script creer_album et placez-le dans un rpertoire comme ~/bin. Depuis la ligne de commande, utilisez cd pour aller dans le rpertoire o lalbum doit tre cr (par exemple celui des photos). Ensuite, excutez les commandes qui vont gnrer la liste des photos inclure dans cet album (par exemple, ls *.jpg, mais consultez galement la recette 9.5, page 195) et dirigez leur sortie vers le script creer_album (voir ci-aprs). Vous devez indiquer le nom de lalbum (cest--dire le nom dun rpertoire qui sera cr par le script) sur la ligne de commande en seul argument au script shell. Voici un exemple dinvocation :
$ ls *.jpg | creer_album matchRugby

La figure 12-1 prsente un exemple de la page web gnre.

Figure 12-1. Exemple de page web produite par creer_album Le titre correspond la photo (le nom de son fichier). Des liens hypertextes permettent daller aux autres pages (premire, prcdente, suivante et dernire). Voici le script shell (creer_album) qui gnre les pages HTML, une pour chaque image (les numros de ligne ne font pas partie du script, mais simplifient les explications) :
1 2 3 4 5 #!/usr/bin/env bash # bash Le livre de recettes : creer_album # creer_album - cre un "album" HTML partir des fichiers de photos. # ver. 0.2 #

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

12.2. Prsenter des photos dans un album

243

6 # Un album est un rpertoire de pages HTML. Il est cr dans le 7 # rpertoire de travail. 8 # 9 # Une page de l'album contient le code HTML permettant d'afficher 10 # une photo, avec un titre (le nom du fichier de la photo) et des 11 # liens hypertextes pour la premire photo, la prcdente, la suivante 12 # et la dernire. 13 # 14 # AFFICHER_ERREUR 15 AFFICHER_ERREUR() 16 { 17 printf "%b" "$@" 18 } >&2 19 20 # 21 # USAGE 22 USAGE() 23 { 24 AFFICHER_ERREUR "usage : %s <nouv_rp>\n" $(basename $0) 25 } 26 27 # GENERER(cetteph, premph, precph, suivph, dernph) 28 GENERER() 29 { 30 CETTEPH="../$1" 31 PREMPH="${2%.*}.html" 32 PRECPH="${3%.*}.html" 33 SUIVPH="${4%.*}.html" 34 DERNPH="${5%.*}.html" 35 if [ -z "$3" ] 36 then 37 LIGNEPREC='<TD> Pr&eacute;c&eacute;dente </TD>' 38 else 39 LIGNEPREC='<TD> <A HREF="'$PRECPH'"> Pr&eacute;c&eacute;dente </A> </TD>' 40 fi 41 if [ -z "$4" ] 42 then 43 LIGNESUIV='<TD> Suivante </TD>' 44 else 45 LIGNESUIV='<TD> <A HREF="'$SUIVPH'"> Suivante </A> </TD>' 46 fi 47 cat <<EOF 48 <HTML> 49 <HEAD><TITLE>$CETTEPH</TITLE></HEAD> 50 <BODY> 51 <H2>$CETTEPH</H2> 52 <TABLE WIDTH="25%"> 53 <TR> 54 <TD> <A HREF="$PREMPH"> Premi&egrave;re </A> </TD>

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

244

Chapitre 12 Tches utilisateur sous forme de scripts shell

55 $LIGNEPREC 56 $LIGNESUIV 57 <TD> <A HREF="$DERNPH"> Derni&egrave;re </A> </TD> 58 </TR> 59 </TABLE> 60 <IMG SRC="$CETTEPH" alt="$CETTEPH" 61 BORDER="1" VSPACE="4" HSPACE="4" 62 WIDTH="800" HEIGHT="600"/> 63 </BODY> 64 </HTML> 65 EOF 66 } 67 68 if (( $# != 1 )) 69 then 70 USAGE 71 exit -1 72 fi 73 ALBUM="$1" 74 if [ -d "${ALBUM}" ] 75 then 76 AFFICHER_ERREUR "Le rpertoire [%s] existe dj.\n" ${ALBUM} 77 USAGE 78 exit -2 79 else 80 mkdir "$ALBUM" 81 fi 82 cd "$ALBUM" 83 84 PREC="" 85 PREM="" 86 DERN="derniere" 87 88 while read PHOTO 89 do 90 # Le dpart. 91 if [ -z "${ENCOURS}" ] 92 then 93 ENCOURS="$PHOTO" 94 PREM="$PHOTO" 95 continue 96 fi 97 98 FICHIERPH=$(basename "${ENCOURS}") 99 GENERER "$ENCOURS" "$PREM" "$PREC" "$PHOTO" "$DERN" > "${FICHIERPH%.*}.html" 100 101 # Prparer l'itration suivante. 102 PREC="$ENCOURS" 103 ENCOURS="$PHOTO"

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

12.2. Prsenter des photos dans un album


104 105 done 106 107 FICHIERPH=$(basename ${ENCOURS}) 108 GENERER "$ENCOURS" "$PREM" "$PREC" "" "$DERN" > "${FICHIERPH%.*}.html" 109 110 # Crer le lien pour "dernire" 111 ln -s "${FICHIERPH%.*}.html" ./derniere.html 112 113 # Crer un lien pour index.html. 114 ln -s "${PREM%.*}.html" ./index.html

245

Discussion
Mme sil existe de nombreux outils gratuits ou bon march pour la visualisation des photos, lutilisation de bash pour construire un album photo simple permet dillustrer la puissance de la programmation shell et constitue un bon exemple de travail. Le script commence (ligne 1) par le commentaire spcial qui prcise lexcutable servant lancer ce script. Ensuite, quelques commentaires dcrivent lobjectif du script. Mme les commentaires les plus courts ont une importance lorsque vous devez, trois jours ou 13 mois plus tard, vous rappeler le fonctionnement du script. Aprs les commentaires, nous avons plac les dfinitions des fonctions. La fonction AFFICHER_ERREUR (lignes 1518) se comporte de manire trs similaire printf (puisquelle invoque simplement printf) mais en redirigeant sa sortie sur lerreur standard. Ainsi, vous ntes pas oblig de rediriger la sortie pour chaque message derreur. En gnral, la redirection est place la fin de la commande. Dans notre cas, nous lajoutons la fin de la dfinition de la fonction (ligne 18) pour indiquer bash de rediriger toutes les sorties manant de cette fonction. Mme sil nest pas absolument ncessaire de la placer dans une fonction spare, la fonction USAGE (lignes 2225) est une bonne manire de documenter lutilisation de votre script. Au lieu de figer le nom du script dans le message dutilisation, nous prfrons employer la variable spciale $0, par exemple pour le cas o le script changerait de nom. Puisque $0 contient le nom du script au moment de son invocation, elle peut galement inclure le nom de chemin complet utilis pour invoquer le script (par exemple, /usr/local/bin/creer_album). Ce chemin se retrouve alors dans le message dutilisation. Grce la commande basename (ligne 24), nous retirons cette partie. La fonction GENERER (lignes 2866) est plus longue. Son rle est de gnrer le code HTML pour chaque page de lalbum, qui se trouve dans sa propre page web (statique), avec des liens hypertextes vers la premire image, limage prcdente, limage suivante et la dernire image. La fonction GENERER nest pas complexe. Elle reoit les noms de toutes les images relier. Elle prend ces noms et les convertit en noms de pages, ce qui, dans notre script, consiste remplacer lextension du fichier de limage par html. Par exemple, si $2 contient le nom de fichier pict001.jpg, le rsultat de ${2%.*}.html est pict001.html.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

246

Chapitre 12 Tches utilisateur sous forme de scripts shell

Puisque le code HTML produire est court, nous nutilisons pas une suite dinstructions printf, mais une commande cat avec un here document (ligne 47). Nous pouvons ainsi saisir littralement le code HTML dans le script, ligne aprs ligne, tout en gardant lexpansion des variables du shell. La commande cat recopie simplement (concatne) STDIN sur STDOUT. Dans notre script, nous redirigeons STDIN de manire ce que les lignes de texte qui suivent, cest--dire le here document, constituent lentre. En ne plaant pas le mot de fin dentre entre apostrophes (simplement EOF et non 'EOF' ou \EOF), bash effectue la substitution des variables sur les lignes dentre. Nous pouvons donc utiliser des noms de variables bass sur nos paramtres pour les diffrents titres et liens hypertextes. Nous aurions pu passer un nom de fichier la fonction GENERER et lui demander de rediriger sa propre sortie vers ce fichier. Mais une telle redirection nest pas vraiment logique dans une fonction de gnration (contrairement AFFICHER_ERREUR dont le seul objectif est la redirection). Le rle de GENERER est de crer le contenu HTML. La destination de ce contenu ne la concerne pas. Puisque bash nous permet de rediriger trs facilement la sortie, il est possible de procder en plusieurs tapes. Par ailleurs, le dbogage est plus facile lorsque la mthode affiche sa sortie sur STDOUT. Les deux dernires commandes du script (lignes 111 et 114) crent des liens symboliques servant de raccourcis vers la premire et la dernire photo. Ainsi, le script na pas besoin de dterminer le nom de la premire et de la dernire page de lalbum. Il utilise simplement des noms figs, index.html et derniere.html, lors de la gnration des autres pages de lalbum. Puisque le dernier fichier trait correspond la dernire photo de lalbum, il suffit de crer un lien vers ce fichier. La premire photo est traite de manire similaire. Mme si nous connaissons son nom ds le dbut, nous attendons la fin du script pour regrouper la cration des deux liens symboliques. Ce nest quune question de style, pour que les oprations de mme type restent ensemble.

Voir aussi
http://www.w3schools.com/ ; HTML & XHTML La rfrence, 6e dition de Chuch Musciano et Bill Kennedy (dition OReilly) ; la recette 3.2, Conserver les donnes avec le script, page 60 ; la recette 3.3, Empcher un comportement trange dans un here document, page 61 ; la recette 3.4, Indenter un here document, page 63 ; la recette 5.13, Obtenir des valeurs par dfaut, page 104 ; la recette 5.14, Fixer des valeurs par dfaut, page 105 ; la recette 5.18, Modifier certaines parties dune chane, page 109 ; la recette 5.19, Utiliser les tableaux, page 111 ; la recette 9.5, Retrouver des fichiers sans tenir compte de la casse, page 195 ; la recette 16.9, Crer son rpertoire priv dutilitaires, page 389.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

12.3. Charger votre lecteur MP3

247

12.3. Charger votre lecteur MP3


Problme
Vous disposez dun ensemble de fichiers MP3 que vous aimeriez placer sur votre lecteur MP3. Cependant, le nombre des fichiers dpasse la capacit de votre lecteur. Comment pouvez-vous recopier vos fichiers audio sans avoir surveiller chaque copie pour dterminer si le lecteur est plein ou non ?

Solution
Utilisez un script shell qui vrifiera la capacit disponible lors de la copie des fichiers sur le lecteur MP3 et qui sarrte lorsquil est plein.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #!/usr/bin/env bash # bash Le livre de recettes : charger_mp3 # Remplit un lecteur MP3 avec le maximum de titres possible. # N.B.: on suppose que le lecteur MP3 est mont sur /media/mp3. # # # Dterminer la taille d'un fichier. # function TAILLE_FICHIER () { NF=${1:-/dev/null} if [[ -e $NF ]] then # FZ=$(ls -s $NF | cut -d ' ' -f 1) set -- $(ls -s "$NF") FZ=$1 fi } # # Calculer l'espace disponible sur le lecteur MP3. # function ESPACE_LIBRE { # LIBRE=$(df /media/mp3 | awk '/^\/dev/ {print $4}') set -- $(df /media/mp3 | grep '^/dev/') LIBRE=$4 } # Soustraire la TAILLE_FICHIER (donne) de l'espace disponible (global). function DIMINUER () (( LIBRE-=${1:-0}))

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

248
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

Chapitre 12 Tches utilisateur sous forme de scripts shell


# # Code principal : # let SOMME=0 let COMPTE=0 export FZ export LIBRE ESPACE_LIBRE find . -name '*.mp3' -print | \ (while read NOM_CHEMIN do TAILLE_FICHIER "$NOM_CHEMIN" if ((FZ <= LIBRE)) then echo charger $NOM_CHEMIN cp "$NOM_CHEMIN" /media/mp3 if (( $? == 0 )) then let SOMME+=FZ let COMPTE++ DIMINUER $FZ else echo "erreur de copie de $NOM_CHEMIN sur /media/mp3" rm -f /media/mp3/$(basename "$NOM_CHEMIN") # Recalculer car on ne sait pas combien a t copi. ESPACE_LIBRE fi # Une raison de poursuivre ? if (( LIBRE <= 0 )) then break fi else echo sauter $NOM_CHEMIN fi done printf "%d chansons (%d blocs) ont t charges" $COMPTE $SOMME printf " sur /media/mp3 (%d blocs libres)\n" $LIBRE ) # Fin du script.

Discussion
Invoquez ce script et il copie tous les fichiers MP3 qui se trouvent dans le rpertoire de travail et ses sous-rpertoires vers un lecteur MP3 (ou un autre priphrique) mont sur /media/mp3. Le script tente de dterminer lespace disponible sur le priphrique avant deffectuer la copie, puis il soustrait la taille des lments copis de celle du disque afin de savoir quand sarrter (cest--dire lorsque le priphrique est plein ou aussi plein que possible).

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

12.3. Charger votre lecteur MP3


Linvocation du script est simple :
$ charger_mp3

249

Vous pouvez vous asseoir et le regarder copier les fichiers ou bien aller prendre un caf (selon la rapidit des critures dans la mmoire de votre lecteur MP3). Examinons quelques fonctionnalits de bash employes par ce script. Commenons la ligne 35, aprs les commentaires et les dfinitions de fonctions. Nous reviendrons par la suite aux fonctions. Le corps principal du script shell commence par initialiser des variables (lignes 3839) et en exporte certaines afin quelles soient disponible globalement. la ligne 42, nous invoquons la fonction ESPACE_LIBRE pour dterminer lespace disponible sur le lecteur MP3 avant de dbuter la copie des fichiers. La ligne 43 montre la commande find qui retrouve tous les fichiers MP3 (en ralit, uniquement ceux dont les noms se terminent par .mp3 ). Cette information est envoye une boucle while, qui dbute la ligne 44. Pourquoi la boucle while est-elle place entre des parenthses ? Les instructions lintrieur des parenthses seront excutes dans un sous-shell. Mais, ce qui nous intresse ici, cest de regrouper linstruction while avec les instructions printf suivantes (lignes 71 et 72). Puisque chaque instruction dun tube est excute dans son propre sous-shell et puisque la commande find envoie sa sortie vers la boucle while, aucun comptage effectu lintrieur de la boucle while nest donc disponible en dehors de cette boucle. En plaant des instructions while et printf dans un sous-shell, elles sont excutes dans le mme environnement et peuvent partager des variables. tudions le contenu de la boucle while :
46 47 48 49 50 51 52 TAILLE_FICHIER "$NOM_CHEMIN" if ((FZ <= LIBRE)) then echo charger $NOM_CHEMIN cp "$NOM_CHEMIN" /media/mp3 if (( $? == 0 )) then

Pour chaque nom de fichier lu (depuis la sortie de la commande find), elle invoque la fonction TAILLE_FICHIER pour dterminer la taille de ce fichier (voir ci-aprs pour lexplication de cette fonction). Ensuite, elle vrifie (ligne 47) si le fichier est plus petit que lespace disque restant, autrement dit sil peut tre copi. Si cest le cas, elle affiche le nom du fichier puis le copie (ligne 50) sur le lecteur MP3. Il est important de vrifier que la copie sest bien passe (ligne 51). La variable $? contient le rsultat de la commande prcdente, cest--dire de cp. Si la copie a russi, nous dduisons alors sa taille de lespace disponible sur le lecteur MP3. En revanche, si elle a chou, nous devons essayer de supprimer la copie (car, si elle se trouve sur le lecteur, elle est incomplte). Loption -f de rm nous permet dviter les messages derreur si le fichier na pas t cr. Nous recalculons ensuite lespace disponible afin que les comptes soient bons. En effet, la copie peut avoir chou cause dune mauvaise estimation (lespace disponible ntait pas suffisant). Dans la partie principale du script, les trois instructions if (lignes 47, 51 et 63) utilisent les doubles parenthses autour de lexpression. Ces trois instructions if sont numri-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

250

Chapitre 12 Tches utilisateur sous forme de scripts shell

ques et nous voulions utiliser les oprateurs classiques (<= et ==). Les mmes conditions if auraient pu tre testes avec des expressions entre crochets ([), mais les oprateurs auraient alors t -le et -eq. Nous employons une forme diffrente de linstruction if la ligne 13, dans la fonction TAILLE_FICHIER. Vous devons y vrifier lexistence du fichier (dont le nom se trouve dans la variable $NF). Ce test est plus simple crire avec loprateur -e, mais celui nest pas disponible dans les instructions if de style arithmtique (cest--dire, avec des parenthses la place des crochets). Examinons prsent la fonction DIMINUER et son expression arithmtique :
32 33 function DIMINUER () (( LIBRE-=${1:-0}))

En gnral, les fonctions emploient des accolades pour dlimiter leurs corps. Cependant, en bash, toute instruction composite fait laffaire. Dans ce cas, nous choisissons les doubles parenthses de lvaluation arithmtique, puisque la fonction na que cette opration effectuer. Quelle que soit la valeur fournie sur la ligne de commande dinvocation de DIMINUER, elle sera le premier paramtre positionnel (cest--dire, $1). Nous soustrayons simplement cette valeur de $LIBRE. Cest pour cette raison que nous avons employ la syntaxe des expressions arithmtiques (pour pouvoir utiliser loprateur -=). Voyons de plus prs deux lignes de la fonction TAILLE_FICHIER :
16 17 set -- $(ls -s "$NF") FZ=$1

Il se passe beaucoup de choses dans ces quelques caractres. Tout dabord, la commande ls est excute dans un sous-shell (la construction $( )). Loption -s de ls nous donne la taille, en blocs, du fichier ainsi que son nom. La sortie de la commande est retourne sous forme de mots sur la ligne de commande de set. Le rle de cette commande est ici danalyser les mots contenus dans la sortie de ls. Il existe plusieurs manires de procder, mais vous pouvez retenir cette technique. Linstruction set -- prend les mots restants sur la ligne de commande et en fait les nouveaux paramtres positionnels. Si vous crivez set -- voici un petit test, alors $1 vaut voici et $3 vaut petit. Les prcdentes valeurs de $1, $2, etc., sont perdues. Cependant, la ligne 12, nous avons enregistr dans $NF le seul paramtre pass la fonction. Nous pouvons ainsi rutiliser les paramtres positionnels. Nous demandons au shell den effectuer lanalyse notre place. Nous disposons donc de la taille du fichier dans la variable $1 (ligne 17). Cela dit, dans cet exemple, puisque lopration est effectue lintrieur dune fonction, seuls les paramtres positionnels de la fonction sont modifis, non ceux de linvocation du script. Nous demandons nouveau au shell deffectuer une analyse notre place (ligne 27) :
27 28 set -- $(df /media/mp3 | grep '^/dev/') LIBRE=$4

La sortie de la commande df indique la taille, en bloc, disponible sur le priphrique. Nous la passons grep, car nous voulons conserver uniquement les informations sur le priphrique, sans les lignes den-tte affiches par df. Aprs que bash a dtermin les arguments, nous pouvons rcuprer la taille de lespace libre sur le priphrique dans le paramtre $4.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

12.4. Graver un CD

251

Le commentaire en ligne 26 montre une autre manire danalyser la sortie de la commande df. Nous pouvons lenvoyer awk et laisser celui-ci effectuer lanalyse :
26 # LIBRE=$(df /media/mp3 | awk '/^\/dev/ {print $4}')

Grce lexpression place entre les barres obliques, nous demandons awk de prendre uniquement en compte les lignes commenant par /dev. Le symbole ^ ancre la recherche au dbut de la ligne et la barre oblique inverse chappe la signification de la barre oblique. Ainsi, lexpression de recherche ne se finit pas ce point et le premier caractre trouver est une barre oblique. Quelle solution devez-vous choisir ? Elles impliquent toutes deux linvocation dun programme externe (grep ou awk). En gnral, il existe plusieurs manires de faire la mme chose (en bash, comme dans la vie) et le choix vous revient. Daprs notre exprience, nous vous conseillons dopter pour la premire solution qui vous vient lesprit.

Voir aussi
man df ; man grep ; man awk ; la recette 10.4, Dfinir des fonctions, page 211 ; la recette 10.5, Utiliser des fonctions : paramtres et valeur de retour, page 213 ; la recette 19.8, Oublier que les tubes crent des sous-shells, page 493.

12.4. Graver un CD
Problme
Lun de vos rpertoires est rempli de fichiers que vous souhaitez graver sur un CD. Devez-vous acheter un logiciel commercial coteux ou pouvez-vous employer le shell et quelques programmes Open Source ?

Solution
Vous avez simplement besoin de deux logiciels Open Source, mkisofs et cdrecord, et dun script bash pour fournit les options adquates. Commencez par placer tous les fichiers copier sur le CD dans une structure arborescente. Le script va lire ce rpertoire, en crer une image au format ISO, puis la graver sur le CD. Vous avez simplement besoin dun espace disque suffisant et dun peu de temps.
Il est possible que ce script ne fonctionne pas sur votre systme. Nous vous le prsentons comme un exemple et non comme un programme oprationnel pour lenregistrement et la sauvegarde sur un CD.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

252
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

Chapitre 12 Tches utilisateur sous forme de scripts shell


#!/usr/bin/env bash # bash Le livre de recettes : script_cd # script_cd - prpare et grave un CD partir d'un rpertoire. # # usage : script_cd rp [ pri_cd ] # if [[ $# < 1 || $# > 2 ]] then echo 'usage : script_cd rp [ pri_cd ]' exit 2 fi # Fixer les valeurs par dfaut. REPSRC=$1 # Votre priphrique peut tre "ATAPI:0,0,0" ou autre. PERICD=${2:-"ATAPI:0,0,0"} IMAGEISO=/tmp/cd$$.iso echo "construire l'image ISO..." # # Crer le systme de fichiers ISO. # mkisofs $ISOPTS -A "$(cat ~/.cdAnnotation)" \ -p "$(hostname)" -V "$(basename $REPSRC)" \ -r -o "$IMAGEISO" $REPSRC ETAT=$? if [ $ETAT -ne 0 ] then echo "Erreur. Impossible de crer l'image ISO." echo "Recherchez la raison, puis supprimez $IMAGEISO." exit $ETAT fi echo "image ISO termine ; gravure du CD..." exit # Graver le CD. VITESSE=8 OPTS="-eject -v fs=64M driveropts=burnproof" cdrecord $OPTS -speed=$VITESSE dev=${PERICD} $IMAGEISO ETAT=$? if [ $ETAT -ne 0 ] then echo "Erreur. Impossible de graver le CD." echo "Recherchez la raison, puis supprimez $IMAGEISO." exit $ETAT fi 48 rm -f $IMAGEISO echo "termin."

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

12.4. Graver un CD

253

Discussion
Examinons les constructions les plus complexes de ce script. Voici la ligne 17 :
17 IMAGEISO=/tmp/cd$$.iso

Nous construisons un nom de fichier temporaire bas sur la variable $$, qui contient le numro de notre processus. Tant que ce script est en cours dexcution, il sera le seul avoir ce numro. Nous obtenons donc un nom unique parmi tous les autres processus en cours dexcution. (Voyez la recette 14.11, page 304, pour une meilleure solution.) la ligne 26, nous conservons le code dtat de la commande mkisofs. Les commandes Unix ou Linux bien crites (ainsi que les scripts bash) retournent 0 en cas de succs et une valeur diffrente de zro en cas dchec. Nous aurions pu utiliser uniquement la variable $? dans linstruction if de la ligne de 27, mais nous voulons conserver ltat de la commande mkisofs pour que, en cas derreur, nous puissions la renvoyer comme valeur de retour du script (ligne 31). Nous procdons de mme avec la commande cdrecord et sa valeur de retour (lignes 4147). Les lignes 2325 mritent sans doute quelques explications :
23 24 25 mkisofs $ISOPTS -A "$(cat ~/.cdAnnotation)" \ -p "$(hostname)" -V "$(basename $REPSRC)" \ -r -o "$IMAGEISO" $REPSRC

Ces trois lignes reprsentent une seule ligne de commande de bash. Elles ont t places sur plusieurs lignes en ajoutant une barre oblique inverse en dernier caractre afin dchapper la signification normale dune fin de ligne. Veillez donc ne mettre aucune espace aprs le caractre \ final. Cette ligne de commande invoque trois sous-shells dont la sortie permet dobtenir la version finale de la commande mkisofs. Tout dabord, le programme cat est appel afin dafficher le contenu du fichier .cdAnnotation, qui se trouve dans le rpertoire personnel (~/) de lutilisateur ayant lanc ce script. Lide est de fournir une chane loption -A, qui, daprs la page de manuel de mkisofs, est une chane crite dans len-tte du volume . De manire similaire, loption -p attend une autre chane, qui donne le nom du prparateur de limage. Dans cet exemple, nous utilisons le nom de la machine sur laquelle le script est dmarr, en invoquant hostname dans un sous-shell. Enfin, le nom du volume est indiqu par le paramtre -V et nous choisissons le nom du rpertoire dans lequel se trouvent les fichiers. Puisque ce rpertoire est prcis sur la ligne de commande du script, mais quil inclut probablement un nom de chemin complet, nous utilisons basename pour retirer cette partie. Par exemple, /usr/local/donnees devient simplement donnees).

Voir aussi
la recette 14.11, Utiliser des fichiers temporaires scuriss, page 304.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

254

Chapitre 12 Tches utilisateur sous forme de scripts shell

12.5. Comparer deux documents


Problme
Il est facile de comparer deux documents textuels (voir la recette 17.10, page 441). Mais quen est-il des documents crs partir des logiciels bureautiques ? Puisquils ne sont pas enregistrs comme du texte, comment pouvez-vous les comparer ? Vous disposez de deux versions du mme document et vous souhaitez connatre les modifications apportes au contenu. Existe-t-il une solution autre que limpression des deux documents et leur comparaison page par page ?

Solution
Tout dabord, utilisez un logiciel de bureautique qui vous permet denregistrer vos documents au format ODF (Open Document Format). Cest le cas des suites telles que OpenOffice. Dautres produits commerciaux devraient normalement reconnatre bientt ce format. Une fois en possession des fichiers ODF, vous pouvez vous servir dun script pour comparer uniquement leur contenu. Nous insistons sur le terme contenu car les diffrences de mise en forme constituent un autre problme et, en gnral, le contenu est llment le plus important pour lutilisateur. Voici un script bash qui permet de comparer deux fichiers OpenOffice enregistrs au format ODF (utilisez lextension conventionnelle odt pour indiquer que le document est de type texte et non une feuille de calcul ou une prsentation).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #!/usr/bin/env bash # bash Le livre de recettes : diff_oo # diff_oo -- compare le CONTENU de deux fichiers OpenOffice. # Ne fonctionne qu'avec des fichiers .odt. # function usage_quitter () { echo "usage : $0 fichier1 fichier2" echo "les deux fichiers doivent tre de type .odt." exit $1 } >&2 # Vrifier que les deux arguments sont des noms de fichiers # lisibles se terminant par .odt. if (( $# != 2 )) then usage_quitter 1 fi if [[ $1 != *.odt || $2 != *.odt ]] then usage_quitter 2 fi if [[ ! -r $1 || ! -r $2 ]] then

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

12.5. Comparer deux documents


25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 usage_quitter 3 fi BASE1=$(basename "$1" .odt) BASE2=$(basename "$2" .odt) # Les dcompresser dans des rpertoires privs. PRIV1="/tmp/${BASE1}.$$_1" PRIV2="/tmp/${BASE2}.$$_2" # Les rendre absolus. ICI=$(pwd) if [[ ${1:0:1} == '/' ]] then COMPLET1="${1}" else COMPLET1="${ICI}/${1}" fi if [[ ${2:0:1} == '/' ]] then COMPLET2="${2}" else COMPLET2="${ICI}/${2}" fi # Crer les zones de travail et vrifier le succs. # N.B. il faut des espaces autour de { et de } et # un ; la fin des listes dans {}. mkdir "$PRIV1" || { echo Impossible de crer $PRIV1 ; exit 4; } mkdir "$PRIV2" || { echo Impossible de crer $PRIV2 ; exit 5; } cd "$PRIV1" unzip -q "$COMPLET1" sed -e 's/>/>\ /g' -e 's/</\ </g' content.xml > contentwnl.xml cd "$PRIV2" unzip -q "$COMPLET2" sed -e 's/>/>\ /g' -e 's/</\ </g' content.xml > contentwnl.xml cd $ICI diff "${PRIV1}/contentwnl.xml" "${PRIV2}/contentwnl.xml" rm -rf $PRIV1 $PRIV2

255

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

256

Chapitre 12 Tches utilisateur sous forme de scripts shell

Discussion
Pour crire ce script, il fallait savoir que les fichiers OpenOffice sont enregistrs sous forme de fichiers ZIP Si vous les dcompressez, vous obtenez un ensemble de fichiers XML . qui constituent votre document. Le contenu du document, cest--dire les paragraphes de texte sans la mise en forme (mais avec les balises XML qui relient le texte sa mise en forme), se trouve dans lun de ces fichiers. Lide de ce script est de dcompresser (unzip) les deux documents et de comparer le contenu avec diff, puis de nettoyer lespace de travail utilis. Nous faisons galement en sorte que les diffrences soient plus faciles lire. Puisque le contenu est en XML et quil y a peu de sauts de ligne, le script en ajoute aprs chaque balise douverture et avant chaque balise de fermeture (celles qui commencent par une barre oblique, comme dans </ ... >). Bien que cela gnre un grand nombre de lignes vides, cela permet galement diff de se concentrer sur les diffrences dans le contenu textuel. Du point de vue syntaxique, vous avez dj tout vu dans les autres recettes de ce livre, mais il nest peut-tre pas inutile dexpliquer certains points, simplement pour que vous compreniez bien le fonctionnement du script. La ligne 11 redirige la sortie de la fonction shell vers STDERR. En effet, il sagit dun message daide et non de la sortie normale du programme. En plaant la redirection au niveau de la dfinition de la fonction, il est inutile de rediriger sparment chaque ligne. La ligne 37 contient lexpression if [[ ${1:0:1} == '/' ]], qui vrifie si le premier argument commence par une barre oblique. ${1:0:1} est la syntaxe dextraction dune chane contenue dans une variable. La variable est ${1}, cest--dire le premier paramtre positionnel. La syntaxe :0:1 indique que lextraction doit commencer avec un dcalage gal zro et que la chane extraite ne doit contenir quun seul caractre. Les lignes 5960 et 6061 sont peut-tre plus difficiles lire car elles appliquent lchappement au caractre de saut de ligne, pour quil fasse partie de la chane de remplacement de sed. Lexpression prend chaque > de la premire substitution et chaque < de la seconde, en remplaant ce contenu par lui-mme plus un saut de ligne. Ces modifications permettent de placer le code XML et le contenu sur des lignes distinctes. Ainsi, la commande diff naffiche aucune balise XML, uniquement le texte du contenu.

Voir aussi
la recette 8.7, Dcompresser des fichiers, page 180 ; la recette 13.3, Analyser du contenu HTML, page 262 ; la recette 14.11, Utiliser des fichiers temporaires scuriss, page 304 ; la recette 17.3, Dzipper plusieurs archives ZIP, page 432 ; la recette 17.10, Utiliser diff et patch, page 441.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13
Analyses et tches similaires
Les programmeurs reconnatront rapidement les tches de ce chapitre. Les recettes ne sont pas ncessairement plus labores que les autres scripts bash de cet ouvrage, mais, si vous ntes pas un programmeur, ces tches pourraient vous sembler obscures ou sans rapport avec votre utilisation de bash. Nous nallons pas expliquer les raisons des problmes traits ici (en tant que programmeurs, vous les reconnatrez facilement). Mme si les situations dcrites ne se prsentent pas vous, nhsitez pas les tudier malgr tout car elles ont plein de choses vous apprendre sur bash. Certaines solutions de ce chapitre concernent lanalyse des arguments de la ligne de commande. Vous savez que les options dun script shell sont gnralement indiques par un signe moins suivi dune seule lettre. Par exemple, une option -q pourrait demander votre script de passer en mode silencieux (quiet) et dafficher moins de messages. Parfois, une option attend un argument. Par exemple, une option -u pourrait servir prciser un nom dutilisateur et devrait donc tre suivie de ce nom. Cette distinction va tre clarifie dans la premire recette de ce chapitre.

13.1. Analyser les arguments dun script


Problme
Vous souhaitez passer des options votre script afin den ajuster le comportement. Vous pouvez les analyser directement en utilisant ${#}, pour connatre le nombre darguments, et ${1:0:1}, pour tester si le premier caractre du premier argument est un signe moins. Vous avez alors besoin dune forme de logique if/then ou case pour reconnatre loption et son argument (si elle en prend un). Et si lutilisateur na pas fourni largument attendu ? Et sil a appel votre script en combinant deux options (par exemple, -ab) ? Devez-vous galement traiter ce cas ? Lanalyse des options dun script shell est une tche trs frquente. De nombreux scripts ont des options. Existe-t-il une manire standard de les analyser ?

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

258

Chapitre 13 Analyses et tches similaires

Solution
Utilisez la commande getopts interne bash pour faciliter lanalyse des options. Voici un exemple, bas en grande partie sur celui de la page de manuel de getopts :
#!/usr/bin/env bash # bash Le livre de recettes : exemple_getopts # # Utiliser getopts. # optiona= optionb= while getopts 'ab:' OPTION do case $OPTION in a) optiona=1 ;; b) optionb=1 valeurb="$OPTARG" ;; ?) printf "Usage : %s: [-a] [-b valeur] args\n" $(basename $0) >&2 exit 2 ;; esac done shift $(($OPTIND - 1)) if [ "$optiona" ] then printf "Option -a donne\n" fi if [ "$optionb" ] then printf 'Option -b "%s" donne\n' "$valeurb" fi printf "Les arguments restants sont : %s\n" "$*"

Discussion
Ce script reconnat deux sortes doptions. La premire, et la plus simple, est une option donne seule. Elle reprsente gnralement un indicateur qui modifie le comportement dune commande. Cest, par exemple, le cas de loption -l de la commande ls. Une option de la deuxime sorte prend un argument, par exemple loption -u de la commande mysql. Elle attend quun nom dutilisateur soit indiqu, comme dans mysql -u sysadmin. Voyons comment getopts analyse ces deux sortes doptions. La commande getopts prend deux arguments :
getopts 'ab:' OPTION

Le premier prcise la liste des lettres doptions. Le second est le nom dune variable. Dans notre exemple, -a et -b sont les deux seules options valides et le premier argument de ge[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.1. Analyser les arguments dun script

259

topts contient donc ces deux lettres, ainsi que des deux-points. Que reprsente donc ce caractre ? Il signifie que -b prend un argument, tout comme -u nomUtilisateur ou -f nomFichier. Le caractre deux-points doit tre accol toute option attendant un argument. Par exemple, si seule -a prend un argument, nous devons alors crire 'a:b'. La commande getopts fixe la variable indique dans le deuxime argument la valeur quelle trouve lors de lanalyse de la liste des arguments du script ($1, $2, etc.). Si elle rencontre un argument commenant par un signe moins, elle le considre comme une option et place la lettre dans la variable indique ($OPTION dans notre exemple). Ensuite, elle retourne vrai (0) afin que la boucle while qui traite loption puisse poursuivre avec les autres options en renouvelant des appels getopts jusqu ce quil ny ait plus darguments (ou quelle rencontre un double signe moins --, qui permet aux utilisateurs dindiquer explicitement la fin des options). Ensuite, getopts retourne faux (diffrent de zro) et la boucle while se termine. lintrieur de la boucle, pour traiter les lettres doptions, nous employons une instruction case sur la variable $OPTION et fixons la valeur dindicateurs ou effectuons laction correspondant loption en cours. Lorsque loption prend un argument, celui-ci est plac dans la variable $OPTARG (un nom fig sans rapport avec $OPTION). Nous devons enregistrer cette valeur en laffectant une autre variable, car lanalyse se poursuit et la variable $OPTARG est rinitialise chaque invocation de getopts. Le troisime cas de notre instruction case est un point dinterrogation. Ce motif du shell correspond tout caractre isol. Lorsque getopts rencontre une option qui ne fait pas partie de celles attendues ('ab:' dans notre exemple), elle retourne un point dinterrogation littral dans la variable ($OPTION dans notre cas). Notre instruction case aurait donc pu utiliser \?) ou '?') pour une correspondance exacte, mais ?, en tant que motif correspondant un seul caractre, est parfaitement un cas par dfaut. Il correspondra au point dinterrogation littral ainsi qu tout autre caractre isol. Dans le message dutilisation affich, nous apportons deux changements par rapport au script dexemple de la page de manuel. Tout dabord, nous utilisons $(basename $0) pour obtenir le nom du script sans le chemin qui pourrait faire partie de son invocation. Ensuite, nous redirigeons le message vers lerreur standard (>&2) car cest l que doivent aller de tels messages. Tous les messages derreur mis par getopts, lorsquelle rencontre une option inconnue ou quun argument est manquant, sont toujours crits sur lerreur standard. Nous y ajoutons notre message dutilisation. Aprs la boucle while, la ligne suivante est excute :
shift $(($OPTIND 1))

Elle utilise une instruction shift pour dcaler les paramtres positionnels du script shell ($1, $2, etc.) dun nombre de positions vers le bas (en supprimant les premiers). La variable $OPTIND est un indice dans les arguments dont getopts se sert pour indiquer la position de son analyse. Une fois lanalyse termine, nous pouvons carter toutes les options traites en appelant cette instruction shift. Prenons par exemple la ligne de commande suivante :
monScript -a -b alt rouge vert bleu

Aprs lanalyse des options, $OPTIND a la valeur 4. En procdant un dcalage de trois ($OPTIND-1), nous cartons les options. Un rapide echo $* affiche alors :
rouge vert bleu
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

260

Chapitre 13 Analyses et tches similaires

Les arguments restants (qui ne sont pas des options) sont prts tre employs dans le script (sans doute dans une boucle for). Dans notre exemple, la dernire ligne est une instruction printf qui affiche tous les arguments restants.

Voir aussi
help case ; help getopts ; help getopt ; la recette 5.8, Parcourir les arguments dun script, page 96 ; la recette 5.11, Compter les arguments, page 101 ; la recette 5.12, Extraire certains arguments, page 103 ; la recette 6.10, Boucler avec while, page 131 ; la recette 6.14, Raliser des branchements multiples, page 137 ; la recette 6.15, Analyser les arguments de la ligne de commande, page 139 ; la recette 13.2, Afficher ses propres messages derreur lors de lanalyse, page 260.

13.2. Afficher ses propres messages derreur lors de lanalyse


Problme
Vous utilisez getopts pour analyser les options de votre script shell, mais vous naimez pas les messages derreur quelle affiche lorsque lentre ne lui convient pas. Pouvezvous continuer utiliser getopts, mais en crivant votre propre gestion des erreurs ?

Solution
Si vous souhaitez que getopts naffiche aucune erreur, donnez simplement la valeur 0 $OPTERR avant de commencer lanalyse. Si vous voulez que getopts vous donne plus dinformations sans les messages derreur, commencez la liste des options par un caractre deux-points. (Dans les commentaires du script, v--- reprsente une f lche pointant vers un endroit particulier de la ligne qui se trouve en dessous, dans ce cas pour montrer le caractre deux-points.)
#!/usr/bin/env bash # bash Le livre de recettes : getopts_personnalise # # Utiliser getopts - avec des messages d'erreur personnaliss # optiona= optionb= # Puisque getopts ne doit pas gnrer de messages d'erreur, # mais que ce script doit afficher ses propres messages,

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.2. Afficher ses propres messages derreur lors de lanalyse


# nous commenons la liste des options par un ':' pour # getopts au silence. # v---ici while getopts :ab: TROUVE do case $TROUVE in a) optiona=1 ;; b) optionb=1 valeurb="$OPTARG" ;; \:) printf "Il manque un argument l'option -%s printf "Usage : %s: [-a] [-b valeur] args\n" exit 2 ;; \?) printf "Option inconnue : -%s\n" $OPTARG printf "Usage : %s: [-a] [-b valeur] args\n" exit 2 ;; esac >&2 done shift $(($OPTIND - 1)) if [ "$optiona" ] then printf "Option -a donne\n" fi if [ "$optionb" ] then printf 'Option -b "%s" donne\n' "$valeurb" fi printf "Les arguments restants sont : %s\n" "$*" rduire

261

\n" $OPTARG $(basename $0)

$(basename $0)

Discussion
Ce script est trs similaire celui de la recette 13.1, page 257. Reportez-vous sa description pour en comprendre le fonctionnement. Cependant, dans cet exemple, getopts peut retourner un caractre deux-points. Cela se produit lorsquil manque une option (par exemple, si vous invoquez le script avec -b sans lui donner dargument). Dans ce cas, la lettre de loption est place dans $OPTARG afin que vous sachiez quelle est option fautive. De manire similaire, lorsquune option non reconnue est utilise (par exemple, si vous ajoutez -d lors de linvocation de notre exemple), getopts retourne un point dinterrogation dans la variable $TROUVE et place la lettre (d dans ce cas) dans la variable $OPTARG, afin quelle puisse servir dans les messages derreur. Nous avons plac une barre oblique inverse devant les caractres deux-points et points dinterrogation pour indiquer quil sagit de littraux et non de motifs ou dune syntaxe
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

262

Chapitre 13 Analyses et tches similaires

particulire du shell. Mme si ce nest pas ncessaire pour les deux-points, il est prfrable de conserver la mme construction pour les deux signes de ponctuation. Nous avons galement ajout une redirection des entres/sorties la clause esac (la fin de linstruction case). Ainsi, les diverses instructions printf envoient leur sortie vers lerreur standard. Cest tout fait le rle de lerreur standard et il est plus facile de placer cette redirection cet endroit quindividuellement sur chaque instruction printf.

Voir aussi
help case ; help getopts ; help getopt ; la recette 5.8, Parcourir les arguments dun script, page 96 ; la recette 5.11, Compter les arguments, page 101 ; la recette 5.12, Extraire certains arguments, page 103 ; la recette 6.15, Analyser les arguments de la ligne de commande, page 139 ; la recette 13.1, Analyser les arguments dun script, page 257.

13.3. Analyser du contenu HTML


Problme
Vous souhaitez extraire les chanes de caractres dun contenu HTML. Par exemple, vous aimeriez obtenir les chanes de type href="URL" qui se trouvent dans les balises <a>.

Solution
Pour une analyse rapide, non toute preuve, dun contenu HTML, vous pouvez essayer les commandes suivantes :
cat $1 | sed -e 's/>/>\ /g' | grep '<a' | while IFS='"' read a b c ; do echo $b; done

Discussion
Lanalyse dun contenu HTML en bash est assez complexe, principalement parce que bash est trs orient ligne alors que HTML considre les sauts de ligne comme des espaces. Il nest donc pas rare de rencontrer des balises qui occupent plusieurs lignes :
<a href="blah...blah...blah autre texte >

Il existe galement deux manires dcrire les balises <a>. La premire utilise une balise de fermeture </a> spare, tandis que la seconde termine la balise douverture <a> par />. Par consquent, il est assez difficile danalyser des lignes qui peuvent contenir plu-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.3. Analyser du contenu HTML

263

sieurs balises et des balises qui peuvent occuper plusieurs lignes. Notre technique simple base sur bash nest pas toute preuve. Voici les diffrentes tapes de notre solution. Tout dabord, nous sparons les diffrentes balises prsentes sur une ligne en au plus une ligne par balise :
cat fichier | sed -e 's/>/>\ /g'

Juste aprs la barre oblique inverse, il sagit bien dun saut de ligne. Chaque caractre de fin de balise (>) est remplac par le mme caractre et un saut de ligne. Ainsi, chaque balise est place sur des lignes spares, peut-tre avec quelques lignes vides supplmentaires. Le g final demande sed deffectuer la recherche et le remplacement de manire globale, cest--dire plusieurs fois sur une ligne si ncessaire. La sortie de cette commande est ensuite envoye vers grep afin de garder uniquement les lignes des balises <a> ou uniquement celles contenant des guillemets :
cat fichier | sed -e 's/>/>\ /g' | grep '<a'

Ou :
cat fichier | sed -e 's/>/>\ /g' | grep '".*"'

Les apostrophes indiquent au shell de prendre les caractres intrieurs tels quels et de ne pas leur appliquer une expansion. Nous utilisons une expression rgulire qui correspond des guillemets, suivis de tout caractre (.), un nombre quelconque de fois (*), puis dautres guillemets. (Cela ne fonctionne pas si la chane est elle-mme sur plusieurs lignes.) Pour analyser le contenu de lintrieur des guillemets, une astuce consiste employer la variable $IFS (Internal Field Separator) du shell afin de lui indiquer que les guillemets (") servent de sparateurs. Vous pouvez galement faire la mme chose avec awk et son option -F. Par exemple :
cat $1 | sed -e 's/>/>\ /g' | grep '".*"' | awk -F'"' '{ print $2}'

(Ou grep '<a' si vous souhaitez uniquement les balises <a> et non toutes les chanes entre guillemets.) La commande suivante sappuie sur $IFS, la place de awk :
cat $1 | sed -e 's/>/>\ /g' | grep '<a' | while IFS='"' read PRE URL POST ; do echo $URL; done

La sortie de grep est envoye dans une boucle while qui lit lentre et la rpartit dans trois champs (PRE, URL et POST). En faisant prcder la commande read de IFS='"', nous fixons cette variable denvironnement uniquement pour la commande read et non pour lintgralit du script. Par consquent, la ligne dentre lue est analyse avec les guillemets comme sparateurs des mots. PRE reoit donc tout ce qui se trouve avant les guillemets, URL tout ce qui se trouve entre les guillemets, et POST tout ce qui vient ensuite. Pour finir, le script affiche la deuxime variable, URL, cest--dire tous les caractres lintrieur des guillemets.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

264

Chapitre 13 Analyses et tches similaires

Voir aussi
man sed ; man grep.

13.4. Placer la sortie dans un tableau


Problme
Vous souhaitez que la sortie dun programme ou dun script soit place dans un tableau.

Solution
#!/usr/bin/env bash # bash Le livre de recettes : analyseParTableau # # Dterminer la taille du fichier. # Utiliser un tableau pour analyser la sortie de ls -l. LSL=$(ls -ld $1) declare -a MONTAB MONTAB=($LSL) echo Le fichier $1 contient ${MONTAB[4]} octets.

Discussion
Dans notre exemple, nous prenons la sortie de la commande ls -l et plaons ses diffrents mots dans un tableau. Ensuite, nous pouvons simplement faire rfrence chaque lment du tableau pour obtenir chacun des mots. La sortie de la commande ls -l est gnralement similaire la suivante (elle peut varier selon vos paramtres rgionaux) :
-rw-r--r-1 albing users 113 2006-10-10 23:33 mondoc.txt

Si vous connaissez, au moment de lcriture de script, les valeurs placer dans le tableau, il est facile initialiser. Le format est simple. Nous commenons par dclarer une variable de type tableau, puis nous lui affectons les valeurs :
declare -a MONTAB MONTAB=(premier deuxieme troisieme quatrieme)

Nous pouvons galement placer une variable lintrieur des parenthses. Il faut simplement ne pas utiliser de guillemets autour de la variable. Linstruction MONTAB= $("$LSL") place lintgralit de la chane dans le premier lment du tableau puisque les guillemets en font un tout. ${MYRA[0]} est alors le seul lment du tableau et il contient la chane complte. Ce nest pas ce que nous souhaitons. Nous pourrions galement raccourcir ce script en combinant les tapes de la manire suivante :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.5. Analyser la sortie avec une fonction


declare -a MONTAB MONTAB=($(ls -ld $1))

265

Si vous souhaitez connatre le nombre dlments du nouveau tableau, faites simplement rfrence sa variable : ${#MONTAB[*]} ou ${#MYRA[@]}. Ces deux formes impliquent la saisie de nombreux caractres spciaux.

Voir aussi
la recette 5.19, Utiliser les tableaux, page 111.

13.5. Analyser la sortie avec une fonction


Problme
Vous souhaitez analyser la sortie dun programme en la plaant dans diffrentes variables qui seront utilises par ailleurs dans votre script. Les tableaux sont bien adapts aux itrations sur les valeurs, mais ne sont pas trs lisibles si vous voulez faire rfrence individuellement chaque lment au lieu dutiliser un indice.

Solution
Utilisez une fonction pour analyser les mots :
#!/usr/bin/env bash # bash Le livre de recettes : analyseParFonction # # Analyser ls -l par un appel de fonction. # Voici un exemple de la sortie de ls -l : # -rw-r--r-- 1 albing users 126 2006-10-10 22:50 fichier function partiesls () { AUTORISATIONS=$1 LIENS=$2 PROPRIETAIRE=$3 GROUPE=$4 TAILLE=$5 DATECREATION=$6 HEURECREATION=$7 FICHIER=$8 } partiesls $(ls -l "$1") echo $FICHIER a $LIENS 'lien(s)' et sa taille est de $TAILLE octets.

Voici un exemple dexcution:


$ ./analyseParFonction analyseParFonction analyseParFonction a 1 lien(s) et sa taille est de 468 octets. $
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

266

Chapitre 13 Analyses et tches similaires

Discussion
En plaant le texte analyser dans un appel de fonction, nous laissons bash faire le travail. Un appel de fonction est trs similaire un appel de script shell. bash affecte les diffrents mots en arguments $1, $2, etc. Notre fonction prend simplement chaque paramtre positionnel et les affecte une variable spare. Si les variables ne sont pas dclares locales, elles sont alors disponibles lintrieur et hors de la fonction. Nous plaons des guillemets autour de la rfrence $1 dans la commande ls car le nom de fichier indiqu pourrait comporter des espaces. Les guillemets permettent ls de voir un seul nom de fichier et non une suite de noms de fichiers spars. Les apostrophes dans lexpression 'lien(s)' vitent que bash ne traite de manire particulire les parenthses. Nous aurions galement pu placer lintgralit de la phrase ( lexception de la commande echo) entre guillemets ; des guillemets et non des apostrophes pour que la substitution de variables (pour $FICHIER, etc.) ait bien lieu.

Voir aussi
la recette 10.4, Dfinir des fonctions, page 211 ; la recette 10.5, Utiliser des fonctions : paramtres et valeur de retour, page 213 ; la recette 13.8, Dterminer le bon accord, page 268 ; la recette 17.7, Effacer lcran lorsque vous vous dconnectez, page 438.

13.6. Analyser du texte avec read


Problme
Il existe de nombreuses manires danalyser du texte avec bash. Est-il possible demployer autre chose quune fonction ?

Solution
Utilisez linstruction read.
#!/usr/bin/env bash # bash Le livre de recettes : analyseParRead # # Analyser ls -l avec une instruction read. # Voici un exemple de la sortie de ls -l : # -rw-r--r-- 1 albing users 126 2006-10-10 22:50 fichier ls -l "$1" | { read AUTORISATIONS LIENS PROPRIETAIRE GROUPE TAILLE \ DATECREATION HEURECREATION FICHIER ; echo $FICHIER a $LIENS 'lien(s)' et sa taille est \ de $TAILLE octets. ; }

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.7. Analyser avec read dans un tableau

267

Discussion
Dans ce script, lanalyse est effectue par read. Cette commande dcompose lentre en mots, ceux-ci tant spars par des espaces, et affecte chacun deux aux variables indiques. Vous pouvez galement modifier le sparateur, en fixant la variable $IFS (Internal Field Separator) de bash au caractre adquat. Noubliez pas de la remettre sa valeur initiale ! Comme le montre lexemple de la sortie de ls -l, nous avons choisi des noms significatifs pour chacun des mots. Puisque FICHIER est le dernier mot, tout champ supplmentaire sera inclus dans cette variable. Si le nom du fichier comporte des espaces comme dans cinquime symphonie de Beethoven , tous ces mots seront placs dans $FICHIER.

Voir aussi
la recette 2.14, Enregistrer ou runir la sortie de plusieurs commandes, page 44 ; la recette 19.8, Oublier que les tubes crent des sous-shells, page 493.

13.7. Analyser avec read dans un tableau


Problme
Chaque ligne dentre contient un nombre variable de mots. Vous ne pouvez donc pas les affecter des variables prdfinies.

Solution
Utilisez loption -a de linstruction read pour placer les mots lus dans une variable de type tableau :
read -a MONTAB

Discussion
Quelle provienne de lutilisateur ou dun tube, lentre est lue par read et chacun de ses mots sont placs dans un lment du tableau. Il nest pas ncessaire de dclarer la variable comme un tableau. Le simple fait de lutiliser ainsi suffit en faire un tableau. Chaque lment peut tre rfrenc laide de la syntaxe des tableaux de bash, dont le premier indice commence zro. Par consquent, le deuxime mot de la ligne dentre se trouve dans ${MONTAB[1]}. Le nombre de mots dtermine la taille du tableau. Vous pouvez lobtenir laide de ${#MONTAB[@]}.

Voir aussi
la recette 3.5, Lire lentre de lutilisateur, page 64 ; la recette 13.6, Analyser du texte avec read, page 266.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

268

Chapitre 13 Analyses et tches similaires

13.8. Dterminer le bon accord


Problme
Vous souhaitez accorder un nom en fonction du nombre dobjets. Cependant, vous ne voulez pas encombrer votre code dinstructions if.

Solution
#!/usr/bin/env bash # bash Le livre de recettes : auPuriel # # Cette fonction met les mots au pluriel en ajoutant un s lorsque # la valeur ($2) est diffrente de 1 ou gale -1. # Elle ajoute uniquement un 's' ; elle n'est pas trs intelligente. # function pluriel () { if [ $2 -ne 1 -o $2 -eq -1 ] then echo ${1}s else echo ${1} fi } while read num nom do echo $num $(pluriel "$nom" $num) done

Discussion
La fonction, mme si elle ne fait quajouter un s, fonctionne parfaitement pour de nombreux noms. Elle ne procde aucun contrle derreur sur le nombre ou le contenu des arguments. Si vous souhaitez employer ce script dans une application relle, vous devrez ajouter ces vrifications. Nous plaons le nom entre guillemets lors de lappel la fonction pluriel car il peut comporter des espaces. En effet, il est fourni par linstruction read et la dernire variable de cette instruction reoit tout le texte restant sur la ligne dentre. Vous pouvez le constater dans lexemple suivant. Nous enregistrons le script dans le fichier nomm auPluriel et lexcutons sur les donnes suivantes :
$ 1 2 3 cat fichier.entree poule canard oie blanche

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.9. Analyser une chane caractre par caractre


4 cheval fougueux 5 ne gris $ 1 2 3 4 5 $ ./auPluriel < fichier.entree poule canards oie blanches cheval fougueuxs ne griss

269

Le rsultat contient de nombreuses fautes dorthographe, mais le script se comporte comme attendu. Si vous prfrez une syntaxe de type C, crivez linstruction if de la manire suivante :
if (( $2 != 1 || $2 == -1 ))

Le crochet (cest--dire la commande interne test) correspond lancienne forme, plus courante dans les diffrentes versions de bash, mais les deux solutions doivent fonctionner. Utilisez la syntaxe que vous prfrez. Nous ne pensons pas que vous allez garder un script comme auPluriel seul. Mais la fonction pluriel pourrait tre utile dans un projet plus important. Ds que vous souhaitez afficher un nombre de quelque chose, vous pouvez utiliser la fonction pluriel dans la rfrence, comme lillustre la boucle while prcdente.

Voir aussi
la recette 6.11, Boucler avec read, page 133.

13.9. Analyser une chane caractre par caractre


Problme
Quelquen soit la raison vous devez analyser une chane caractre par caractre.

Solution
La fonction dextraction dune sous-chane pour les variables vous permet dobtenir les caractres que vous souhaitez et une autre fonction vous indique la longueur dune chane :
#!/usr/bin/env bash # bash Le livre de recettes : unparun # # Analyser un caractre de l'entre la fois. while read UNELIGNE do

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

270

Chapitre 13 Analyses et tches similaires

for ((i=0; i < ${#UNELIGNE}; i++)) do UNCAR=${UNELIGNE:i:1} # Faire quelque chose, par exemple echo $UNCAR echo $UNCAR done done

Discussion
Linstruction read lit depuis lentre standard et place son contenu, une ligne la fois, dans la variable $UNELIGNE. Puisquil sagit de la seule variable de la commande read, elle contient lintgralit de la ligne. La boucle for parcourt chaque caractre de la variable $UNELIGNE. Nous pouvons calculer le nombre ditrations en utilisant ${#UNELIGNE}, qui retourne la longueur du contenu de $UNELIGNE. Lors de chaque passage dans la boucle, nous affectons UNCAR la valeur dune sous-chane de UNELIGNE. Cette sous-chane est constitue dun caractre et commence la position i. La solution est simple, mais pourquoi aviez-vous ce problme ?

Voir aussi
les autres techniques danalyse de ce chapitre pour savoir sil est possible dviter de travailler un niveau aussi bas.

13.10. Nettoyer une arborescence SVN


Problme
La commande svn status de Subversion affiche tous les fichiers qui ont t modifis, mais si larborescence contient galement des fichiers temporaires ou assimils, svn les inclut galement. Vous souhaitez disposer dun systme de nettoyage de larborescence source, qui supprime les fichiers inconnus de Subversion.
Tant que vous naurez pas invoqu une commande svn add, Subversion ne connatra pas lexistence des nouveaux fichiers. Ce script ne doit tre excut quaprs avoir ajout les nouveaux fichiers sources larborescence ou ils seront supprims.

Solution
svn status src | grep '^\?' | cut -c8- | \ while read nf; do echo "$nf"; rm -rf "$nf"; done

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.11. Configurer une base de donnes MySQL

271

Discussion
svn status affiche des listes dont chaque ligne concerne un fichier. La ligne commence par le caractre M lorsque le fichier a t modifi. Le caractre A reprsente un fichier nouvellement ajout, mais pas encore valid. Un point dinterrogation signifie que le fichier est inconnu. laide de la commande grep, nous slectionnons les lignes qui commencent par un point dinterrogation. Ensuite, nous retirons les huit dernires colonnes de chaque ligne de la sortie afin de ne conserver que le nom du fichier. Nous lisons les noms de fichiers avec une instruction read dans une boucle while. La commande echo nest pas indispensable, mais elle permet de savoir ce qui est supprim. Pour la suppression, nous utilisons les options -rf car le fichier peut tre un rpertoire et nous voulons quelle se fasse en silence. Les problmes, comme ceux provenant des autorisations, sont carts par loption -f. Elle permet de supprimer le fichier pour autant que les permissions lautorisent. Nous plaons la rfrence au nom de fichier entre guillemets ("$nf") pour le cas o il contiendrait des caractres spciaux (comme des espaces).

Voir aussi
la recette 6.11, Boucler avec read, page 133 ; lannexe D, Gestion de versions, page 575.

13.11. Configurer une base de donnes MySQL


Problme
Vous souhaitez crer et initialiser plusieurs bases de donnes MySQL. Elles sont toutes initialises avec les mmes commandes SQL. Pour chaque base de donnes, son nom doit tre prcis, mais elles auront toutes le mme contenu, tout au moins quant linitialisation. Vous devez effectuer cette configuration de nombreuses fois, par exemple parce quelles sont employes dans des procdures de tests et doivent tre rinitialises avant le dbut des tests.

Solution
Un simple script bash vous aidera dans cette tche dadministration :
#!/usr/bin/env bash # bash Le livre de recettes : initbdd # # Initialiser des bases de donnes partir d'un fichier standard. # Crer les bases au besoin. LISTEBDD=$(mysql -e "SHOW DATABASES;" | tail +2) select BDD in $LISTEBDD "nouvelle..." do if [[ $BDD == "nouvelle..." ]]

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

272

Chapitre 13 Analyses et tches similaires


then printf "%b" "nom de la nouvelle base : " read BDD reste echo cration de la base $BDD mysql -e "CREATE DATABASE IF NOT EXISTS $BDD;" fi

if [ "$BDD" ] then echo Initialisation de la base : $BDD mysql $BDD < monInit.sql fi done

Discussion
La commande tail +2 permet de retirer les en-ttes de la liste des bases de donnes (voir la recette 2.12, page 43). Linstruction select cre un menu affichant les bases de donnes existantes. Nous ajoutons lentre "nouvelle..." (voir les recettes 3.7, page 68, et 6.16, page 142). Lorsque lutilisateur souhaite crer une nouvelle base de donnes, nous lui demandons dentrer un nouveau nom. Nous indiquons deux champs la commande read, afin de mettre en place une petite gestion des erreurs. Si lutilisateur saisit plusieurs noms sur la ligne, nous prenons uniquement le premier ; il est plac dans la variable $BDD, tandis que la fin de lentre va dans $reste et est ignore. Nous pourrions vrifier que $reste est nulle. Que lutilisateur ait choisi une base parmi la liste ou quil en cre une nouvelle, si la variable $BDD nest pas vide, nous invoquons mysql avec les instructions SQL places dans le fichier monInit.sql. Celui-ci contient la squence dinitialisation commune. Si vous souhaitez utiliser un script comme celui-ci, vous devrez peut-tre ajouter des paramtres votre commande mysql, comme -u et -p pour demander la saisie dun nom dutilisateur et dun mot de passe. Cela dpend de la configuration de votre base de donnes et de ses autorisations, ou de lexistence dun fichier .my.cnf. Nous pourrions galement ajouter un contrle derreur pour vrifier si la cration de la nouvelle base de donnes sest bien passe. Dans le cas contraire, il faudrait annuler la variable BDD afin de ne pas effectuer initialisation. Cependant, comme le propose de nombreux livre de mathmatiques, nous laissons cet exercice au lecteur.

Voir aussi
la recette 2.12, Sauter len-tte dun fichier, page 43 ; la recette 3.7, Choisir dans une liste doptions, page 68 ; la recette 6.16, Crer des menus simples, page 142, pour plus dinformations sur la commande select ; la recette 14.20, Utiliser des mots de passe dans un script, page 319.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.12. Extraire certains champs des donnes

273

13.12. Extraire certains champs des donnes


Problme
Vous souhaitez extraire un ou plusieurs champs depuis chaque ligne en sortie.

Solution
Utilisez cut si la ligne contient des dlimiteurs faciles identifier, mme sils sont diffrents au dbut et la fin des champs :
# Exemple simple : utilisateurs, rpertoires personnels et shells # prsents sur ce systme NetBSD : $ cut -d':' -f1,6,7 /etc/passwd root:/root:/bin/csh toor:/root:/bin/sh daemon:/:/sbin/nologin operator:/usr/guest/operator:/sbin/nologin bin:/:/sbin/nologin games:/usr/games:/sbin/nologin postfix:/var/spool/postfix:/sbin/nologin named:/var/chroot/named:/sbin/nologin ntpd:/var/chroot/ntpd:/sbin/nologin sshd:/var/chroot/sshd:/sbin/nologin smmsp:/nonexistent:/sbin/nologin uucp:/var/spool/uucppublic:/usr/libexec/uucp/uucico nobody:/nonexistent:/sbin/nologin jp:/home/jp:/usr/pkg/bin/bash

# Quel est le shell le plus utilis sur le systme ? $ cut -d':' -f7 /etc/passwd | sort | uniq -c | sort -rn 10 /sbin/nologin 2 /usr/pkg/bin/bash 1 /bin/csh 1 /bin/sh 1 /usr/libexec/uucp/uucico

# Voyons la liste des deux premiers niveaux de rpertoire : $ cut -d':' -f6 /etc/passwd | cut -d'/' -f1-3 | sort -u / /home/jp /nonexistent /root /usr/games /usr/guest /var/chroot /var/spool

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

274

Chapitre 13 Analyses et tches similaires

Utilisez awk pour un dcoupage selon les espaces ou si vous devez rorganiser les champs de la sortie. Le symbole reprsente un caractre de tabulation dans la sortie. Lespace est utilise par dfaut, mais vous pouvez modifier cette configuration en utilisant $OFS :
# Utilisateurs, rpertoires personnels et shells, mais inverser ces # deux derniers et utiliser une tabulation comme dlimiteur : $ awk 'BEGIN {FS=":"; OFS="\t"; } { print $1,$7,$6; }' /etc/passwd root /bin/csh /root toor /bin/sh /root daemon /sbin/nologin / operator /sbin/nologin /usr/guest/operator bin /sbin/nologin / games /sbin/nologin /usr/games postfix /sbin/nologin /var/spool/postfix named /sbin/nologin /var/chroot/named ntpd /sbin/nologin /var/chroot/ntpd sshd /sbin/nologin /var/chroot/sshd smmsp /sbin/nologin /nonexistent uucp /usr/libexec/uucp/uucico /var/spool/uucppublic nobody /sbin/nologin /nonexistent jp /usr/pkg/bin/bash /home/jp

# Supprimer les multiples espaces et inverser, # le premier champ est retir : $ grep '^# [1-9]' /etc/hosts | awk '{print $3,$2}' 10.255.255.255 10.0.0.0 172.31.255.255 172.16.0.0 192.168.255.255 192.168.0.0

Utilisez grep -o pour afficher uniquement la partie qui correspond votre motif. Cette solution est trs pratique lorsque vous ne pouvez pas prciser les dlimiteurs comme dans les solutions prcdentes. Par exemple, supposons que vous deviez extraire toutes les adresses IP dun fichier, quel quil soit. Nous utilisons egrep afin de bnficier des expressions rgulires, mais -o doit fonctionner avec toute variante GNU de grep (elle nest probablement pas reconnue par les versions non GNU) ; consultez votre documentation.
$ cat mes_adr_ip Cette ligne 1 contient 1 adresse IP : 10.10.10.10 La ligne 2 en inclut 2 ; elles sont 10.10.10.11 et 10.10.10.12. Ligne trois, correspondant ftp_server=10.10.10.13:21. $ egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' mes_adr_ip 10.10.10.10 10.10.10.11 10.10.10.12 10.10.10.13

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.12. Extraire certains champs des donnes

275

Discussion
Les possibilits sont infinies et nous en avons peine vu le dbut. Vous retrouvez ici toute la philosophie des chanes de commandes dUnix. Prenez plusieurs petit outils qui font trs bien une chose et combinez-les pour rsoudre vos problmes. Lexpression rgulire utilise pour les adresses IP est trs simple et pourrait correspondre dautres lments, y compris des adresses invalides. Pour un meilleur motif, utilisez les expressions rgulires PCRE (Perl Compatible Regular Expressions) du livre Matrise des expressions rgulires, 2e dition de Jeffrey E. F. Friedl (ditions OReilly), si votre version de grep reconnat loption -P Sinon, utilisez Perl. .
$ grep -oP '([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[05])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])' mes_adr_ip 10.10.10.10 10.10.10.11 10.10.10.12 10.10.10.13

$ perl -ne 'while ( m/([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[04]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[05])/g ) { print qq($1.$2.$3.$4\n); }' mes_adr_ip 10.10.10.10 10.10.10.11 10.10.10.12 10.10.10.13

Voir aussi
man cut ; man awk ; man grep ; Matrise des expressions rgulires, 2e dition de Jeffrey E. F. Friedl (ditions OReilly) ; la recette 8.4, Couper des parties de la sortie, page 176 ; la recette 13.14, Supprimer les espaces, page 277 ; la recette 15.10, Dterminer mon adresse, page 349 ; la recette 17.16, Trouver les lignes prsentes dans un fichier mais pas dans un autre, page 456.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

276

Chapitre 13 Analyses et tches similaires

13.13. Actualiser certains champs dans des fichiers de donnes


Problme
Vous souhaitez extraire certaines parties (champs) dune ligne (enregistrement) et les actualiser.

Solution
Dans le cas le plus simple, vous souhaitez extraire un seul champ dune ligne, puis lutiliser. Pour cela, vous pouvez vous servir de cut ou de awk (voir la recette 13.12, page 273). Si vous devez modifier un champ dans un fichier de donnes sans lextraire, le cas est plus complexe. Pour une simple recherche et remplacement, utilisez sed. Par exemple, la commande suivante fait passer tous les utilisateurs de csh sh sur ce systme NetBSD :
$ grep csh /etc/passwd root:*:0:0:Charlie &:/root:/bin/csh $ sed 's/csh$/sh/' /etc/passwd | grep '^root' root:*:0:0:Charlie &:/root:/bin/sh

Si le champ est impliqu dans des oprations arithmtiques ou si vous devez modifier une chane uniquement dans un certain champ, employez awk :
$ cat Ligne Ligne Ligne Ligne Ligne $ awk Ligne Ligne Ligne Ligne Ligne fichier_donnees 1 termine 2 termine 3 termine 4 termine 5 termine '{print $1, $2+5, $3}' fichier_donnees 6 termine 7 termine 8 termine 9 termine 10 termine

# Si le deuxime champ contient '3', le passer '8' et le marquer. $ awk '{ if ($2 == "3") print $1, $2+5, $3, "Ajuste" ; else print $0; }' fichier_donnees Ligne 1 termine Ligne 2 termine Ligne 8 termine Ajuste Ligne 4 termine Ligne 5 termine

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.14. Supprimer les espaces

277

Discussion
Les possibilits sont aussi nombreuses que vos donnes, mais les exemples prcdents vous permettront de comprendre comment modifier facilement vos donnes.

Voir aussi
man awk ; man sed ; http://sed.sourceforge.net/sedfaq.html ; http://sed.sourceforge.net/sed1line.txt ; la recette 11.7, Calculer avec des dates et des heures, page 233 ; la recette 13.12, Extraire certains champs des donnes, page 273.

13.14. Supprimer les espaces


Problme
Vous souhaitez supprimer les espaces de dbut et/ou de fin sur les lignes contenant des champs de donnes.

Solution
Les solutions sappuient sur un traitement de read et de $REPLY spcifique bash. Pour une autre solution, consultez la fin de la section Discussion. Tout dabord, voici un fichier dont les lignes contiennent des caractres despacement au dbut et la fin. Nous avons ajout ~~ afin de les reprer plus facilement. Le symbole reprsente un caractre de tabulation dans la sortie :
# Montrer les espaces dans notre fichier dexemple : $ while read; do echo ~~"$REPLY"~~; done < espaces ~~ Cette ligne contient des espaces au dbut.~~ ~~Cette ligne contient des espaces la fin. ~~ ~~ Cette ligne contient des espaces au dbut et la fin. ~~ ~~ Tabulation au dbut.~~ ~~Tabulation la fin. ~~ ~~ Tabulation au dbut et la fin. ~~ ~~ Mlange despaces au dbut.~~ ~~Mlange despaces la fin. ~~ Mlange despaces au dbut et la fin. ~~ ~~

Pour supprimer la fois les espaces de dbut et de fin, utilisez $IFS et ajoutez la variable interne REPLY (la section Discussion expliquera ce fonctionnement) :
$ while read REPLY; do echo ~~"$REPLY"~~; done < espaces ~~Cette ligne contient des espaces au dbut.~~ ~~Cette ligne contient des espaces la fin.~~

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

278

Chapitre 13 Analyses et tches similaires

~~Cette ligne contient des espaces au dbut et la fin.~~ ~~Tabulation au dbut.~~ ~~Tabulation la fin.~~ ~~Tabulation au dbut et la fin.~~ ~~Mlange despaces au dbut.~~ ~~Mlange despaces la fin.~~ ~~Mlange despaces au dbut et la fin.~~

Pour ne supprimer que les espaces, utilisez une correspondance de motifs simple :
# Uniquement les espaces de dbut. $ while read; do echo "~~${REPLY## }~~"; done < espaces ~~Cette ligne contient des espaces au dbut.~~ ~~Cette ligne contient des espaces la fin. ~~ ~~Cette ligne contient des espaces au dbut et la fin. ~~ ~~ Tabulation au dbut.~~ ~~Tabulation la fin. ~~ ~~ Tabulation au dbut et la fin. ~~ ~~ Mlange despaces au dbut.~~ ~~Mlange despaces la fin. ~~ ~~ Mlange despaces au dbut et la fin. ~~ # Uniquement les espaces de fin. $ while read; do echo "~~${REPLY%% }~~"; done < espaces ~~ Cette ligne contient des espaces au dbut.~~ ~~Cette ligne contient des espaces la fin.~~ ~~ Cette ligne contient des espaces au dbut et la fin.~~ ~~ Tabulation au dbut.~~ ~~Tabulation la fin. ~~ ~~ Tabulation au dbut et la fin. ~~ ~~ Mlange despaces au dbut.~~ ~~Mlange despaces la fin. ~~ Mlange despaces au dbut et la fin. ~~ ~~

Pour supprimer uniquement les caractres despacement (y compris les tabulations) au dbut ou la fin, la commande est plus complexe :
# Dans les deux cas, cette commande est ncessaire. $ shopt -s extglob # Uniquement les espacements du dbut. $ while read; do echo "~~${REPLY##+([[:space:]])}~~"; done < espaces ~~Cette ligne contient des espaces au dbut.~~ ~~Cette ligne contient des espaces la fin. ~~ ~~Cette ligne contient des espaces au dbut et la fin. ~~ ~~Tabulation au dbut.~~ ~~Tabulation la fin. ~~ ~~Tabulation au dbut et la fin. ~~ ~~Mlange despaces au dbut.~~ ~~ ~~Mlange despaces la fin. ~~Mlange despaces au dbut et la fin. ~~

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.14. Supprimer les espaces


# Uniquement les espacements de fin. $ while read; do echo "~~${REPLY%%+([[:space:]])}~~"; done < espaces ~~ Cette ligne contient des espaces au dbut.~~ ~~Cette ligne contient des espaces la fin.~~ ~~ Cette ligne contient des espaces au dbut et la fin.~~ ~~ Tabulation au dbut.~~ ~~Tabulation la fin.~~ ~~ Tabulation au dbut et la fin.~~ ~~ Mlange despaces au dbut.~~ ~~Mlange despaces la fin.~~ ~~ Mlange despaces au dbut et la fin.~~

279

Discussion
ce stade, il est fort probable que vous regardiez ces lignes en vous demandant comment nous allons bien pouvoir les rendre comprhensibles. En ralit, lexplication, bien que subtile, reste simple. Le premier exemple repose sur la variable $REPLY que read utilise par dfaut lorsque vous nindiquez pas votre propre variable. Chet Ramey (le responsable de bash) a fait ce choix de conception : sil ny a pas dautres variables, enregistrer le texte de la ligne lue dans la variable $REPLY, sans le modifier, sinon lanalyser en utilisant $IFS .
$ while read; do echo ~~"$REPLY"~~; done < espaces

En revanche, lorsque nous passons un ou plusieurs noms de variables read, cette instruction analyse lentre, en utilisant les valeurs de $IFS (qui contient par dfaut une espace, une tabulation et un saut de ligne). Une phase de ce processus danalyse consiste retirer les espaces de dbut et de fin :
$ while read REPLY; do echo ~~"$REPLY"~~; done < espaces

Pour supprimer les espaces de dbut ou de fin (non les deux), il suffit demployer les oprateurs ${##} ou ${%%} (voir la recette 6.7, page 126) :
$ while read; do echo "~~${REPLY## }~~"; done < espaces $ while read; do echo "~~${REPLY%% }~~"; done < espaces

Cependant, la prise en compte des caractres de tabulation est plus complexe. Si les lignes ne comportaient que des tabulations, nous pourrions utiliser les oprateurs ${##} ou ${%%} et insrer les caractres de tabulation avec la squence de touches Ctrl-V Ctrl-I. Malheureusement, nos lignes mlangent espaces et tabulations. Nous activons donc la globalisation tendue et utilisons une classe de caractres qui clarifie notre intention. La classe de caractres [:space:] pourrait fonctionner sans extglob, mais nous devons prciser une ou plusieurs occurrences avec +() pour supprimer plusieurs espaces ou tabulations (ou combinaisons des deux) sur la mme ligne .
# $ $ $ Cela fonctionne, mais extglob est ncessaire pour la partie +(). shopt -s extglob while read; do echo "~~${REPLY##+([[:space:]])}~~"; done < espaces while read; do echo "~~${REPLY%%+([[:space:]])}~~"; done < espaces

# Cela ne fonctionne pas. $ while read; do echo "~~${REPLY##[[:space:]]}~~"; done < espaces

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

280

Chapitre 13 Analyses et tches similaires

~~Cette ligne contient des espaces au dbut.~~ ~~Cette ligne contient des espaces la fin. ~~ ~~Cette ligne contient des espaces au dbut et la fin. ~~ ~~Tabulation au dbut.~~ ~~Tabulation la fin. ~~ ~~Tabulation au dbut et la fin. ~~ Mlange despaces au dbut.~~ ~~ ~~Mlange despaces la fin. ~~ ~~ Mlange despaces au dbut et la fin. ~~

Voici une approche diffrente, qui se fonde galement sur $IFS, mais pour analyser des champs (ou mots) la place denregistrements (ou lignes) :
$ for i in $(cat autres_espaces); do echo ~~$i~~; done ~~Cette~~ ~~ligne~~ ~~contient~~ ~~un~~ ~~espace~~ ~~au~~ ~~dbut.~~ ~~Cette~~ ~~ligne~~ ~~contient~~ ~~un~~ ~~espace~~ ~~~~ ~~la~~ ~~fin.~~ ~~Cette~~ ~~ligne~~ ~~contient~~ ~~un~~ ~~espace~~ ~~au~~ ~~dbut~~ ~~et~~ ~~~~ ~~la~~ ~~fin.~~

Enfin, contrairement nos solutions prcdentes qui sappuient sur le choix de conception de Chet quant linstruction read et la variable $REPLY, le code suivant prend une toute autre approche :
shopt -s extglob while IFS= read # Conserver echo "Aucun # Supprimer echo "Dbut -r ligne; do toutes les espaces. : ~~$ligne~~" les espaces de dbut. : ~~${ligne##+([[:space:]])}~~"

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.15. Compacter les espaces


# Supprimer les espaces de fin. echo "Fin : ~~${ligne%%+([[:space:]])}~~" # Supprimer les espaces de dbut et de fin. ligne="${ligne##+([[:space:]])}" ligne="${ligne%%+([[:space:]])}" echo "Tous : ~~$ligne~~" done < espaces

281

Voir aussi
la recette 6.7, Tester avec des correspondances de motifs, page 126 ; la recette 13.6, Analyser du texte avec read, page 266.

13.15. Compacter les espaces


Problme
Un fichier contient des suites despaces et vous souhaitez les compacter afin de navoir quun seul caractre ou dlimiteur.

Solution
Utilisez tr ou awk, selon les circonstances.

Discussion
Pour transformer une suite despaces en un seul caractre, vous pouvez employer tr, mais vous risquez dendommager le fichier sil nest pas correctement form. Par exemple, si certains champs sont dlimits par plusieurs espaces mais quils contiennent euxmmes des espaces, le compactage supprimera cette distinction. Dans lexemple suivant, les caractres _ remplacent les espaces. Le symbole reprsente un caractre de tabulation dans la sortie.
$ cat fichier_donnees Intitule1 Enr1_Champ1 Enr2_Champ1 Enr3_Champ1 Intitule2 Enr1_Champ2 Enr2_Champ2 Enr3_Champ2 Intitule3 Enr1_Champ3 Enr2_Champ3 Enr3_Champ3

$ cat fichier_donnees | tr -s ' ' '\t' Intitule1 Intitule2 Intitule3 Enr1_Champ1 Enr1_Champ2 Enr1_Champ3 Enr2_Champ1 Enr2_Champ2 Enr2_Champ3 Enr3_Champ1 Enr3_Champ2 Enr3_Champ3

Si le dlimiteur de champs est constitu de plusieurs caractres, tr ne fonctionne pas car les caractres uniques de son premier jeu sont convertis en un caractre unique correspondant du second jeu. Vous pouvez employer awk pour combiner ou convertir des spara-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

282

Chapitre 13 Analyses et tches similaires

teurs de champs. Puisque le sparateur FS interne awk accepte les expressions rgulires, vous avez une grande libert pour la sparation. Il existe galement une astuce intressante. En cas daffectation dun champ, awk rassemble lenregistrement en utilisant le sparateur de champs de sortie OFS. Par consquent, si vous affectez un champ lui-mme et affichez ensuite lenregistrement, vous obtenez le mme rsultat que si vous aviez remplac FS par OFS sans vous proccuper du nombre denregistrements dans les donnes. Dans cet exemple, les champs sont spars par plusieurs espaces, mais ils incluent galement des espaces. Par consquent, la commande awk 'BEGIN { OFS = "\t" } { $1 = $1; print }' fichier_donnees1 ne fonctionne pas. Voici le fichier de donnes :
$ cat fichier_donnees1 Intitule1 Enr1 Champ1 Enr2 Champ1 Enr3 Champ1 Intitule2 Enr1 Champ2 Enr2 Champ2 Enr3 Champ2 Intitule3 Enr1 Champ3 Enr2 Champ3 Enr3 Champ3

Dans lexemple suivant, nous affectons deux espaces FS et une tabulation OFS. Nous procdons ensuite une affectation ($1 = $1) pour que awk reconstruise lenregistrement, mais, puisque les doubles espaces sont remplaces par des tabulations, nous utilisons gsub pour compacter celles-ci, puis nous affichons le rsultat. Le symbole reprsente un caractre de tabulation dans la sortie. Le rsultat tant plus difficile lire, nous prsentons galement une version hexadcimale. Noubliez pas que le code ASCII du caractre de tabulation est 09 et que celui de lespace est 20.
$ awk 'BEGIN { FS = " "; OFS = "\t" } { $1 = $1; gsub(/\t+/, "\t"); print }' fichier_donnees1 Intitule1 Intitule2 Intitule3 Enr1 Champ1 Enr1 Champ2 Enr1 Champ3 Enr2 Champ1 Enr2 Champ2 Enr2 Champ3 Enr3 Champ1 Enr3 Champ2 Enr3 Champ3

$ awk 'BEGIN { FS = " "; OFS = "\t" } { fichier_donnees1 | hexdump -C 00000000 49 6e 74 69 74 75 6c 65 31 09 00000010 6c 65 32 09 49 6e 74 69 74 75 00000020 72 31 20 43 68 61 6d 70 31 09 00000030 68 61 6d 70 32 09 45 6e 72 31 00000040 33 0a 45 6e 72 32 20 43 68 61 00000050 72 32 20 43 68 61 6d 70 32 09 00000060 68 61 6d 70 33 0a 45 6e 72 33 00000070 31 09 45 6e 72 33 20 43 68 61 00000080 72 33 20 43 68 61 6d 70 33 0a 0000008a

$1 = $1; gsub(/\t+/, "\t"); print }' 49 6c 45 20 6d 45 20 6d 6e 65 6e 43 70 6e 43 70 74 33 72 68 31 72 68 32 69 0a 31 61 09 32 61 09 74 45 20 6d 45 20 6d 45 75 6e 43 70 6e 43 70 6e |Intitule1.Intitu| |le2.Intitule3.En| |r1 Champ1.Enr1 C| |hamp2.Enr1 Champ| |3.Enr2 Champ1.En| |r2 Champ2.Enr2 C| |hamp3.Enr3 Champ| |1.Enr3 Champ2.En| |r3 Champ3.|

Vous pouvez galement vous servir de awk pour supprimer les caractres despacement au dbut et la fin des lignes. Mais, comme nous lavons mentionn prcdemment, les sparateurs de champs seront galement remplacs sauf sils sont dj des espaces :
# Supprime les caractres despacement au dbut et la fin, mais # remplace galement les sparateurs de champs TAB par des espaces. $ awk '{ $1 = $1; print }' espaces
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.16. Traiter des enregistrements de longueur fixe

283

Voir aussi
Effective awk Programming de Arnold Robbins (OReilly Media) ; sed & awk de Arnold Robbins et Dale Dougherty (OReilly Media) ; la recette 13.16, Traiter des enregistrements de longueur fixe, page 283 ; la section Squences dchappement de tr, page 548 ; la section Tableau des valeurs ASCII, page 555.

13.16. Traiter des enregistrements de longueur fixe


Problme
Vous devez lire et traiter des donnes qui sont de longueur fixe.

Solution
Utilisez Perl ou gawk 2.13 (ou une version ultrieure). Voici le fichier des donnes :
$ cat fichier_longueur_fixe Intitule1---------Intitule2-----------------------Intitule3------Enr1 Champ1 Enr1 Champ2 Enr1 Champ3 Enr2 Champ1 Enr2 Champ2 Enr2 Champ3 Enr3 Champ1 Enr3 Champ2 Enr3 Champ3

Vous pouvez traiter son contenu avec gawk de GNU. La variable FIELDWIDTHS doit contenir les diffrentes longueurs des champs. La variable OFS peut avoir la valeur que vous souhaitez. Vous devez effectuer une affectation afin que gawk reconstruise lenregistrement (voir la recette 13.14, page 277). Cependant, gawk ne supprime pas les espaces qui servent remplir lenregistrement dorigine. Nous utilisons donc deux commandes gsubs pour raliser cette opration, la premire pour les premiers champs et la seconde pour le dernier. Enfin, nous affichons le rsultat. Le symbole reprsente un caractre de tabulation dans la sortie. Le rsultat tant plus difficile lire, nous prsentons galement une version hexadcimale. Noubliez pas que le code ASCII du caractre de tabulation est 09 et que celui de lespace est 20.
$ gawk ' BEGIN { FIELDWIDTHS = "18 32 16"; OFS = "\t" } { $1 = $1; gsub(/ +\t/, "\t"); gsub(/ +$/, ""); print }' fichier_longueur_fixe Intitule1----------- Intitule2------------------------- Intitule3--------Enr1 Champ1 Enr1 Champ2 Enr1 Champ3 Enr2 Champ1 Enr2 Champ2 Enr2 Champ3 Enr3 Champ1 Enr3 Champ2 Enr3 Champ3

$ gawk ' BEGIN { FIELDWIDTHS = "18 32 16"; OFS = "\t" } { $1 = $1; gsub(/ +\t/, "\t"); gsub(/ +$/, ""); print }' fichier_longueur_fixe | hexdump -C

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

284
00000000 00000010 00000020 00000030 00000040 00000050 00000060 00000070 00000080 00000090 000000a0 000000b0 000000b1 49 2d 2d 2d 2d 09 31 61 09 33 61 0a 6e 2d 2d 2d 2d 45 20 6d 45 20 6d 74 09 2d 2d 2d 6e 43 70 6e 43 70 69 49 2d 09 2d 72 68 31 72 68 32 74 6e 2d 49 0a 31 61 09 32 61 09 75 74 2d 6e 45 20 6d 45 20 6d 45 6c 69 2d 74 6e 43 70 6e 43 70 6e 65 74 2d 69 72 68 33 72 68 31 72 31 75 2d 74 31 61 0a 32 61 09 33

Chapitre 13 Analyses et tches similaires


2d 6c 2d 75 20 6d 45 20 6d 45 20 2d 65 2d 6c 43 70 6e 43 70 6e 43 2d 32 2d 65 68 32 72 68 33 72 68 2d 2d 2d 33 61 09 32 61 0a 33 61 2d 2d 2d 2d 6d 45 20 6d 45 20 6d 2d 2d 2d 2d 70 6e 43 70 6e 43 70 2d 2d 2d 2d 31 72 68 32 72 68 33 |Intitule1-------| |--.Intitule2----| |----------------| |---.Intitule3---| |----.Enr1 Champ1| |.Enr1 Champ2.Enr| |1 Champ3.Enr2 Ch| |amp1.Enr2 Champ2| |.Enr2 Champ3.Enr| |3 Champ1.Enr3 Ch| |amp2.Enr3 Champ3| |.|

Si gawk nest pas install sur votre systme, vous pouvez utiliser Perl, qui savre plus simple. Une boucle while sans affichage lit lentre (-n), dcompose chaque rengistrement ($_) et reconstruit la liste en concatnant les lments laide dun caractre de tabulation. Chaque enregistrement est affich avec un saut de ligne :
$ perl -ne 'print join("\t", unpack("A18 A32 A16", $_) ) . "\n";' fichier_longueur_fixe Intitule1----------- Intitule2------------------------- Intitule3--------Enr1 Champ1 Enr1 Champ2 Enr1 Champ3 Enr2 Champ1 Enr2 Champ2 Enr2 Champ3 Enr3 Champ1 Enr3 Champ2 Enr3 Champ3

$ perl -ne 'print join("\t", unpack("A18 longueur_fixe_file | hexdump -C 00000000 49 6e 74 69 74 75 6c 65 31 2d 00000010 2d 2d 09 49 6e 74 69 74 75 6c 00000020 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 00000030 2d 2d 2d 09 49 6e 74 69 74 75 00000040 2d 2d 2d 2d 0a 45 6e 72 31 20 00000050 09 45 6e 72 31 20 43 68 61 6d 00000060 31 20 43 68 61 6d 70 33 0a 45 00000070 61 6d 70 31 09 45 6e 72 32 20 00000080 09 45 6e 72 32 20 43 68 61 6d 00000090 33 20 43 68 61 6d 70 31 09 45 000000a0 61 6d 70 32 09 45 6e 72 33 20 000000b0 0a 000000b1

A32 A16", $_) ) . "\n";' 2d 65 2d 6c 43 70 6e 43 70 6e 43 2d 32 2d 65 68 32 72 68 33 72 68 2d 2d 2d 33 61 09 32 61 0a 33 61 2d 2d 2d 2d 6d 45 20 6d 45 20 6d 2d 2d 2d 2d 70 6e 43 70 6e 43 70 2d 2d 2d 2d 31 72 68 32 72 68 33 |Intitule1-------| |--.Intitule2----| |----------------| |---.Intitule3---| |----.Enr1 Champ1| |.Enr1 Champ2.Enr| |1 Champ3.Enr2 Ch| |amp1.Enr2 Champ2| |.Enr2 Champ3.Enr| |3 Champ1.Enr3 Ch| |amp2.Enr3 Champ3| |.|

Consultez la documentation de Perl pour plus dinformations sur les formats de pack et de unpack.

Discussion
Quiconque possde une exprience Unix utilise automatiquement une forme de dlimiteur dans la sortie, puisque les outils textutils ne sont jamais trs loin. Par consquent, les enregistrements de longueur fixe sont assez rares dans le monde Unix. En revanche, ils sont trs frquents dans les grands systmes et ils proviennent gnralement dappli-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.17. Traiter des fichiers sans sauts de ligne

285

cations de type SAP Comme nous venons de le voir, vous pouvez les manipuler sans dif. ficult. Les solutions donnes ont pour inconvnient dexiger que lenregistrement se termine par un saut de ligne. Ce nest pas souvent le cas dans les fichiers provenant des grands systmes. Vous pouvez alors utiliser la recette 13.17, page 285, pour ajouter des sauts de lignes la fin de chaque enregistrement avant de les traiter.

Voir aussi
man gawk ; http://www.faqs.org/faqs/computer-lang/awk/faq/ ; http://perl.enstimac.fr/DocFr/perlfunc.html#item_unpack ; http://perl.enstimac.fr/DocFr/perlfunc.html#item_pack ; la recette 13.14, Supprimer les espaces, page 277 ; la recette 13.17, Traiter des fichiers sans sauts de ligne, page 285.

13.17. Traiter des fichiers sans sauts de ligne


Problme
Vous disposez dun grand fichier qui ne contient aucun saut de ligne et vous devez le traiter.

Solution
Prtraitez le fichier en ajoutant des sauts de ligne aux endroits adquats. Par exemple, les fichiers ODF (Open Document Format) dOpenOffice.org sont essentiellement des fichiers XML compresss. Il est possible de les dcompresser (avec unzip) et de traiter le contenu XML avec grep. La recette 12.5, page 254, dtaille la manipulation de fichiers ODF. Dans cet exemple, nous insrons un saut de ligne aprs chaque symbole de fermeture (>). Il est ainsi plus facile de traiter le fichier avec grep ou dautres logiciels textutils. Notez quil faut saisir une barre oblique inverse immdiatement suivie de la touche Entre pour inclure un saut de ligne chapp dans le script sed :
$ wc -l contenu.xml 1 contenu.xml $ sed -e 's/>/>\ /g' contenu.xml | wc -l 1687

Si les enregistrements sont de longueur fixe, sans saut de ligne, optez pour la solution suivante, o 48 correspond la longueur de lenregistrement.
$ cat longueur_fixe Ligne_1_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZLigne_2_ _ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZLigne_3_ _

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

286

Chapitre 13 Analyses et tches similaires

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZLigne_4_ _ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZLigne_5_ _ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZLigne_6_ _ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZLigne_7_ _ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZLigne_8_ _ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZLigne_9_ _ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZLigne_10_ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZLigne_11_ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZLigne_12_ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ

wc -l longueur_fixe 1 longueur_fixe

$ sed 's/.\{48\}/&\ /g;' longueur_fixe Ligne_1_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_2_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_3_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_4_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_5_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_6_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_7_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_8_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_9_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_10_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_11_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_12_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ

$ perl -pe 's/(.{48})/$1\n/g;' longueur_fixe Ligne_1_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_2_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_3_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_4_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_5_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_6_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_7_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_8_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_9_ _aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_10_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_11_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ Ligne_12_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZZZ

Discussion
Cette situation se rencontre le plus souvent lorsque les programmeurs gnre une sortie, notamment en utilisant des modules tout faits, en particulier pour du contenu HTML ou XML.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.18. Convertir un fichier de donnes au format CSV

287

Vous noterez que la syntaxe des substitutions de sed permet dinclure des sauts de ligne. Dans sed, une esperluette littrale (&) sur le ct droit dune substitution est remplace par lexpression correspondante sur le ct gauche. La barre oblique inverse finale (\) sur la premire ligne chappe le saut de ligne afin que le shell laccepte, mais il fait partie de la partie droite de la substitution de sed. En effet, sed ne reconnat pas \n comme un mta-caractre lorsquil se trouve dans la partie droite de s///.

Voir aussi
http://sed.sourceforge.net/sedfaq.html ; Effective awk Programming de Arnold Robbins (OReilly Media) ; sed & awk de Arnold Robbins et Dale Dougherty (OReilly Media) ; la recette 12.5, Comparer deux documents, page 254 ; la recette 13.16, Traiter des enregistrements de longueur fixe, page 283.

13.18. Convertir un fichier de donnes au format CSV


Problme
Vous souhaitez convertir votre fichier de donnes au format CVS (Comma Separated Values).

Solution
Utilisez awk pour convertir les donnes au format CSV :
$ awk 'BEGIN { FS="\t"; OFS="\",\"" } { gsub(/"/, "\"\""); "\"%s\"\n", $0}' avec_tab "Ligne 1","Champ 2","Champ 3","Champ 4 avec ""guillemets"" "Ligne 2","Champ 2","Champ 3","Champ 4 avec ""guillemets"" "Ligne 3","Champ 2","Champ 3","Champ 4 avec ""guillemets"" "Ligne 4","Champ 2","Champ 3","Champ 4 avec ""guillemets"" $1 = $1; printf internes" internes" internes" internes"

Vous pouvez galement obtenir le mme rsultat avec Perl :


$ perl -naF'\t' -e 'chomp @F; s/"/""/g @F).qq("\n);' avec_tab "Ligne 1","Champ 2","Champ 3","Champ 4 "Ligne 2","Champ 2","Champ 3","Champ 4 "Ligne 3","Champ 2","Champ 3","Champ 4 "Ligne 4","Champ 2","Champ 3","Champ 4 for @F; print q(").join(q(","), avec avec avec avec ""guillemets"" ""guillemets"" ""guillemets"" ""guillemets"" internes" internes" internes" internes"

Discussion
Tout dabord, il nest pas facile de donner une dfinition prcise de CSV. Il nexiste aucune spcification formelle et autant de versions que de fournisseurs. Dans notre cas, la

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

288

Chapitre 13 Analyses et tches similaires

version est trs simple et devrait fonctionner sur tous les systmes. Nous plaons des guillemets autour de tous les champs (certaines implmentations placent des guillemets uniquement autour des chanes ou celles contenant des virgules) et nous doublons les guillemets internes. Pour cela, nous demandons awk de dcomposer les champs de lentre en utilisant la tabulation comme sparateur et nous fixons le sparateur de sortie (OFS) ",". Ensuite, nous remplaons globalement tout guillemet par deux guillemets, nous effectuons une affectation afin que awk reconstruise lenregistrement (voir la recette 13.14, page 277) et nous affichons celui-ci avec des guillemets de dbut et de fin. Il nous a fallu chapper les guillemets en plusieurs endroits. La commande est donc peu lisible, mais elle est relativement simple.

Voir aussi
la FAQ de awk ; la recette 13.14, Supprimer les espaces, page 277 ; la recette 13.19, Analyser un fichier CSV, page 288.

13.19. Analyser un fichier CSV


Problme
Vous disposez dun fichier de donnes au format CSV (Comma Separated Values) et vous souhaitez lanalyser.

Solution
Contrairement la recette prcdente qui convertit un fichier au format CSV, il nexiste aucune solution simple pour celle-ci. En effet, il est assez difficile de dfinir prcisment ce que signifie CSV. Voici les pistes que vous pouvez explorer : sed : http://sed.sourceforge.net/sedfaq4.html#s4.12 ; awk : http://lorance.freeshell.org/csv/ ; Perl : le livre Matrise des expressions rgulires, 2e dition de Jeffrey E. F. Friedl (ditions OReilly) propose une expression rgulire pour cette manipulation ; Perl : voir CPAN (http://www.cpan.org/) pour les diffrents modules disponibles ; chargez le fichier CSV dans un tableur (Calc dOpenOffice et Excel de Microsoft feront parfaitement laffaire), puis copiez et collez le contenu dans un diteur de texte. Vous devriez obtenir un contenu dlimit par des tabulations que vous pouvez alors manipuler facilement.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

13.19. Analyser un fichier CSV

289

Discussion
Comme nous lavons indiqu la recette 13.18, page 287, il nexiste aucune spcification formelle de CSV. Combine aux diffrents types de donnes, cette situation rend lanalyse dun fichier CSV plus complique quil ny parat.

Voir aussi
la recette 13.18, Convertir un fichier de donnes au format CSV, page 287.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14
Scripts scuriss
Comment des scripts shell peuvent-ils tre scuriss alors quil est toujours possible den lire le code source ? Les systmes qui veulent dissimuler les dtails dimplmentation sappuient sur une scurit par lobscurit, mais cette scurit nest quapparente. Pour sen convaincre, il suffit simplement dinterroger les fournisseurs de logiciels dont les codes sources sont gards secrets. Leurs produits nen restent pas moins vulnrables aux attaques dveloppes par des personnes qui nont jamais eu accs ces sources. loppos, le code source dOpenSSH et dOpenBSD, qui est totalement disponible, est trs sr. La scurit par lobscurit ne tiendra pas longtemps, mme si, sous certaines formes, elle peut apporter un niveau de scurit supplmentaire. Par exemple, lorsque les dmons coutent sur des numros de ports non standard, les pirates nophytes ont plus de difficults perptrer leurs forfaits. En revanche, la scurit par lobscurit ne doit jamais tre employe seule car, tt ou tard, quelquun dcouvrira ce que vous cachez. Comme lexplique Bruce Schneier, la scurit est un processus. Il ne sagit pas dun produit, dun objet ou dune technique, et elle nest jamais termine. Les technologies, les rseaux, les attaques et les dfenses voluent. Ce doit galement tre le cas de votre systme de scurit. Par consquent, comment peut-on crire des scripts shell scuriss ? Les scripts shell scuriss raliseront leurs tches de manire fiable et uniquement ces tches. Ils ne seront pas des portes vers les autorisations de root, ne permettront pas le lancement accidentel dune commande rm -rf / et ne dvoileront pas des informations sensibles, comme les mots de passe. Ils seront robustes, mais choueront avec lgance. Ils tolreront les erreurs de lutilisateur et valideront toutes ses entres. Ils seront aussi simples que possible et contiendront uniquement du code clair et lisible, ainsi quune description du fonctionnement de chaque ligne ambigu. Cette description convient tout programme robuste bien conu. La scurit doit tre incluse ds le dbut de la conception et non pas ajoute la fin. Dans ce chapitre, nous prsentons les faiblesses et les problmes de scurit les plus courants, ainsi que leurs solutions.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

292

Chapitre 14 Scripts scuriss

La littrature concernant la scurit est importante. Si ce sujet vous intresse, vous pouvez commencer par le livre Practical UNIX & Internet Security de Gene Spafford et autres (OReilly Media). Le chapitre 15 de louvrage Introduction aux scripts shell de Nelson H.F. Beebe et Arnold Robbins (ditions OReilly) constitue une autre ressource indispensable. Il existe galement de nombreux documents en ligne, comme A Lab engineers check list for writing secure Unix code http://www.auscert.org.au/render.html?it=1975. Le code suivant reprend les techniques universelles dcriture dun script scuris. En les runissant dans un mme fichier, vous pourrez y faire rfrence plus facilement lorsque vous en aurez besoin. Prenez le temps de lire attentivement les recettes correspondant chaque technique afin de bien les comprendre.
#!/usr/bin/env bash # bash Le livre de recettes : modele_securite # Dfinir un chemin sr. PATH='/usr/local/bin:/bin:/usr/bin' # Il est sans doute dj export, mais mieux vaut s'en assurer. \export PATH # Effacer tous les alias. Important : le caractre \ de dbut # vite le dveloppement des alias. \unalias -a # Effacer les commandes mmorises. hash -r # Fixer la limite stricte 0 afin de dsactiver les fichiers core. ulimit -H -c 0 -# Dfinir un IFS sr (cette syntaxe est celle de bash et de ksh93, # elle n'est pas portable). IFS=$' \t\n' # Dfinir une variable umask sre et l'utiliser. Cela n'affecte pas # les fichiers dj redirigs sur la ligne de commande. 002 donne les # autorisations 0774, 077 les autorisations 0700, etc. UMASK=002 umask $UMASK until [ -n "$rep_temp" -a ! -d "$rep_temp" ]; do rep_temp="/tmp/prefixe_significatif.${RANDOM}${RANDOM}${RANDOM}" done mkdir -p -m 0700 $rep_temp \ || (echo "FATAL : impossible de crer le rpertoire temporaire" \ "'$rep_temp' : $?"; exit 100) # Nettoyer au mieux les fichiers temporaires. La variable # $rep_temp doit tre fixe avant ces instructions et ne doit # pas tre modifie ! nettoyage="rm -rf $rep_temp" trap "$nettoyage" ABRT EXIT HUP INT QUIT
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.1. viter les problmes de scurit classiques

293

14.1. viter les problmes de scurit classiques


Problme
Vous souhaitez viter les problmes de scurit classiques dans vos scripts.

Solution
Validez toutes les entres externes, y compris les saisies interactives et celles provenant des fichiers de configuration. En particulier, nutilisez jamais une instruction eval sur une entre qui na pas t soigneusement vrifie. Utilisez des fichiers temporaires scuriss, idalement dans des rpertoires temporaires scuriss. Vrifiez que les programmes excutables externes utiliss sont dignes de confiance.

Discussion
Dune certaine manire, cette recette aborde peine la scurit des scripts et des systmes. Nanmoins, elle dcrit les problmes que vous rencontrerez le plus souvent. La validation des donnes, ou plutt son absence, reprsente un point important de la scurit dun ordinateur. Elle est lorigine des dbordements de tampons, qui constituent les attaques les plus rpandues. Ces problmes naffectent pas bash de la mme manire que C, mais les concepts sont identiques. En bash, il est plus probable quune entre non valide contienne une commande du type ; rm -rf / plutt quun dbordement de tampon. Quoi quil en soit, vous devez valider vos donnes ! La concurrence critique constitue galement un autre point important. Elle est li au problme dun assaillant obtenant la possibilit dcrire dans certains fichiers. Une concurrence critique se produit lorsque deux vnements distincts, ou plus, doivent arriver dans un certain ordre un certain moment, sans interfrences externes. Le plus souvent, ils donnent un utilisateur non privilgi des accs en lecture et/ou en criture des fichiers quil ne devrait pas pouvoir manipuler et, par lvation des privilges, lui apporte des accs root. Une utilisation non scurise des fichiers temporaires est souvent lorigine de ce type dattaques. Pour lviter, il suffit demployer les fichiers temporaires scuriss, si possible dans des rpertoires temporaires scuriss. Les utilitaires infests par un cheval de Troie constituent une autre source dattaques. Tout comme le cheval de Troie, ils ne sont pas ce quils semblent tre. Lexemple classique est la version dtourne de la commande ls, qui fonctionne exactement comme la commande ls relle, sauf lorsquelle est excute par root. Dans ce cas, elle cre un nouvel utilisateur nomm r00t, avec un mot de passe par dfaut connu de lassaillant, et supprime son fichier excutable (sauto-dtruit). Du ct des scripts, vous pouvez au mieux dfinir une variable $PATH sre. Du point de vue du systme, de nombreux outils, comme Tripwire et AIDE, peuvent vous aider garantir son intgrit.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

294

Chapitre 14 Scripts scuriss

Voir aussi
http://www.tripwiresecurity.com/ ; http://www.cs.tut.fi/~rammer/aide.html ; http://osiris.shmoo.com/.

14.2. viter lusurpation de linterprteur


Problme
Vous souhaitez viter certaines formes dattaques par usurpation avec les accs administrateur (setuid root).

Solution
Ajoutez un seul tiret la fin du shell :
#!/bin/bash -

Discussion
La premire ligne dun script (souvent appele ligne shebang) dsigne linterprteur utilis pour traiter la suite du fichier. Le systme recherche galement une seule option passe linterprteur indiqu. Certaines attaques exploitent ce fonctionnement. Si vous passez explicitement un argument, elles sont donc vites. Pour plus de dtails, consultez le document http://www.faqs.org/faqs/unix-faq/faq/part4/section-7.html. Nanmoins, en figeant le chemin de bash, vous pourrez rencontrer des problmes de portabilit. Pour plus dinformations, consultez la recette 15.1, page 334.

Voir aussi
la recette 14.15, crire des scripts setuid ou setgid, page 312 ; la recette 15.1, Trouver bash de manire portable, page 334.

14.3. Dfinir une variable $PATH sre


Problme
Vous souhaitez tre certain que votre chemin est sr.

Solution
Fixez $PATH une valeur rpute valide au dbut de chaque script :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.3. Dfinir une variable $PATH sre


# Dfinir un chemin sr. PATH='/usr/local/bin:/bin:/usr/bin' # Il est sans doute dj export, mais mieux vaut s'en assurer. export PATH

295

Vous pouvez galement employer getconf pour obtenir un chemin permettant daccder tous les outils standard (garanti par POSIX) :
export PATH=$(getconf PATH)

Discussion
Lexemple prcdent conduit deux problmes de portabilit. Premirement, `` est plus portable (mais moins lisible) que $(). Deuximement, lajout de la commande export sur la mme ligne que laffectation de la variable nest pas toujours pris en charge. var='toto'; export var est plus portable que export var='toto'. Notez galement quune seule invocation de la commande export est ncessaire pour quune variable soit exporte vers tous les processus enfants. Si vous nemployez pas getconf, notre exemple propose un bon chemin initial, mais vous devrez sans doute lajuster votre environnement ou vos besoins particuliers. Vous pouvez galement utiliser une version moins portable :
export PATH='/usr/local/bin:/bin:/usr/bin'

Selon les risques de scurit et les besoins, vous pouvez indiquer des chemins absolus. Cette approche devient rapidement lourde et peut constituer un problme lorsque la portabilit est importante, car les diffrents systmes dexploitation ne placent pas les outils aux mmes endroits. Pour limiter ces problmes, utilisez des variables. Si vous procdez ainsi, noubliez pas de les trier afin de ne pas rpter trois fois la mme commande parce que vous auriez manqu les deux autres instances lors du contrle de la liste non trie. Cette mthode a galement pour avantage de prsenter rapidement les outils employs par le script. Vous pouvez mme ajouter une fonction simple qui vrifie que chaque outil est disponible et excutable avant que le script neffectue son travail.
#!/usr/bin/env bash # bash Le livre de recettes : trouver_outils # export peut tre ncessaire, selon ce que vous faites. # Chemins relativement fiables. _cp='/bin/cp' _mv='/bin/mv' _rm='/bin/rm' # Chemins moins universels. case $(/bin/uname) in 'Linux') _cut='/bin/cut' _nice='/bin/nice' # [...] ;;
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

296
'SunOS') _cut='/usr/bin/cut' _nice='/usr/bin/nice' # [...] ;; # [...] esac

Chapitre 14 Scripts scuriss

Faites attention aux noms des variables. Certains programmes, comme InfoZip, utilisent des variables denvironnement, comme $ZIP et $UNZIP, pour le passage des paramtres. Par consquent, si vous excutez une commande de type ZIP='/usr/bin/zip', vous risquez de passer plusieurs jours vous demander pourquoi les instructions fonctionnent parfaitement sur la ligne de commande mais pas dans votre script. Croyez-nous sur parole, nous en avons fait les frais. Noubliez pas non plus de lire la documentation.

Voir aussi
la recette 6.14, Raliser des branchements multiples, page 137 ; la recette 6.15, Analyser les arguments de la ligne de commande, page 139 ; la recette 14.9, Trouver les rpertoires modifiables mentionns dans $PATH, page 300 ; la recette 14.10, Ajouter le rpertoire de travail dans $PATH, page 303 ; la recette 15.2, Dfinir une variable $PATH de type POSIX, page 335 ; la recette 16.3, Modifier dfinitivement $PATH, page 376 ; la recette 16.4, Modifier temporairement $PATH, page 377 ; la recette 19.3, Oublier que le rpertoire de travail nest pas dans $PATH, page 488 ; la section Commandes internes et mots rservs, page 508.

14.4. Effacer tous les alias


Problme
Vous souhaitez vous assurer quaucun alias malveillant ne se trouve dans votre environnement.

Solution
Utilisez la commande \unalias -a pour effacer tous les alias existants.

Discussion
Si un assaillant parvient faire excuter une commande root ou un autre utilisateur, il peut russir obtenir un accs des donnes ou des privilges qui lui sont interdits.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.5. Effacer les commandes mmorises

297

Une manire de procder consiste crer un alias dun autre programme commun (par exemple ls). Le caractre \ plac au dbut de la commande permet dviter le dveloppement des alias. Il est trs important car il empche des comportements comme les suivants :
$ alias unalias=echo $ alias builtin=ls $ builtin unalias vi ls: unalias: Aucun fichier ou rpertoire de ce type ls: vi: Aucun fichier ou rpertoire de ce type $ unalias -a -a

Voir aussi
la recette 10.7, Redfinir des commandes avec des alias, page 219 ; la recette 10.8, Passer outre les alias ou les fonctions, page 221 ; la recette 16.6, Raccourcir ou modifier des noms de commandes, page 385.

14.5. Effacer les commandes mmorises


Problme
Vous souhaitez vrifier que la mmoire des commandes na pas t dtourne.

Solution
Utilisez la commande hash -r pour effacer les commandes mmorises.

Discussion
Lors de lexcution des commandes, bash mmorise lemplacement de celles qui se trouvent dans la variable $PATH afin dacclrer les invocations ultrieures. Si un assaillant parvient faire excuter une commande root ou un autre utilisateur, il peut russir obtenir un accs des donnes ou des privilges qui lui sont interdits. Une manire de procder consiste modifier la mmoire des commandes afin que le programme souhait soit excut.

Voir aussi
la recette 14.9, Trouver les rpertoires modifiables mentionns dans $PATH, page 300 ; la recette 14.10, Ajouter le rpertoire de travail dans $PATH, page 303 ; la recette 15.2, Dfinir une variable $PATH de type POSIX, page 335 ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

298

Chapitre 14 Scripts scuriss

la recette 16.3, Modifier dfinitivement $PATH, page 376 ; la recette 16.4, Modifier temporairement $PATH, page 377 ; la recette 19.3, Oublier que le rpertoire de travail nest pas dans $PATH, page 488.

14.6. Interdire les fichiers core


Problme
Vous souhaitez empcher que votre script cre un fichier core en cas derreur non rcuprable. En effet, ces fichiers peuvent contenir des informations sensibles, comme des mots de passe, provenant de la mmoire.

Solution
Utilisez la commande interne ulimit pour fixer la taille des fichiers core 0, en gnral dans votre fichier .bashrc :
ulimit -H -c 0 --

Discussion
Les fichiers core sont employs pour le dbogage et constituent une image de la mmoire du processus au moment du dysfonctionnement. Par consquent, ils contiennent tout ce que le processus avait stock en mmoire, par exemple les mots de passe saisis par lutilisateur. La commande prcdente peut tre place dans un fichier systme, comme /etc/profile ou /etc/bashrc, que les utilisateurs ne peuvent modifier.

Voir aussi
help ulimit.

14.7. Dfinir une variable $IFS sre


Problme
Vous souhaitez que votre variable denvironnement IFS (Internal Field Separator) soit correcte.

Solution
Fixez-la un tat reconnu au dbut de chaque script en utilisant la syntaxe suivante (non compatible POSIX) :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.8. Dfinir un umask sr


# Dfinir un IFS sr (cette syntaxe est celle de bash et de ksh93, # elle n'est pas portable). IFS=$' \t\n'

299

Discussion
Comme nous le signalons, cette syntaxe nest pas portable. Cependant, la version portable nest pas fiable car elle peut tre facilement modifie par les diteurs qui suppriment les espaces. En gnral, les valeurs sont lespace, la tabulation et le saut de ligne ; lordre est important. $*, qui retourne tous les paramtres positionnels, les remplacements de paramtres spciaux ${!prfixe@} et ${!prfixe*}, ainsi que la compltion programmable, utilisent tous la premire valeur de $IFS comme sparateur. La mthode dcriture classique laisse une espace et une tabulation la fin de la premire ligne :
1 2 IFS=' ' &#182;

Lordre saut de ligne, espace, puis tabulation est moins sujet aux suppressions, mais peut conduire des rsultats inattendus avec certaines commandes :
1 2 IFS='&#182; '

Voir aussi
la recette 13.14, Supprimer les espaces, page 277.

14.8. Dfinir un umask sr


Problme
Vous souhaitez dfinir un umask fiable.

Solution
Utilisez la commande interne umask pour dfinir un tat reconnu au dbut de chaque script :
# Dfinir une variable umask sre et l'utiliser. Cela n'affecte pas # les fichiers dj redirigs sur la ligne de commande. 002 donne les # autorisations 0774, 077 les autorisations 0700, etc. UMASK=002 umask $UMASK

Discussion
Nous dfinissons une variable $UMASK car nous pourrions avoir besoin de masques diffrents dans le programme. Vous pouvez parfaitement vous en passer :
umask 002

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

300

Chapitre 14 Scripts scuriss

Noubliez pas que umask est un masque qui prcise les bits retirer de lautorisation par dfaut (777, pour les rpertoires, et 666, pour les fichiers). En cas de doutes, faites des tests :
# Dmarrer un nouveau shell afin de ne pas perturber # lenvironnement actuel. /tmp$ bash # Vrifier la configuration en cours. /tmp$ touch umask_actuel # Vrifier dautres configurations. /tmp$ umask 000 ; touch umask_000 /tmp$ umask 022 ; touch umask_022 /tmp$ umask 077 ; touch umask_077 /tmp$ ls -l umask_* -rw-rw-rw- 1 jp jp 0 -rw-r--r-- 1 jp jp 0 -rw------- 1 jp jp 0 -rw-rw-r-- 1 jp jp 0

2007-07-30 2007-07-30 2007-07-30 2007-07-30

12:23 12:23 12:23 12:23

umask_000 umask_022 umask_077 umask_actuel

# Nettoyer et quitter le sous-shell. /tmp$ rm umask_* /tmp$ exit

Voir aussi
help umask ; http://linuxzoo.net/page/sec_umask.html.

14.9. Trouver les rpertoires modifiables mentionns dans $PATH


Problme
Vous souhaitez vous assurer que la variable $PATH de root ne contient aucun rpertoire modifiable par tous les utilisateurs. Pour connatre les raisons de cette exigence, lisez la recette 14.10, page 303.

Solution
Le simple script suivant permet de vrifier la variable $PATH. Invoquez-le avec su - ou sudo pour vrifier les chemins des autres utilisateurs :
#!/usr/bin/env bash # bash Le livre de recettes : verifier_path.1 # Vrifier si la variable $PATH contient des rpertoires inexistants ou # modifiables par tous les utilisateurs.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.9. Trouver les rpertoires modifiables mentionns dans $PATH


code_sortie=0 for rep in ${PATH//:/ }; do [ -L "$rep" ] && printf "%b" "lien symbolique, " if [ ! -d "$rep" ]; then printf "%b" "manquant\t\t" (( code_sortie++ )) elif [ "$(ls -lLd $rep | grep '^d.......w. ')" ]; then printf "%b" "modifiable par tous les utilisateurs\t" (( code_sortie++ )) else printf "%b" "ok\t\t" fi printf "%b" "$rep\n" done exit $code_sortie

301

Par exemple :
# ./verfier_path.1 ok /usr/local/sbin ok /usr/local/bin ok /sbin ok /bin ok /usr/sbin ok /usr/bin ok /usr/X11R6/bin ok /root/bin manquant /inexistant modifiable par tous les utilisateurs /tmp lien symbolique, modifiable par tous les utilisateurs /tmp/bin lien symbolique, ok /root/sbin

Discussion
Nous convertissons le contenu de la variable $PATH en une liste spare par des espaces grce une technique dcrite la recette 9.11, page 202. Chaque rpertoire est ensuite compar un lien symbolique (-L) et son existence est vrifie (-d). Puis, nous gnrons une longue liste (-l) des rpertoires, en drfrenant les liens symboliques (-L) et en ne gardant que les noms des rpertoires (-d), sans leur contenu. Enfin, nous dterminons les rpertoires modifiables par tous les utilisateurs laide de grep. Comme vous pouvez le constater, nous espaons les rpertoires ok, tandis que ceux ayant un problme peuvent tre plus resserrs. Dautre part, nous ne respectons pas la rgle habituelle des outils Unix, qui restent silencieux except en cas de problme, car nous estimons quil est intressant de savoir ce qui se trouve dans le chemin, en plus de la vrification automatique. Lorsquaucun problme na t dtect dans la variable $PATH, le code de sortie a la valeur zro. Dans le cas contraire, il comptabilise le nombre derreurs trouves. En modifiant lgrement ce code, nous pouvons ajouter le mode, le propritaire et le groupe du fichier. Ces informations peuvent galement tre intressantes contrler :
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

302

Chapitre 14 Scripts scuriss

#!/usr/bin/env bash # bash Le livre de recettes : verifier_path.2 # Vrifier si la variable $PATH contient des rpertoires inexistants ou # modifiables par tous les utilisateurs, avec des statistiques. code_sortie=0 for rep in ${PATH//:/ }; do [ -L "$rep" ] && printf "%b" "lien symbolique, " if [ ! -d "$rep" ]; then printf "%b" "manquant\t\t\t\t" (( code_sortie++ )) else stat=$(ls -lHd $rep | awk '{print $1, $3, $4}') if [ "$(echo $stat | grep '^d.......w. ')" ]; then printf "%b" "modifiable par tous les utilisateurs\t$stat " (( code_sortie++ )) else printf "%b" "ok\t\t$stat " fi fi printf "%b" "$rep\n" done exit $code_sortie

Par exemple :
# ./verifier_path.2 ; echo $? ok drwxr-xr-x root root /usr/local/sbin ok drwxr-xr-x root root /usr/local/bin ok drwxr-xr-x root root /sbin ok drwxr-xr-x root root /bin ok drwxr-xr-x root root /usr/sbin ok drwxr-xr-x root root /usr/bin ok drwxr-xr-x root root /usr/X11R6/bin ok drwx------ root root /root/bin manquant /inexistant modifiable par tous les utilisateurs drwxrwxrwt root root /tmp lien symbolique, ok drwxr-xr-x root root /root/sbin 2

Voir aussi
la recette 9.11, Retrouver un fichier partir dune liste demplacements possibles, page 202 ; la recette 14.10, Ajouter le rpertoire de travail dans $PATH, page 303 ; la recette 15.2, Dfinir une variable $PATH de type POSIX, page 335 ; la recette 16.3, Modifier dfinitivement $PATH, page 376 ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.10. Ajouter le rpertoire de travail dans $PATH


la recette 16.4, Modifier temporairement $PATH, page 377 ; la recette 19.3, Oublier que le rpertoire de travail nest pas dans $PATH, page 488.

303

14.10. Ajouter le rpertoire de travail dans $PATH


Problme
Vous ne voulez plus saisir ./script pour excuter le script qui se trouve dans le rpertoire de travail et prfrez simplement ajouter . (ou un rpertoire vide, cest--dire un caractre : initial ou final, ou bien :: au milieu) la variable $PATH.

Solution
Nous dconseillons cette configuration pour nimporte quel utilisateur et recommandons de la bannir pour root. Si vous devez absolument ajouter le rpertoire de travail la variable $PATH, vrifiez que . se trouve en dernier. Ne le faites jamais en tant que root.

Discussion
Comme vous le savez, le shell examine les rpertoires indiqus dans $PATH lorsque vous entrez le nom dune commande sans prciser son chemin. La raison de ne pas ajouter . est la mme que celle dinterdire les rpertoires modifiables par tous les utilisateurs dans la variable $PATH. Supposons que le rpertoire de travail soit /tmp et que . se trouve au dbut de $PATH. Si vous saisissez ls et que le fichier /tmp/ls existe, cest celui-ci qui est excut et non /bin/ls. Quelles peuvent tre les consquences ? Il est possible que /tmp/ls soit un script malveillant et, si vous lavez excut en tant que root, personne ne peut dire ce qui va se passer. Il peut mme aller jusqu se supprimer lui-mme une fois la trace de ses forfaits efface. Que se passe-t-il si . arrive en dernier ? Vous est-il dj arriv, comme nous, de saisir mc la place de mv ? moins que Midnight Commander ne soit install sur votre systme, vous pouvez alors excuter ./mc alors que vous vouliez /bin/mv. Les rsultats peuvent tre identiques au cas prcdent. En rsum, najoutez pas . la variable $PATH !

Voir aussi
la section 2.13 de http://www.faqs.org/faqs/unix-faq/faq/part2/ ; la recette 9.11, Retrouver un fichier partir dune liste demplacements possibles, page 202 ; la recette 14.3, Dfinir une variable $PATH sre, page 294 ; la recette 14.9, Trouver les rpertoires modifiables mentionns dans $PATH, page 300 ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

304

Chapitre 14 Scripts scuriss

la recette 15.2, Dfinir une variable $PATH de type POSIX, page 335 ; la recette 16.3, Modifier dfinitivement $PATH, page 376 ; la recette 16.4, Modifier temporairement $PATH, page 377 ; la recette 19.3, Oublier que le rpertoire de travail nest pas dans $PATH, page 488.

14.11. Utiliser des fichiers temporaires scuriss


Problme
Vous devez crer un fichier ou un rpertoire temporaire, mais tes soucieux des implications dun nom prvisible sur la scurit.

Solution
La solution la plus simple et gnralement satisfaisante consiste employer $RANDOM dans le script. Par exemple :
# Vrifier que $TMP est dfinie. [ -n "$TMP" ] || TMP='/tmp' # Crer un rpertoire temporaire alatoire "satisfaisant". until [ -n "$rep_temp" -a ! -d "$rep_temp" ]; do rep_temp="/tmp/prefixe_significatif.${RANDOM}${RANDOM}${RANDOM}" done mkdir -p -m 0700 $rep_temp \ || ( echo "FATAL : impossible de crer le rpertoire temporaire" \ "'$rep_temp' : $?"; exit 100 )

# Crer un fichier temporaire alatoire "satisfaisant". until [ -n "$fichier_temp" -a ! -e "$fichier_temp" ]; do fichier_temp="/tmp/prefixe_significatif.${RANDOM}${RANDOM}${RANDOM}" done touch $fichier_temp && chmod 0600 $fichier_temp || ( echo "FATAL : impossible de crer le fichier temporaire" \ "'$fichier_temp' : $?"; exit 101 )

Mieux encore, vous pouvez utiliser un rpertoire temporaire et un nom de fichier alatoires !
# bash Le livre de recettes : creer_temp # Crer un rpertoire temporaire alatoire "satisfaisant". until [ -n "$rep_temp" -a ! -d "$rep_temp" ]; do rep_temp="/tmp/prefixe_significatif.${RANDOM}${RANDOM}${RANDOM}" done

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.11. Utiliser des fichiers temporaires scuriss


mkdir -p -m 0700 $rep_temp \ || ( echo "FATAL : impossible de crer le rpertoire temporaire" \ "'$rep_temp' : $?"; exit 100 )

305

# Crer un fichier temporaire alatoire "satisfaisant" dans le # rpertoire temporaire. fichier_temp="$rep_temp/prefixe_significatif.${RANDOM}${RANDOM}${RANDOM}" touch $fichier_temp && chmod 0600 $fichier_temp \ || ( echo "FATAL : impossible de crer le rpertoire temporaire" \ "'$fichier_temp' : $?"; exit 101 )

Quelle que soit la manire dont vous procdez, noubliez pas de dfinir un gestionnaire de signaux afin dassurer le nettoyage. Comme nous lavons not, $rep_temp doit tre dfini avant que ce gestionnaire soit dclar et sa valeur ne doit pas changer. Si ces points ne sont pas respects, modifiez le code afin de ladapter vos besoins.
# bash Le livre de recettes : nettoyer_temp # Nettoyer au mieux les fichiers temporaires. La variable # $rep_temp doit tre fixe avant ces instructions et ne doit # pas tre modifie ! nettoyage="rm -rf $rep_temp" trap "$nettoyage" ABRT EXIT HUP INT QUIT

Discussion
$RANDOM existe depuis bash-2.0 et savre souvent suffisante. Un code simple est prfrable et est plus facile scuriser quun code complexe. Ainsi, lemploi de $RANDOM peut rendre votre code plus sr sans avoir prendre en charge les complexits de validation et de vrification des erreurs de mktemp ou de /dev/urandom. Sa simplicit joue en sa faveur. Cependant, $RANDOM ne fournit que des nombres. mktemp gnre des nombres et des lettres majuscules et minuscules, et urandom produit des nombres et des lettres minuscules. Ces deux outils largissent normment lespace des cls. Quelle que soit la manire dont il est cr, un rpertoire de travail temporaire prsente les avantages suivants : mkdir -p -m 0700 $rep_temp vite la concurrence critique inhrente touch $fichier_temp && chmod 0600 $fichier_temp ; les fichiers crs lintrieur du rpertoire ne sont pas visibles un assaillant nonroot lorsque ce rpertoire possde les autorisations 0700 ; avec un rpertoire temporaire, il est plus facile de sassurer que tous les fichiers temporaires sont supprims lorsquils sont devenus inutiles. Si les fichiers temporaires sont parpills, il est trs facile den oublier un lors du nettoyage ; vous pouvez choisir des noms significatifs pour les fichiers temporaires de ce rpertoire, ce qui facilite le dveloppement et le dbogage et amliore la scurit et la robustesse du script ; lutilisation dun prfixe significatif dans le chemin indique clairement les scripts en excution (cette option peut tre bonne ou mauvaise, mais sachez que ps et /proc font de mme). Par ailleurs, elle peut permettre dindiquer que le nettoyage dun script a chou et donc viter des fuites dinformations.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

306

Chapitre 14 Scripts scuriss

Le code prcdent conseille dutiliser un prefixe_significatif dans le nom de chemin cr. Certains dveloppeurs prtendent que cela rduit la scurit puisque ce nom est prvisible. Cette partie du chemin est effectivement prvisible, mais nous pensons que les avantages apports surpassent cette objection. Si vous ntes pas daccord, omettez simplement le prfixe significatif. En fonction des risques et de vos besoins scuritaires, vous prfrerez peut-tre utiliser des fichiers temporaires alatoires lintrieur du rpertoire temporaire alatoire, comme nous lavons fait prcdemment. Cela namliore probablement pas la scurit, mais si cela vous rassure, procdez de cette manire. Nous avons mentionn lexistence dune concurrence critique dans la commande touch $fichier_temp && chmod 0600 $fichier_temp. Voici une manire de lviter :
umask_memorise=$(umask) umask 077 touch $fichier_temp umask $umask_memorise unset umask_memorise

Nous vous conseillons dutiliser un rpertoire temporaire alatoire ainsi quun nom de fichier alatoire (ou semi-alatoire) puisque les avantages sont plus nombreux. Si la nature uniquement numrique de $RANDOM vous ennuie, vous pouvez combiner dautres sources de donnes pseudo-imprvisibles et pseudo-alatoires avec une fonction de hachage :
longue_chaine_aleatoire=$( (last ; who ; netstat -a ; free ; date \ ; echo $RANDOM) | md5sum | cut -d' ' -f1 )
Nous vous dconseillons cette mthode car la complexit supplmentaire est probablement un remde pire que le mal. Nanmoins, elle permet de voir que les choses peuvent tre rendues beaucoup plus complexes quil est ncessaire.

Une approche thoriquement plus sre consiste employer lutilitaire mktemp prsent sur de nombreux systmes modernes, avec un repli sur /dev/urandom, galement disponible sur de nombreux systmes rcents, ou mme $RANDOM. Cependant, mktemp et /dev/urandom ne sont pas toujours disponibles et la prise en compte de ce problme de manire portable est beaucoup plus complexe que notre solution.
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # Tenter de crer un nom de fichier ou un rpertoire temporaire scuris. # Usage : $fichier_temp=$(CreerTemp <fichier|rep> [chemin/vers/prfixe]) # Retourne le nom alatoire dans NOM_TEMP. # Par exemple : # $rep_temp=$(CreerTemp rep /tmp/$PROGRAMME.foo) # $fichier_temp=$(CreerTemp fichier /tmp/$PROGRAMME.foo) # function CreerTemp { # Vrifier que $TMP est dfinie. [ -n "$TMP" ] || TMP='/tmp'

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.11. Utiliser des fichiers temporaires scuriss


local nom_type=$1 local prefixe=${2:-$TMP/temp} # Si le prfixe nest pas indiqu, # utiliser $TMP + temp. local type_temp='' local controle='' case $nom_type in fichier ) type_temp='' ur_cmd='touch' # Fichier normal Lisible Mappartenant controle='test -f $NOM_TEMP -a -r $NOM_TEMP -a -a -O $NOM_TEMP' ;; rep|repertoire ) type_temp='-d' ur_cmd='mkdir -p -m0700' # Rpertoire Lisible Parcourable Mappartenant controle='test -d $NOM_TEMP -a -r $NOM_TEMP -a -a -x $NOM_TEMP -a -O $NOM_TEMP' ;; * ) Error "\nType erron dans $PROGRAMME:CreerTemp fichier|rep." 1 ;; esac # Tout dabord, essayer mktemp. NOM_TEMP=$(mktemp $type_temp ${prefixe}.XXXXXXXXX)

307

Modifiable -w $NOM_TEMP

Modifiable -w $NOM_TEMP

! Prciser

# En cas dchec, essayer urandom. Si cela choue, abandonner. if [ -z "$NOM_TEMP" ]; then NOM_TEMP="${prefixe}.$(cat /dev/urandom | od -x | tr -d ' ' | head -1)" $ur_cmd $NOM_TEMP fi # Vrifier que le fichier ou le rpertoire a bien t cr. # Sinon, quitter. if ! eval $controle; then Error "\aERREUR FATALE : impossible de crer $nom_type avec '$0:CreerTemp $*'!\n" 2 else echo "$NOM_TEMP" fi } # Fin de la fonction CreerTemp.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

308

Chapitre 14 Scripts scuriss

Voir aussi
man mktemp ; la recette 14.13, Fixer les autorisations, page 310 ; lannexe B, Exemples fournis avec bash, page 559, en particulier le script ./scripts. noah/mktmp.bash.

14.12. Valider lentre


Problme
Vous attendez une entre (provenant, par exemple, dun utilisateur ou dun programme) et, pour assurer la scurit ou lintgrit des donnes, vous devez vrifier que vous avez obtenu ce que vous aviez demand.

Solution
Il existe diffrentes manires de valider lentre, en fonction de sa nature et de lexactitude souhaite. Pour les cas simples de type elle convient ou ne convient pas , utilisez les correspondances de motifs (voir les recettes 6.6, page 124, 6.7, page 126, et 6.8, page 127).
[[ "$entree_brute" == *.jpg ]] && echo "Fichier JPEG reu."

Lorsque plusieurs possibilits sont valides, employez une instruction case (voir les recettes 6.14, page 137, et 6.15, page 139).
# bash Le livre de recettes : valider_avec_case case $entree_brute in *.societe.fr ;; *.jpg ;; *.[jJ][pP][gG] ;; toto | titi ;; [0-9][0-9][0-9] ;; [a-z][a-z][a-z][a-z] ;; * ;; esac

) # Probablement un nom d'hte local. ) # Probablement un fichier JPEG. ) # Probablement un fichier JPEG, insensible # la casse. ) # Saisie de 'toto' ou de 'titi'. ) # Un nombre 3 chiffres. ) # Un mot de 4 caractres en minuscules. ) # Autre chose.

Lorsque la correspondance de motifs nest pas suffisamment prcise et que bash est dune version suprieure ou gale 3.0, utilisez une expression rgulire (voir la recette

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.12. Valider lentre

309

6.8, page 127). Lexemple suivant recherche un nom de fichier sur trois six caractres alphanumriques et lextension .jpg (insensible la casse) :
[[ "$entree_brute" =~ [[:alpha:]]{3,6}\.jpg ]] && echo "Un fichier JPEG."

Discussion
Les versions rcentes de bash proposent un exemple plus complet et plus dtaill, dans examples/scripts/shprompt. Il a t crit par Chet Ramey, le responsable de bash :
# # # # # # # # # # # # shprompt -- give a prompt and get an answer satisfying certain criteria shprompt [-dDfFsy] prompt s = prompt for string f = prompt for filename F = prompt for full pathname to a file or directory d = prompt for a directory name D = prompt for a full pathname to a directory y = prompt for y or n answer Chet Ramey chet@ins.CWRU.Edu

Un exemple similaire se trouve dans examples/scripts.noah/y_or_n_p.bash, crit en 1993 par Noah Friedman, puis converti bash version 2 par Chet Ramey. Vous pouvez galement examiner les exemples ./functions/isnum.bash, ./functions/isnum2 et ./functions/ isvalidip.

Voir aussi
la recette 3.5, Lire lentre de lutilisateur, page 64 ; la recette 3.6, Attendre une rponse Oui ou Non, page 65 ; la recette 3.7, Choisir dans une liste doptions, page 68 ; la recette 3.8, Demander un mot de passe, page 69 ; la recette 6.6, Tester lgalit, page 124 ; la recette 6.7, Tester avec des correspondances de motifs, page 126 ; la recette 6.8, Tester avec des expressions rgulires, page 127 ; la recette 6.14, Raliser des branchements multiples, page 137 ; la recette 6.15, Analyser les arguments de la ligne de commande, page 139 ; la recette 11.2, Fournir une date par dfaut, page 225 ; la recette 13.6, Analyser du texte avec read, page 266 ; la recette 13.7, Analyser avec read dans un tableau, page 267 ; lannexe B, Exemples fournis avec bash, page 559, pour les exemples de bash.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

310

Chapitre 14 Scripts scuriss

14.13. Fixer les autorisations


Problme
Vous souhaitez fixer des autorisations de manire scurise.

Solution
Si, pour des raisons de scurit, vous devez dfinir des autorisations prcises (ou, si vous tes certain que les permissions en place nont pas dimportance, vous pouvez juste les changer), utilisez chmod avec un mode octal sur 4 chiffres :
$ chmod 0755 un_script

Si vous souhaitez uniquement ajouter ou retirer certaines autorisations, tout en conservant les autres, utilisez les oprations + et - en mode symbolique :
$ chmod +x un_script

vitez de fixer rcursivement les autorisations sur tous les fichiers dune structure arborescente avec une commande comme chmod -R 0644 un_rpertoire car les sous-rpertoires deviennent alors non excutables. Autrement dit, vous naurez plus accs leur contenu, vous ne pourrez plus invoquer cd sur eux, ni aller dans leurs sous-rpertoires. la place, utilisez find et xargs avec chmod pour fixer les autorisations des fichiers et des rpertoires individuellement :
$ find un_rpertoire -type f | xargs chmod 0644 # Autorisations de fichier. $ find un_rpertoire -type d | xargs chmod 0755 # Autorisations de rp.

Bien entendu, si vous voulez simplement dfinir les autorisations des fichiers dun seul rpertoire (sans ses sous-rpertoires), allez simplement dans ce rpertoire et fixez-les. Lorsque vous crez un rpertoire, employez une commande mkdir -m mode nouveau_rpertoire. Ainsi, non seulement vous accomplissez deux tches en une commande, mais vous vitez toute concurrence critique entre la cration du rpertoire et la dfinition des autorisations.

Discussion
De nombreux utilisateurs ont lhabitude demployer un mode octal sur trois chiffres. Nous prfrons prciser les quatre chiffres possibles afin dtre parfaitement explicites. Nous prfrons galement le mode octal car il indique clairement les autorisations rsultantes. Vous pouvez employer loprateur absolu (=) en mode symbolique, mais nous sommes des traditionalistes qui ne veulent pas autre chose que la mthode octale. Lorsque vous utilisez le mode symbolique avec + ou -, il est plus difficile de dterminer les autorisations finales car ces oprations sont relatives et non absolues. Malheureusement, il existe de nombreux cas dans lesquels le remplacement des autorisations existantes ne peut se faire laide du mode octal. Vous navez alors pas dautre choix que demployer le mode symbolique, le plus souvent avec + pour ajouter une permission sans perturber celles dj prsentes. Pour plus de dtails, consultez la documentation de la commande chmod propre votre systme et vrifiez que les rsultats obtenus sont ceux attendus.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.14. Afficher les mots de passe dans la liste des processus


$ ls -l -rw-r--r-- 1 jp jp 0 2005-11-06 01:44 script.sh # Rendre le fichier lisible, modifiable et excutable pour son # propritaire, en utilisant le mode octal. $ chmod 0700 script.sh $ ls -l -rwx------ 1 jp jp 0 2005-11-06 01:44 script.sh # Rendre le fichier lisible et excutable pour tout le monde, # en utilisant le mode symbolique. $ chmod ugo+rx *.sh $ ls -l -rwxr-xr-x 1 jp jp 0 2005-11-06 01:45 script.sh

311

Dans le dernier exemple, vous remarquerez que, mme si nous avons ajout (+) rx tout le monde (ugo), le propritaire conserve son autorisation dcriture (w). Cest bien ce que nous voulions. Mais, vous pouvez sans peine imaginer combien il peut tre facile de faire une erreur et de donner une autorisation non voulue. Cest la raison pour laquelle nous prfrons employer le mode octal, si possible, et que nous vrifions toujours les rsultats de notre commande. Dans tous les cas, avant dajuster les autorisations dun grand nombre de fichiers, testez scrupuleusement votre commande. Vous pouvez galement enregistrer les autorisations et les propritaires de fichiers (voir la recette 17.8, page 439).

Voir aussi
man chmod ; man find ; man xargs ; la recette 17.8, Capturer les mta-informations des fichiers pour une restauration, page 439.

14.14. Afficher les mots de passe dans la liste des processus


Problme
ps peut afficher les mots de passe indiqus en clair sur la ligne de commande. Par exemple :
$ ./app_stupide -u utilisateur -p motdepasse & [1] 13301

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

312
$ ps PID 5280 9784 13301

Chapitre 14 Scripts scuriss

TT p0 p0 p0

STAT TIME COMMAND S 0:00.08 -bash R+ 0:00.00 ps S 0:00.01 /bin/sh ./app_stupide -u utilisateur -p motdepasse

Solution
vitez de prciser les mots de passe sur la ligne de commande.

Discussion
Ce nest pas une plaisanterie, nindiquez jamais les mots de passe sur la ligne de commande. De nombreuses applications disposent dune option -p, ou similaire, qui vous invite saisir un mot de passe lorsquil nest pas indiqu sur la ligne de commande. Si cette approche est satisfaisante pour une utilisation interactive, ce nest pas le cas dans les scripts. Vous pourriez tre tent dcrire un script enveloppe simple ou un alias pour encapsuler le mot de passe sur la ligne de commande. Malheureusement, cela ne fonctionne pas car la commande est excute et apparat donc dans la liste des processus. Si la commande peut accepter le mot de passe sur STDIN, vous pouvez le passer de cette manire. Cette approche cre dautres problmes, mais vite au moins laffichage du mot de passe dans la liste des processus.
$ ./app_incorrecte ~.masque/motdepasse_apps_incorrectes

Si cela ne fonctionne pas, vous devrez trouver une autre application, corriger celle que vous utilisez ou faire avec.

Voir aussi
la recette 3.8, Demander un mot de passe, page 69 ; la recette 14.20, Utiliser des mots de passe dans un script, page 319.

14.15. crire des scripts setuid ou setgid


Problme
Vous rencontrez un problme que vous pensez pouvoir rsoudre en fixant le bit setuid ou setgid du script shell.

Solution
Utilisez les autorisations de groupes et de fichiers dUnix et/ou sudo pour accorder aux utilisateurs les privilges minimums dont ils ont besoin pour accomplir leur travail. Lemploi des bits setuid et setgid sur un script shell cre plus de problmes, en particulier de scurit, quil nen rsout. Par ailleurs, certains systmes, comme Linux, ne respec-

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.15. crire des scripts setuid ou setgid

313

tent pas le bit setuid sur les scripts shell et la cration de scripts setuid ajoute des problmes de portabilit, en plus des risques de scurit.

Discussion
Les scripts setuid root sont particulirement dangereux et doivent tre totalement proscrits. la place, utilisez sudo. setuid et setgid ont des significations diffrentes lorsquils sont appliqus des rpertoires ou des fichiers excutables. Pour un rpertoire, lorsque lun de ces bits est positionn, les nouveaux fichiers ou sous-rpertoires crs appartiennent, respectivement, au propritaire ou au groupe du rpertoire. Pour vrifier si le bit setuid est positionn sur un fichier, excutez la commande test -u (pour setgid, invoquez test -g).
$ mkdir rep_suid rep_sgid $ touch fichier_suid fichier_sgid $ ls -l total 8 -rw-r--r--rw-r--r-drwxr-xr-x drwxr-xr-x

1 1 2 2

jp jp jp jp

jp 0 2007-07-31 jp 0 2007-07-31 jp 4096 2007-07-31 jp 4096 2007-07-31

21:34 21:34 21:34 21:34

fichier_sgid fichier_suid rep_sgid rep_suid

$ chmod 4755 rep_suid fichier_suid $ chmod 2755 rep_sgid fichier_sgid $ ls -l total 8 -rwxr-sr-x -rwsr-xr-x drwxr-sr-x drwsr-xr-x

1 1 2 2

jp jp jp jp

jp 0 2007-07-31 jp 0 2007-07-31 jp 4096 2007-07-31 jp 4096 2007-07-31

21:34 21:34 21:34 21:34

fichier_sgid fichier_suid rep_sgid rep_suid

$ [ -u rep_suid ] && echo 'Oui, suid' || echo 'Non, pas suid' Oui, suid $ [ -u rep_sgid ] && echo 'Oui, suid' || echo 'Non, pas suid' Non, pas suid $ [ -g fichier_sgid ] && echo 'Oui, sgid' || echo 'Non, pas sgid' Oui, sgid $ [ -g fichier_suid ] && echo 'Oui, sgid' || echo 'Non, pas sgid' Non, pas sgid

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

314

Chapitre 14 Scripts scuriss

Voir aussi
man chmod ; la recette 14.18, Excuter un script sans avoir les privilges de root, page 317 ; la recette 14.19, Utiliser sudo de manire plus sre, page 318 ; la recette 14.20, Utiliser des mots de passe dans un script, page 319 ; la recette 17.15, Utiliser sudo avec un groupe de commandes, page 454.

14.16. Restreindre les utilisateurs invits


La description du shell restreint donne dans cette recette apparat galement dans le livre Le shell bash, 3e dition de Cameron Newman et Bill Rosenblatt (ditions OReilly).

Problme
Vous souhaitez accepter des utilisateurs invits sur votre systme et restreindre leurs possibilits daction.

Solution
Si possible, vitez les comptes partags car vous perdez alors la comptabilit et crez des problmes logistiques lorsque les utilisateurs partent (vous devez changer le mot de passe et en informer les autres utilisateurs). Crez des comptes spars avec des autorisations aussi rduites que possible, juste suffisantes pour que les utilisateurs puissent effectuer leur travail. Vous pouvez envisager les solutions suivantes : utiliser un environnement chroot, comme lexplique la recette 14.17, page 316 ; passer par SSH pour autoriser des accs non interactifs aux commandes ou aux ressources, comme le dcrit la recette 14.21, page 321 ; offrir un shell bash restreint.

Discussion
Le shell restreint est conu pour placer lutilisateur dans un environnement o ses possibilits de mouvement et dcriture de fichiers sont trs limites. Il est gnralement employ avec les comptes d invits . Vous pouvez restreindre le shell douverture de session dun utilisateur en plaant rbash sur sa ligne dans le fichier /etc/passwd, si cette option a t incluse lors de la compilation du shell. Les contraintes spcifiques imposes par le shell restreint interdit lutilisateur deffectuer les actions suivantes : changer de rpertoire de travail : cd est inoprante. Sil tente de lemployer, il obtient le message derreur de bash cd: restricted ; rediriger la sortie vers un fichier : les oprateurs de redirection >, >|, <> et >> sont interdits ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.16. Restreindre les utilisateurs invits


315

attribuer de nouvelles valeurs aux variables denvironnement $ENV, $BASH_ENV, $SHELL et $PATH ; invoquer des commandes contenant des barres obliques (/). Le shell considrera que les fichiers qui se trouvent en dehors du rpertoire courant nexistent pas ; utiliser la commande interne exec ; prciser un nom de fichier qui contient un / en argument la commande . (source) interne ; importer des dfinitions de fonctions depuis lenvironnement du shell au dmarrage ; ajouter ou supprimer des commandes internes avec les options -f et -d de la commande enable ; spcifier loption -p la commande interne command ; dsactiver le mode restreint avec set +r.

Ces restrictions prennent effet aprs la lecture des fichiers .bash_profile et denvironnement de lutilisateur. De plus, il est sage de fixer le propritaire des fichiers .bash_profile et .bashrc de lutilisateur root et de les mettre en lecture seule. Les rpertoires personnels des utilisateurs doivent galement tre mis en lecture seule. Cela signifie que lenvironnement complet de lutilisateur du shell restreint est configur dans /etc/profile et .bash_profile. Puisque lutilisateur ne peut accder /etc/profile et ne peut modifier .bash_profile, cest ladministrateur systme qui configure lenvironnement comme il le souhaite. Les deux manires classiques de mettre en place de tels environnements consistent crer un rpertoire des commandes sres et de ne mettre que celui-ci dans PATH, et de configurer un menu de commandes depuis lequel lutilisateur ne peut sortir sans quitter le shell.
Le shell restreint ne rsistera pas un assaillant dtermin. Il sera galement peut-tre difficile verrouiller autant que vous le voudriez car de nombreuses applications classiques, comme vi et Emacs, autorisent des chappements vers le shell, qui peuvent passer outre le shell restreint. Utilis de manire adquate, il ajoute un niveau de scurit apprciable, mais il ne doit pas reprsenter la seule scurit.

Le shell Bourne doriginal dispose galement dune version restreinte, appele rsh, qui peut tre confondue avec les outils (rsh, rcp, rlogin, etc.) du Remote Shell (rsh). Le Remote Shell ne nest pas vraiment sr et a t remplac par SSH (le Secure Shell).

Voir aussi
la recette 14.17, Utiliser un environnement chroot, page 316 ; la recette 14.21, Utiliser SSH sans mot de passe, page 321.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

316

Chapitre 14 Scripts scuriss

14.17. Utiliser un environnement chroot


Problme
Vous devez employer un script ou une application qui nest pas digne de confiance.

Solution
Placez le script ou lapplication dans un environnement chroot. La commande chroot change le rpertoire racine du processus en cours en lui attribuant le rpertoire que vous indiquez, puis retourne un shell ou excute la commande prcise. Ainsi, le processus, et donc le programme, est plac dans une prison de laquelle il ne peut, en thorie, schapper et aller vers le rpertoire parent. Par consquent, si lapplication est compromise ou effectue des oprations malveillantes, elle ne peut affecter que la petite partie du systme de fichiers dans laquelle vous lavez confine. Associe un utilisateur aux droits trs limits, cette approche ajoute un niveau de scurit intressant. Malheureusement, la description complte de chroot sort du cadre de cette recette, puisque cette commande pourrait faire lobjet dun livre elle seule. Nous la prsentons ici pour que vous en apprciiez les fonctionnalits.

Discussion
Pourquoi ne peut-on pas tout excuter dans des environnements chroot ? Tout simplement parce que de nombreuses applications ont besoin dinteragir avec dautres applications, des fichiers, des rpertoires ou des sockets qui se trouvent sur le systme de fichiers global. Il sagit du point dlicat des environnements chroot ; lapplication na pas accs ce qui se trouve hors des murs de sa prison et tout ce dont elle a besoin doit donc sy trouver. Plus lapplication est complexe, plus il est difficile de lexcuter dans un environnement chroot. Les applications qui doivent tre accessibles depuis lInternet, comme les serveurs DNS (par exemple, BIND), web et de messagerie (par exemple, Postfix), peuvent tre configures, avec plus ou moins de difficults, pour fonctionner dans un environnement chroot. Pour plus de dtails, consultez la documentation de votre distribution et des applications concernes. chroot est galement utile lors de la reprise dun systme. Aprs avoir dmarr partir dun Live CD et mont le systme de fichier racine sur votre disque dur, vous pourriez avoir excuter un outil, comme Lilo ou Grub, qui, selon votre configuration, devra peut-tre croire quil sexcute rellement sur le systme endommag. Si le Live CD et le systme install ne sont pas trop diffrents, vous pouvez gnralement invoquer chroot sur le point de montage du systme endommag et le rparer. Cela fonctionne car tous les outils, bibliothques, fichiers de configuration et priphriques existent dj dans lenvironnement chroot. En effet, il sagit en ralit dun systme complet, mme sil ne fonctionne pas (encore). Vous devrez peut-tre ajuster votre $PATH pour trouver ce dont vous avez besoin aprs avoir invoqu chroot (cest lun des aspects de si le Live CD et le systme install ne sont pas trop diffrents ).

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.18. Excuter un script sans avoir les privilges de root

317

Vous pourriez galement tre intress par les contrles daccs obligatoires (MAC Mandatory Access Controls) de SELinux (Security Enhanced Linux) de la NSA. MAC permet de cibler trs prcisment au niveau systme ce qui est autoris ou non et les interactions entre les diffrents composants du systme. Une dfinition est appele politique de scurit et ses effets sont trs similaires un environnement chroot, car une application ou un processus ne peut effectuer que ce que la politique lui permet. Red Hat Linux inclut SELinux dans sa version entreprise. SUSE de Novell dispose dune implmentation MAC similaire appele AppArmor. Il existe des implmentations quivalentes pour Solaris, BSD et MacOS X.

Voir aussi
man chroot ; http://www.nsa.gov/selinux/ ; http://fr.wikipedia.org/wiki/Mandatory_access_control ; http://olivier.sessink.nl/jailkit/ ; http://www.jmcresearch.com/projects/jail/.

14.18. Excuter un script sans avoir les privilges de root


Problme
Vous souhaitez excuter vos scripts en tant quutilisateur autre que root, mais vous craignez ne pas tre autoris effectuer les tches ncessaires.

Solution
Excutez vos scripts avec des identifiants dutilisateurs autres que root, cest--dire sous votre compte ou ceux dutilisateurs rservs, et nemployez pas le shell en mode interactif avec le compte root, mais configurez sudo de manire effectuer les tches qui requirent des privilges plus levs.

Discussion
sudo peut tre utilis dans un script aussi facilement que depuis la ligne de commande du shell. En particulier, voyez loption NOPASSWD de sudoers et la recette 14.19, page 318.

Voir aussi
man sudo ; man sudoers ; la recette 14.15, crire des scripts setuid ou setgid, page 312 ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

318

Chapitre 14 Scripts scuriss

la recette 14.19, Utiliser sudo de manire plus sre, page 318 ; la recette 14.20, Utiliser des mots de passe dans un script, page 319 ; la recette 17.15, Utiliser sudo avec un groupe de commandes, page 454.

14.19. Utiliser sudo de manire plus sre


Problme
Vous souhaitez utiliser sudo mais ne voulez pas accorder des privilges trop levs tout le monde.

Solution
Cest trs bien ! Vous vous proccupez de la scurit. Mme si lutilisation de sudo augmente la scurit, sa configuration par dfaut peut tre amliore. Prenez le temps dapprendre employer sudo et le fichier /etc/sudoers. En particulier, vous devez savoir que, dans la plupart des cas, la configuration ALL=(ALL) ALL est inutile ! Bien entendu, elle fonctionnera, mais elle nest pas trs sre. Cela quivaut donner le mot de passe de root tous les utilisateurs, mais sans quils le sachent. Ils disposent des mmes possibilits daction que root. sudo journalise les commandes excutes, mais il est facile de passer outre ces traces en invoquant sudo bash. Vous devez galement bien rf lchir vos besoins. Tout comme vous devez viter la configuration ALL=(ALL) ALL, vous devez viter de grer les utilisateurs un par un. Le fichier sudoers permet dobtenir une gestion trs fine et nous vous conseillons fortement de lutiliser. man sudoers affiche une documentation complte et plusieurs exemples, en particulier dans la section expliquant comment empcher les chappements vers le shell. sudoers accepte quatre types dalias : utilisateur (user), personne pour laquelle on se fait passer (runas), machine (host) et commande (command). Une utilisation judicieuse de ces alias en tant que rles ou groupes permet de rduire la maintenance. Par exemple, vous pouvez dfinir un User_Alias pour UTILISATEURS_COMPILATION, puis, avec Host_Alias, indiquer les machines que ces utilisateurs doivent employer et, avec Cmnd_Alias, prciser les commandes quils doivent invoquer. Si votre stratgie consiste modifier /etc/sudoers sur une machine et le recopier priodiquement sur les autres systmes concerns en utilisant scp avec une authentification cl publique, vous pouvez mettre en place un systme trs scuris qui accorde uniquement les privilges ncessaires.
Lorsque sudo vous demande un mot de passe, il sagit du vtre. Cest-dire celui de votre compte dutilisateur et non de celui de root. Il est assez frquent de commencer pas saisir celui de root.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.20. Utiliser des mots de passe dans un script

319

Discussion
Malheureusement, sudo nest pas install par dfaut sur tous les systmes. Il lest gnralement sur Linux et OpenBSD ; pour les autres systmes, cela varie. Consultez la documentation de votre systme et installez sudo sil nest pas dj prsent.
Vous devez toujours modifier le fichier /etc/sudoers laide de visudo. Comme vipw, visudo verrouille le fichier afin quune seule personne la fois puisse lditer et il effectue certains contrles de syntaxe avant de remplacer le fichier officiel. Ainsi, vous ne bloquerez pas par mgarde votre propre systme.

Voir aussi
man sudo ; man sudoers ; man visudo ; SSH, le shell scuris La rfrence de Daniel J. Barrett (ditions OReilly) ; la recette 14.15, crire des scripts setuid ou setgid, page 312 ; la recette 14.18, Excuter un script sans avoir les privilges de root, page 317 ; la recette 14.20, Utiliser des mots de passe dans un script, page 319 ; la recette 17.15, Utiliser sudo avec un groupe de commandes, page 454.

14.20. Utiliser des mots de passe dans un script


Problme
Vous devez indiquer explicitement un mot de passe dans un script.

Solution
Il sagit videmment dune mauvaise ide, qui doit tre proscrite. Malheureusement, il nexiste parfois aucune autre solution. Tout dabord, vous devez vrifier si vous ne pouvez pas utiliser sudo avec loption NOPASSWD pour viter dindiquer explicitement un mot de passe. Cette approche a galement des inconvnients, mais vous devez lessayer. Pour plus dinformations, consultez la recette 14.19, page 318. Une autre solution sappuie sur SSH avec des cls publiques et des commandes restreintes (voir la recette 14.21, page 321). Si ces deux solutions ne vous conviennent pas, placez lidentifiant et le mot de passe de lutilisateur dans un fichier spar, uniquement lisible par lutilisateur qui en a besoin. Ensuite, chargez ce fichier au moment opportun (voir la recette 10.3, page 210). Bien entendu, laissez ce fichier en dehors de tout systme de gestion des versions.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

320

Chapitre 14 Scripts scuriss

Discussion
Grce SSH (voir les recettes 14.21, page 321, et 15.11, page 354), il est relativement facile daccder de manire scurise des donnes distantes. Il est mme possible demployer SSH pour accder des donnes sur lhte local, mais il est alors probablement plus efficace dutiliser sudo. Mais comment accder des donnes enregistres dans une base de donnes distante, peut-tre via une commande SQL ? Dans ce cas, il ny a pas grand-chose faire. Et pourquoi ne pas se servir de crypt ou dautres outils de chiffrement des mots de passe ? Le problme vient des mthodes scurises denregistrement des mots de passe qui impliquent toute lutilisation dune fonction de hachage sens unique. Autrement dit, il est thoriquement impossible de retrouver, partir du code de hachage, le mot de passe en clair. Cest l que le bt blesse. Nous avons besoin du mot de passe en clair pour accder la base de donnes ou un autre serveur. Le stockage scuris nest donc pas une option. Il ne reste donc plus que le stockage non scuris, mais cette approche peut tre pire quun mot de passe en clair car elle apporte un faux sentiment de scurit. Cependant, si elle vous convient, et si vous promettez de faire attention, utilisez-la et masquez le mot de passe avec une mthode de chiffrement de type ROT13 ou autre. Puisque ROT13 naccepte que les lettres ASCII, vous pouvez opter pour ROT47 afin de prendre en charge les symboles de ponctuation.
$ ROT13=$(echo password | tr 'A-Za-z' 'N-ZA-Mn-za-m') $ ROT47=$(echo password | tr '!-~' 'P-~!-O')
Nous souhaitons insister sur le fait que ROT13 ou ROT47 ne constitue rien dautre quune scurit par lobscurit et donc aucune scurit relle. Cette approche vaut mieux que rien si, et uniquement si, vous nimaginez pas que vous tes en scurit alors que ce nest pas le cas. Vous devez simplement avoir conscience des risques. Cela dit, les avantages contrebalancent parfois les risques.

Voir aussi
http://fr.wikipedia.org/wiki/ROT13 ; la recette 10.3, Utiliser des fichiers de configuration dans un script, page 210 ; la recette 14.15, crire des scripts setuid ou setgid, page 312 ; la recette 14.18, Excuter un script sans avoir les privilges de root, page 317 ; la recette 14.19, Utiliser sudo de manire plus sre, page 318 ; la recette 14.21, Utiliser SSH sans mot de passe, page 321 ; la recette 15.11, Obtenir lentre depuis une autre machine, page 354 ; la recette 17.15, Utiliser sudo avec un groupe de commandes, page 454.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.21. Utiliser SSH sans mot de passe

321

14.21. Utiliser SSH sans mot de passe


Problme
Vous devez employer SSH ou scp dans un script, mais ne souhaitez pas utiliser un mot de passe, ou bien, ils doivent servir dans une tche cron et ne peuvent donc avoir un mot de passe1.
SSH1 (le protocole) et SSH1 (lexcutable) sont devenus obsoltes et sont considrs moins srs que le nouveau protocole SSH2 et son implmentation par OpenSSH et SSH Communications Security. Nous vous conseillons fortement demployer SSH2 avec OpenSSH. Nous ne prsenterons pas SSH1.

Solution
Il existe deux manires dutiliser SSH sans mot de passe, la bonne et la mauvaise. La mauvaise consiste employer une cl publique non chiffre par une phrase de passe. La bonne repose sur lutilisation avec ssh-agent ou keychain dune cl publique protge par une phrase de passe. Nous supposons que vous vous servez dOpenSSH ; si ce nest pas le cas, consultez votre documentation (les commandes et les fichiers seront similaires). Tout dabord, vous devez crer une paire de cls, si vous nen possdez pas dj une. Une seule paire de cls est ncessaire pour vous authentifier auprs de toutes les machines que vous configurez, mais vous pouvez choisir dutiliser plusieurs paires, peut-tre pour des utilisations personnelles et professionnelles. La paire est constitue dune cl prive, que vous devez protger tout prix, et dune cl publique (*.pub), que vous pouvez diffuser. Les deux sont lies par une fonction mathmatique complexe, qui leur permet de sidentifier lune et lautre, mais qui ne permet pas de dduire lune de lautre. Utilisez ssh-keygen (peut-tre ssh-keygen2 si vous ne vous servez pas dOpenSSH) pour crer une paire de cls. Loption -t est obligatoire et ses arguments sont rsa ou dsa. -b est facultative et prcise le nombre de bits de la nouvelle cl (1024 par dfaut, au moment de lcriture de ces lignes). -C permet dajouter un commentaire, qui vaut par dfaut utilisateur@nom_de_machine. Nous vous conseillons les paramtres -t dsa -b 2048 et vous recommandons fortement dutiliser une phrase de passe. ssh-keygen vous permet galement de modifier la phrase de passe et le commentaire du fichier de cl.
$ ssh-keygen You must specify a key type (-t). Usage: ssh-keygen [options]

1. Nous remercions Richard Silverman et Daniel Barrett pour leurs ides et leurs excellentes explications dans les livres SSH, le shell scuris La rfrence (ditons OReilly), en particulier les chapitres 2, 6 et 11, et Linux Security Cookbook (OReilly Media), qui ont normment profit cette recette.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

322
Options: -b bits -c -e -f filename -g -i -l -p -q -y -t type -B -H -F hostname -C comment -N phrase -P phrase -r hostname -G file -T file

Chapitre 14 Scripts scuriss

Number of bits in the key to create. Change comment in private and public key files. Convert OpenSSH to IETF SECSH key file. Filename of the key file. Use generic DNS resource record format. Convert IETF SECSH to OpenSSH key file. Show fingerprint of key file. Change passphrase of private key file. Quiet. Read private key file and print public key. Specify type of key to create. Show bubblebabble digest of key file. Hash names in known_hosts file Find hostname in known hosts file Provide new comment. Provide new passphrase. Provide old passphrase. Print DNS resource record. Generate candidates for DH-GEX moduli Screen candidates for DH-GEX moduli

$ ssh-keygen -t dsa -b 2048 -C 'Voici ma nouvelle cle' Generating public/private dsa key pair. Enter file in which to save the key (/home/jp/.ssh/id_dsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/jp/.ssh/id_dsa. Your public key has been saved in /home/jp/.ssh/id_dsa.pub. The key fingerprint is: 84:6f:45:fc:08:3b:ce:b2:4f:2e:f3:5e:b6:9f:65:63 Voici ma nouvelle cle $ ls -l id_dsa* -rw------- 1 jp -rw-r--r-- 1 jp

jp 1264 Dec 13 23:39 id_dsa jp 1120 Dec 13 23:39 id_dsa.pub

$ cat id_dsa.pub ssh-dss AAAAB3NzaC1kc3MAAAEBANpgvvTslst2m0ZJA0ayhh1Mqa3aWwU3kfv0m9+myFZ9veFsxM7IVxI jWfAlQh3jplY+Q78fMzCTiG+ZrGZYn8adZ9yg5/wAC03KXm2vKt8LfTx6I+qkMR7v15NI7tZyhx Gah5qHNehReFWLuk7JXCtRrzRvWMdsHc/L2SA1Y4fJ9Y9FfVlBdE1Er+ZIuc5xIlO6D1HFjKjt3 wjbAal+oJxwZJaupZ0Q7N47uwMslmc5ELQBRNDsaoqFRKlerZASPQ5P+AH/+Cxa/fCGYwsogXSJ J0H5S7+QJJHFze35YZI/+A1D3BIa4JBf1KvtoaFr5bMdhVAkChdAdMjo96xhbdEAAAAVAJSKzCE srUo3KAvyUO8KVD6e0B/NAAAA/3u/Ax2TIB/M9MmPqjeH67Mh5Y5NaVWuMqwebDIXuvKQQDMUU4 EPjRGmS89Hl8UKAN0Cq/C1T+OGzn4zrbE06CO/Sm3SRMP24HyIbElhlWV49sfLR05Qmh9fRl1s7 ZdcUrxkDkr2J6on5cMVB9M2nIl90IhRVLd5RxP01u81yqvhvE61ORdA6IMjzXcQ8ebuD2R733O3 7oGFD7e2O7DaabKKkHZIduL/zFbQkzMDK6uAMP8ylRJN0fUsqIhHhtc//16OT2H6nMU09MccxZT FUfqF8xIOndElP6um4jXYk5Q30i/CtU3TZyvNeWVwyGwDi4wg2jeVe0YHU2Rh/ZcZpwAAAQEAv2 O86701U9sIuRijp8sO4h13eZrsE5rdn6aul/mkm+xAlO+WQeDXR/ONm9BwVSrNEmIJB74tEJL3q QTMEFoCoN9Kp00Ya7Qt8n4gZ0vcZlI5u+cgyd1mKaggS2SnoorsRlb2Lh/Hpe6mXus8pUTf5QT8

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.21. Utiliser SSH sans mot de passe

323

apgXM3TgFsLDT+3rCt40IdGCZLaP+UDBuNUSKfFwCru6uGoXEwxaL08Nv1wZOc19qrc0Yzp7i33 m6i3a0Z9Pu+TPHqYC74QmBbWq8U9DAo+7yhRIhq/fdJzk3vIKSLbCxg4PbMwx2Qfh4dLk+L7wOa sKnl5//W+RWBUrOlaZ1ZP1/azsK0Ncygno/0F1ew== Voici ma nouvelle cle

Lorsque vous disposez dune paire de cls, ajoutez la cl publique au fichier ~/.ssh/authorized_keys dans votre rpertoire personnel sur les autres machines auxquelles vous souhaitez vous connecter laide de cette paire. Pour cela, vous pouvez vous servir de scp, de cp avec une disquette ou une cl USB, ou simplement la copier et la coller entre des sessions de terminal. Il faut juste quelle se trouve sur une mme ligne. Bien que vous puissiez employer une seule commande (par exemple, scp id_dsa.pub hote_distant:.ssh/authorized_keys), nous dconseillons cette solution, mme si vous tes absolument certain que le fichier authorized_keys nexiste pas. Nous prconisons une commande, certe plus complexe, mais beaucoup plus fiable :
$ ssh hote_distant "echo $(cat ~/.ssh/id_dsa.pub) >> ~/.ssh/authorized_keys" jp@hote_distant's password: $ ssh hote_distant Last login: Thu Dec 14 00:02:52 2006 from openbsd.jpsdomai NetBSD 2.0.2 (GENERIC) #0: Wed Mar 23 08:53:42 UTC 2005 Welcome to NetBSD! -bash-3.00$ exit logout Connection to hote_distant closed.

Comme vous pouvez le constater, nous devons entrer un mot de passe lors de la copie initiale, mais, ensuite, ssh ne le demande plus. En ralit, nous venons dillustrer lutilisation de ssh-agent, qui a rcupr la phrase de passe de la cl afin que nous nayons pas la saisir. La commande prcdente suppose galement que le rpertoire ~/.ssh existe sur les deux machines. Si ce nest pas le cas, crez-le par mkdir -m 0700 -p ~/.ssh. Pour quOpenSSH fonctionne, le rpertoire ~/.ssh doit possder les autorisations 0700. Il est galement prfrable de donner les autorisations 0600 ~/.ssh/authorized_keys. Vous noterez que la relation cre est sens unique. Nous pouvons utiliser SSH depuis lhte local vers lhte distant sans mot de passe, mais linverse nest pas vrai car il manque la cl prive et lagent sur lhte distant. Vous pouvez simplement copier votre cl prive sur toutes les machines afin de crer un rseau SSH sans mot de passe, mais le changement de la phrase de passe est alors plus complexe et il est plus difficile de scuriser la cl prive. Si possible, il est prfrable de disposer dune machine bien protge et fiable partir de laquelle vous vous connectez aux htes distants avec ssh. Lagent SSH est intelligent et son utilisation subtile. Nous pourrions mme dire trop intelligent. Son utilisation prvue passe par eval et une substitution de commande : eval `ssh-agent`. Deux variables denvironnement sont alors cres. ssh ou scp les utilisent pour trouver lagent et lui demander les informations didentit. Cette solution est trs habile et trs bien documente. Le seul problme est que cette approche est diffrente des autres programmes couramment employs ( lexception de certaines fonctionnalits de less, voir la recette 8.15, page 189) et totalement incomprhensible aux utilisateurs nophytes ou mal informs.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

324

Chapitre 14 Scripts scuriss

Si vous excutez uniquement lagent, il affiche quelques informations et semble fonctionner. Cest effectivement le cas, car il est bien en cours dexcution. Cependant, il neffectue aucune opration, puisque les variables denvironnement ncessaires nont pas t dfinies. Mentionnons galement loption -k qui demande lagent de se terminer.
# La mauvaise manire dutiliser lagent. # Aucune variable denvironnement adquate. $ set | grep SSH $ $ ssh-agent SSH_AUTH_SOCK=/tmp/ssh-bACKp27592/agent.27592; export SSH_AUTH_SOCK; SSH_AGENT_PID=24809; export SSH_AGENT_PID; echo Agent pid 24809; # Toujours rien. $ set | grep SSH $ # Il est mme impossible de le tuer, puisque -k a besoin de $SSH_AGENT_PID. $ ssh-agent -k SSH_AGENT_PID not set, cannot kill agent # Est-il $ ps x PID TT 24809 ?? 22903 p0 11303 p0 en cours dexcution ? Oui. STAT Is I R+ TIME 0:00.01 0:03.05 0:00.00 COMMAND ssh-agent -bash (bash) ps -x

$ kill 24809 $ ps x PID TT STAT 22903 p0 I 30542 p0 R+

TIME COMMAND 0:03.06 -bash (bash) 0:00.00 ps -x

# Toujours la mauvaise manire demployer lagent. # Cette commande est correcte. $ eval `ssh-agent` Agent pid 21642 # a marche ! $ set | grep SSH SSH_AGENT_PID=21642 SSH_AUTH_SOCK=/tmp/ssh-ZfEsa28724/agent.28724 # Tuer lagent, de la mauvaise manire. $ ssh-agent -k unset SSH_AUTH_SOCK;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.21. Utiliser SSH sans mot de passe


unset SSH_AGENT_PID; echo Agent pid 21642 killed; # Le processus a bien disparu, mais il na pas fait le mnage. $ set | grep SSH SSH_AGENT_PID=21642 SSH_AUTH_SOCK=/tmp/ssh-ZfEsa28724/agent.28724

325

# La bonne manire dutiliser lagent. $ eval `ssh-agent` Agent pid 19330 $ set | grep SSH SSH_AGENT_PID=19330 SSH_AUTH_SOCK=/tmp/ssh-fwxMfj4987/agent.4987 $ eval `ssh-agent -k` Agent pid 19330 killed $ set | grep SSH $

Comme vous pouvez le constater, lemploi de lagent nest pas trs intuitif. Il est peuttre trs habile, trs efficace, trs subtil, mais en aucun cas convivial. Une fois lagent en excution, nous devons charger les informations didentit laide de la commande ssh-add. Cette opration est trs simple, puisquil suffit dexcuter cette commande, avec, de manire facultative, la liste des fichiers de cls charger. Elle demande la saisie de toutes les phrases de passe ncessaires. Dans lexemple suivant, nous nindiquons aucune cl et elle utilise simplement celle par dfaut dfinie dans le fichier de configuration principal de SSH :
$ ssh-add Enter passphrase for /home/jp/.ssh/id_dsa: Identity added: /home/jp/.ssh/id_dsa (/home/jp/.ssh/id_dsa)

Nous pouvons prsent employer SSH interactivement, dans cette session du shell, pour nous connecter toute machine configure prcdemment, sans saisir un mot ou une phrase de passe. Mais, quen est-il des autres sessions, scripts ou tches cron ? Pour cela, utilisez le script keychain (http://www.gentoo.org/proj/en/keychain/) de Daniel Robbins. En effet, il :
[agit] comme une interface ssh-agent, en vous permettant de crer un processus sshagent par systme et non par session. Cette approche permet de rduire considrablement le nombre de saisies de la phrase de passe. Elle ne se fait plus chaque nouvelle connexion, mais chaque dmarrage de la machine locale. [...] keychain permet galement aux tches cron dexploiter de manire propre et scurise les cls RSA/DSA, sans avoir employer des cls prives non chiffres peu sres.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

326

Chapitre 14 Scripts scuriss

keychain est un script shell bien conu, bien crit et bien document. Il automatise et gre le processus fastidieux dexportation des variables denvironnement dcrites prcdemment dans dautres sessions. Il les rend galement disponibles aux scripts et cron. En y rf lchissant, vous pourriez trouver un problme cette approche. En effet, toutes les cls sont confies ce script, jusqu ce que la machine redmarre. En ralit, ce point nest pas aussi risqu quil parat. Premirement, vous pouvez toujours le tuer, mais les scripts et cron ne pourront plus en bnficier. Deuximement, son option --clean permet de retirer les cls mises en cache lorsque que vous ouvrez une session. Voici les dtails, donns par lauteur de keychain (publis initialement par IBM developerWorks http://www.ibm.com/developerworks/, voir http://www.ibm.com/developerworks/linux/library/l-keyc2/) :
Jai expliqu prcdemment que lemploi de cls prives non chiffres est une pratique dangereuse, car elle permet quiconque de voler votre cl prive et de lutiliser pour se connecter vos comptes distants, partir de nimporte quel systme, sans fournir un mot de passe. Mme si keychain nest pas vulnrable ce type de problmes (tant que vous utilisez des cls prives chiffres), il prsente un point faible potentiellement exploitable directement li au fait quil facilite le dtournement du processus ssh-agent de longue dure. Que se passe-t-il si un intrus est en mesure de dterminer mon mot ou ma phrase de passe et se connecte mon systme local ? Sil est capable douvrir une session sous mon nom dutilisateur, keychain lui accorde un accs instantan mes cls prives dchiffres et donc tous mes autres comptes. Avant de poursuivre, tudions cette menace. Si un utilisateur malveillant est en mesure douvrir une session sous mon nom, keychain lui autorise effectivement un accs mes comptes distants. Cependant, il sera trs difficile lintrus dobtenir mes cls prives dchiffres puisquelles sont toujours chiffres sur le disque. Par ailleurs, pour obtenir accs mes cls prives, il faut que lutilisateur ouvre une session sous mon nom et ne lise pas simplement les fichiers dans mon rpertoire. Par consquent, dtourner ssh-agent est une tche plus complexe que le vol dune cl prive non chiffre, pour lequel lintrus simplement besoin daccder aux fichiers de mon rpertoire ~/.ssh, quil ait ouvert une session sous mon nom ou sous un autre nom. Nanmoins, si un assaillant est en mesure de se connecter sous mon nom, il peut causer de nombreux dommages en utilisant mes cls prives dchiffres. Par consquent, si vous voulez employer keychain sur un serveur que vous surveillez peu, utilisez loption --clear afin dapporter un niveau de scurit supplmentaire. Loption --clear indique keychain que toute nouvelle connexion votre compte doit tre considre comme une faille potentielle pour la scurit, except en cas de preuve contraire. Lorsque keychain est dmarr avec loption --clear, il commence par effacer, au moment de la connexion, toutes les cls prives qui se trouvent dans le cache de ssh-agent. Par consquent, si vous tes un intrus, keychain vous demande des phrases de passe au lieu de vous donner accs aux cls existant dans le cache. Cependant, mme si cela amliore la scurit, lutilisation est moins conviviale et trs similaire lexcution de ssh-agent lui-mme, sans keychain. Comme cest souvent le cas, il faut choisir entre scurit et convivialit. Malgr tout, lutilisation de keychain avec --clear prsente des avantages par rapport celle de ssh-agent seul. En effet, avec keychain --clear, vos tches cron et vos scripts peuvent toujours tablir des connexions sans mot de passe, puisque les cls prives sont effaces la connexion et non la dconnexion. La dconnexion dun systme ne constituant pas une brche potentielle dans la scurit, il ny a aucune raison que

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.21. Utiliser SSH sans mot de passe

327

keychain y rponde par la suppression des cls de ssh-agent. Ainsi, loption --clear est parfaitement adapte aux serveurs peu frquents qui doivent raliser occasionnellement des tches de copies scurises, comme les serveurs de sauvegarde, les pare-feu et les routeurs.

Pour utiliser ssh-agent au travers de keychain dans un script ou avec cron, chargez simplement le fichier cr par keychain pour votre script. keychain peut galement grer les cls GPG (GNU Privacy Guard) :
[ -r ~/.ssh-agent ] && source ~/.ssh-agent \ || { echo "keychain nest pas dmarr" >&2 ; exit 1; }

Discussion
Lorsque vous utilisez SSH dans un script, il est assez pnible de devoir sauthentifier ou de recevoir des avertissements. Loption -q active le mode silencieux et supprime les avertissements, tandis que -o 'BatchMode yes' empche les invites. videmment, si SSH ne dispose daucun moyen de sauthentifier, il choue. SSH est un outil merveilleux et mrite un traitement plus dtaill. Nous vous conseillons fortement le livre SSH, le shell scuris La rfrence de Richard E. Silverman et Daniel J. Barrett (ditions OReilly), pour tout ce que vous avez envie de savoir sur SSH. Lutilisation de cls publiques entre OpenSSH et SSH2 Server de SSH Communications Security peut tre assez complexe. Pour plus dinformations, consultez le chapitre 6 du livre Linux Security Cookbook de Daniel J. Barrett et autres (OReilly Media). IBM developerWorks propose plusieurs articles sur SSH rdigs par Daniel Robbins, le crateur de keychain et larchitecte en chef de Gentoo (http://www.ibm.com/developerworks/linux/library/l-keyc.html, http://www.ibm.com/developerworks/linux/library/l-keyc2/ et http://www.ibm.com/developerworks/linux/library/l-keyc3/). Si keychain ne semble pas fonctionner ou sil fonctionne pendant un certain temps puis semble sarrter, il est probable quun autre script excute lui aussi ssh-agent et perturbe le processus. Vrifiez les informations suivantes et assurez-vous que les PID et les sockets sont cohrents. Selon votre systme dexploitation, vous devrez modifier la commande ps (si -ef ne fonctionne pas, essayez -eu).
$ ps -ef | grep [s]sh-agent jp 17364 0.0 0.0 3312 1132 ? S Dec16 0:00 ssh-agent

$ cat ~/.keychain/$HOSTNAME-sh SSH_AUTH_SOCK=/tmp/ssh-UJc17363/agent.17363; export SSH_AUTH_SOCK; SSH_AGENT_PID=17364; export SSH_AGENT_PID; $ set | grep SSH_A SSH_AGENT_PID=17364 SSH_AUTH_SOCK=/tmp/ssh-UJc17363/agent.17363

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

328

Chapitre 14 Scripts scuriss

Empreintes dune cl
Toutes les versions de SSH prennent en charge les empreintes, qui facilitent la comparaison et la vrification des cls, que ce soit celles de lutilisateur ou de lhte. Comme vous pouvez limaginer, la vrification bit par bit dune longue suite de donnes alatoires est fastidieuse et sujette aux erreurs, voire virtuellement impossible (par exemple, par tlphone). Les empreintes permettent deffectuer cette vrification beaucoup plus facilement. Vous avez sans doute dj rencontr des empreintes avec dautres applications, en particulier les cls PGP/GPG. La principale raison de vrifier les cls est dempcher les attaques de type homme du milieu. Si Alice envoie sa cl Bob, celui-ci doit sassurer que la cl reue est rellement celle dAlice et que Eve ne la pas intercepte et envoy la sienne la place. Pour cela, il faut un canal de communication spar, comme un tlphone. Il existe deux formats dempreintes, le format hexadcimal classique de PGP et le nouveau format bubblebabble, suppos plus facile lire. Lorsque Bob reoit la cl dAlice, il lappelle et lui lit lempreinte. Si les deux correspondent, les deux intervenants savent que Bob dispose de la bonne cl.
$ ssh-keygen -l -f ~/.ssh/id_dsa 2048 84:6f:45:fc:08:3b:ce:b2:4f:2e:f3:5e:b6:9f:65:63 /home/jp/.ssh/id_dsa.pub $ ssh-keygen -l -f ~/.ssh/id_dsa.pub 2048 84:6f:45:fc:08:3b:ce:b2:4f:2e:f3:5e:b6:9f:65:63 /home/jp/.ssh/id_dsa.pub $ ssh-keygen -B -f ~/.ssh/id_dsa 2048 xosev-kytit-rakyk-tipos-bocuh-kotef-mupyc-hozok-zalip-pezad-nuxox /home/jp/.ssh/id_dsa.pub $ ssh-keygen -B -f ~/.ssh/id_dsa.pub 2048 xosev-kytit-rakyk-tipos-bocuh-kotef-mupyc-hozok-zalip-pezad-nuxox /home/jp/.ssh/id_dsa.pub

Voir aussi
http://www.gentoo.org/proj/en/keychain/ ; http://www.ibm.com/developerworks/linux/library/l-keyc2/ ; SSH, le shell scuris La rfrence de Richard E. Silverman et Daniel J. Barrett (ditions OReilly) ; Linux Security Cookbook de Daniel J. Barrett et autres (OReilly Media) ; Cryptographie : En pratique de Niels Ferguson et Bruce Schneier (Vuibert) ; Cryptographie applique de Bruce Schneier (Vuibert) ; la recette 8.15, Aller plus loin avec less, page 189.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.22. Restreindre les commandes SSH

329

14.22. Restreindre les commandes SSH


Problme
Vous souhaitez limiter les possibilits dun utilisateur ou dun script SSH2.

Solution
Modifiez le fichier ~/.ssh/authorized_keys, utilisez des commandes forces SSH et, en option, dsactivez les fonctionnalits SSH inutiles. Par exemple, supposons que vous souhaitiez autoriser un processus rsync sans lutilisation interactive. Tout dabord, vous devez dterminer prcisment la commande qui sera excute sur le ct distant. Crez une cl (voir la recette 14.21, page 321) et ajoutez une commande force. Ouvrez le fichier ~/.ssh/authorized_keys et ajoutez la ligne suivante avant la cl :
command="/bin/echo La commande etait : $SSH_ORIGINAL_COMMAND"

Vous devez obtenir une entre similaire la suivante (sur une seule ligne) :
command="/bin/echo La commande etait : $SSH_ORIGINAL_COMMAND" ssh-dss AAAAB3NzaC1kc3MAAAEBANpgvvTslst2m0ZJA0ayhh1Mqa3aWwU3kfv0m9+myFZ9veFsxM7IVxI jWfAlQh3jplY+Q78fMzCTiG+ZrGZYn8adZ9yg5/wAC03KXm2vKt8LfTx6I+qkMR7v15NI7tZyhx Gah5qHNehReFWLuk7JXCtRrzRvWMdsHc/L2SA1Y4fJ9Y9FfVlBdE1Er+ZIuc5xIlO6D1HFjKjt3 wjbAal+oJxwZJaupZ0Q7N47uwMslmc5ELQBRNDsaoqFRKlerZASPQ5P+AH/+Cxa/fCGYwsogXSJ J0H5S7+QJJHFze35YZI/+A1D3BIa4JBf1KvtoaFr5bMdhVAkChdAdMjo96xhbdEAAAAVAJSKzCE srUo3KAvyUO8KVD6e0B/NAAAA/3u/Ax2TIB/M9MmPqjeH67Mh5Y5NaVWuMqwebDIXuvKQQDMUU4 EPjRGmS89Hl8UKAN0Cq/C1T+OGzn4zrbE06CO/Sm3SRMP24HyIbElhlWV49sfLR05Qmh9fRl1s7 ZdcUrxkDkr2J6on5cMVB9M2nIl90IhRVLd5RxP01u81yqvhvE61ORdA6IMjzXcQ8ebuD2R733O3 7oGFD7e2O7DaabKKkHZIduL/zFbQkzMDK6uAMP8ylRJN0fUsqIhHhtc//16OT2H6nMU09MccxZT FUfqF8xIOndElP6um4jXYk5Q30i/CtU3TZyvNeWVwyGwDi4wg2jeVe0YHU2Rh/ZcZpwAAAQEAv2 O86701U9sIuRijp8sO4h13eZrsE5rdn6aul/mkm+xAlO+WQeDXR/ONm9BwVSrNEmIJB74tEJL3q QTMEFoCoN9Kp00Ya7Qt8n4gZ0vcZlI5u+cgyd1mKaggS2SnoorsRlb2Lh/Hpe6mXus8pUTf5QT8 apgXM3TgFsLDT+3rCt40IdGCZLaP+UDBuNUSKfFwCru6uGoXEwxaL08Nv1wZOc19qrc0Yzp7i33 m6i3a0Z9Pu+TPHqYC74QmBbWq8U9DAo+7yhRIhq/fdJzk3vIKSLbCxg4PbMwx2Qfh4dLk+L7wOa sKnl5//W+RWBUrOlaZ1ZP1/azsK0Ncygno/0F1ew== Voici ma nouvelle cle

Ensuite, excutez votre commande et constatez le rsultat :


$ ssh hote_distant 'ls -l /etc' La commande etait : ls -l /etc

Cette approche a pour inconvnient dempcher le fonctionnement dun programme comme rsync qui dpend dun canal STDOUT/STDIN rserv.

2. Nous remercions Richard Silverman et Daniel Barrett pour leurs ides et leurs excellentes explications dans les livres SSH, le shell scuris La rfrence (ditions OReilly), en particulier les chapitres 2, 6 et 11, et Linux Security Cookbook (OReilly Media), qui ont normment profit cette recette.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

330

Chapitre 14 Scripts scuriss

$ rsync -avzL -e ssh hote_distant:/etc . protocol version mismatch -- is your shell clean? (see the rsync man page for an explanation) rsync error: protocol incompatibility (code 2) at compat.c(64)

Nous pouvons contourner ce problme en modifiant la commande force :


command="/bin/echo La commande etait : $SSH_ORIGINAL_COMMAND >> ~/ssh_command"

Nous effectuons un nouveau test du ct client :


$ rsync -avzL -e ssh 192.168.99.56:/etc . rsync: connection unexpectedly closed (0 bytes received so far) [receiver] rsync error: error in rsync protocol data stream (code 12) at io.c(420)

Voici ce que nous obtenons sur lhte distant :


$ cat ../ssh_command La commande etait : rsync --server --sender -vlLogDtprz . /etc

Il est donc possible de mettre jour la commande force selon les besoins. Nous pouvons galement dfinir une restrictions dhte dorigine et dsactiver des commandes SSH. La restriction dhte prcise le nom ou ladresse IP de lhte dorigine. La dsactivation de certaines commandes est galement assez intuitive :
no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty

En runissant le tout, nous obtenons lentre suivante (toujours sur une seule trs longue ligne) :
no-port-forwarding,no-X11-forwarding,no-agent-forwarding,nopty,from="client_local",command="rsync --server --sender -vlLogDtprz . /etc" ssh-dss AAAAB3NzaC1kc3MAAAEBANpgvvTslst2m0ZJA0ayhh1Mqa3aWwU3kfv0m9+myFZ9veFsxM7IVxI jWfAlQh3jplY+Q78fMzCTiG+ZrGZYn8adZ9yg5/wAC03KXm2vKt8LfTx6I+qkMR7v15NI7tZyhx Gah5qHNehReFWLuk7JXCtRrzRvWMdsHc/L2SA1Y4fJ9Y9FfVlBdE1Er+ZIuc5xIlO6D1HFjKjt3 wjbAal+oJxwZJaupZ0Q7N47uwMslmc5ELQBRNDsaoqFRKlerZASPQ5P+AH/+Cxa/fCGYwsogXSJ J0H5S7+QJJHFze35YZI/+A1D3BIa4JBf1KvtoaFr5bMdhVAkChdAdMjo96xhbdEAAAAVAJSKzCE srUo3KAvyUO8KVD6e0B/NAAAA/3u/Ax2TIB/M9MmPqjeH67Mh5Y5NaVWuMqwebDIXuvKQQDMUU4 EPjRGmS89Hl8UKAN0Cq/C1T+OGzn4zrbE06CO/Sm3SRMP24HyIbElhlWV49sfLR05Qmh9fRl1s7 ZdcUrxkDkr2J6on5cMVB9M2nIl90IhRVLd5RxP01u81yqvhvE61ORdA6IMjzXcQ8ebuD2R733O3 7oGFD7e2O7DaabKKkHZIduL/zFbQkzMDK6uAMP8ylRJN0fUsqIhHhtc//16OT2H6nMU09MccxZT FUfqF8xIOndElP6um4jXYk5Q30i/CtU3TZyvNeWVwyGwDi4wg2jeVe0YHU2Rh/ZcZpwAAAQEAv2 O86701U9sIuRijp8sO4h13eZrsE5rdn6aul/mkm+xAlO+WQeDXR/ONm9BwVSrNEmIJB74tEJL3q QTMEFoCoN9Kp00Ya7Qt8n4gZ0vcZlI5u+cgyd1mKaggS2SnoorsRlb2Lh/Hpe6mXus8pUTf5QT8 apgXM3TgFsLDT+3rCt40IdGCZLaP+UDBuNUSKfFwCru6uGoXEwxaL08Nv1wZOc19qrc0Yzp7i33 m6i3a0Z9Pu+TPHqYC74QmBbWq8U9DAo+7yhRIhq/fdJzk3vIKSLbCxg4PbMwx2Qfh4dLk+L7wOa sKnl5//W+RWBUrOlaZ1ZP1/azsK0Ncygno/0F1ew== Voici ma nouvelle cle

Discussion
Si vous rencontrez des difficults avec ssh, loption -v sera trs utile. ssh -v ou ssh -v -v donne des informations sur les dysfonctionnements. Testez-les avec une configuration oprationnelle afin de connatre laspect de leur sortie.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

14.23. Dconnecter les sessions inactives

331

Si vous souhaitez tre un peu plus ouvert sur les possibilits dune cl, tournez-vous vers rssh, le shell restreint dOpenSSH (http://www.pizzashack.org/rssh/), qui prend en charge scp, sftp, rdist, rsync et cvs. Vous auriez pu croire que les restrictions de ce type taient trs simples, mais ce nest pas le cas. Le problme vient du fonctionnement de SSH (et des commandes r avant lui). Il sagit dun outil brillant, qui fonctionne trs bien, mais il est difficile limiter. Si lon veut extrmement simplifier son fonctionnement, vous pouvez voir SSH comme une connexion entre le STDOUT local et le STDIN distant, ainsi quune connexion entre le STDOUT distant et le STDIN local. Par consquent, les commandes comme scp et rsync ne font quenvoyer des octets de la machine locale vers la machine distante, un peu la manire dun tube. Malheureusement, cette souplesse empche SSH de limiter les accs interactifs tout en acceptant scp. Cest galement la raison pour laquelle vous ne pouvez placer des commandes echo et des instructions de dbogage dans les fichiers de configuration de bash (voir la recette 16.19, page 414). En effet, les messages affichs seraient alors mlangs au flux doctets et provoqueraient des dgts. Dans ce cas, comment rssh fonctionne-t-il ? Il fournit une enveloppe utilise la place du shell par dfaut (comme bash) dans /etc/passwd. Cette enveloppe dtermine ce qui est autoris, mais avec beaucoup plus de souplesse quune commande SSH restreinte.

Voir aussi
SSH, le shell scuris La rfrence de Richard E. Silverman et Daniel J. Barrett (ditions OReilly) ; Linux Security Cookbook de Daniel J. Barrett et autres (OReilly Media) ; la recette 14.21, Utiliser SSH sans mot de passe, page 321 ; la recette 16.19, Crer des fichiers dinitialisation autonomes et portables, page 414.

14.23. Dconnecter les sessions inactives


Problme
Vous souhaitez dconnecter automatiquement les utilisateurs inactifs, en particulier root.

Solution
Dans /etc/bashrc ou ~/.bashrc, fixez la variable denvironnement $TMOUT au nombre de secondes dinactivit avant la clture de la session. En mode interactif, aprs laffichage dune invite, si lutilisateur ne saisit pas une commande dans les $TMOUT secondes, bash se termine.

Discussion
$TMOUT est galement utilise par les commandes internes read et select dans les scripts.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

332

Chapitre 14 Scripts scuriss

Noubliez pas de dfinir cette variable dans un fichier systme, comme /etc/profile ou /etc/bashrc, auxquels les utilisateurs nont pas accs en criture. Ainsi, ils nauront pas la possibilit de la modifier
declare -r TMOUT=3600 # Ou : readonly TMOUT=3600
Puisque lutilisateur est matre de son environnement, vous ne pouvez pas totalement vous appuyer sur $TMOUT, mme dfinie dans un fichier systme, car il peut simplement excuter un autre shell. Vous devez voir cette variable comme une aide avec les utilisateurs coopratifs, en particulier les administrateurs systme qui peuvent tre (souvent) distraits de leur travail.

Voir aussi
la recette 16.19, Crer des fichiers dinitialisation autonomes et portables, page 414.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15
Scripts labors
Unix et POSIX promettent depuis trs longtemps compatibilit et portabilit, mais elles sont plutt longues venir. Pour les dveloppeurs, lcriture de scripts labors portables, cest--dire qui fonctionnent sur toute machine possdant bash, reprsente donc un problme important. crire des scripts compatibles avec diffrentes plates-formes savre beaucoup plus complexe quon ne le voudrait. Chaque systme comporte ses petites variantes qui compliquent les rgles. Par exemple, bash lui-mme nest pas toujours install au mme endroit et de nombreuses commandes Unix classiques possdent des options lgrement diffrentes (ou produisent une sortie lgrement diffrente) en fonction du systme dexploitation. Dans ce chapitre, nous examinons plusieurs de ces problmes et donnons leurs solutions. Par ailleurs, certaines oprations ne savrent pas aussi simples quon le souhaiterait. Nous allons donc galement prsenter des solutions pour certains scripts labors, comme lautomatisation de processus, lenvoi de courrier lectronique, la journalisation avec syslog, lutilisation des ressources rseau et quelques astuces concernant la lecture de lentre et la redirection de la sortie. Bien que ce chapitre concerne des scripts labors, nous insistons sur limportance dcrire un code clair, aussi simple que possible et bien document. Brian Kernighan, lun des premiers dveloppeurs Unix, a trs bien expliqu cet aspect :
Le dbogage est deux fois plus complexe que lcriture initiale du code. Par consquent, si vous crivez le code aussi intelligemment que possible, vous ntes, par dfinition, pas assez intelligent pour le dboguer.

Il est trs facile dcrire des scripts shell trs astucieux et trs difficiles, voire impossibles, comprendre. Plus vous serez ingnieux le jour de lcriture du code, plus vous le regretterez six, douze ou dix-huit mois aprs, lorsque vous devrez rsoudre un problme de votre code (ou, pire encore, celui dun autre dveloppeur). Si vous devez tre astucieux, vous devez, pour le moins, documenter le fonctionnement du script (voir la recette 5.1, page 87) !

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

334

Chapitre 15 Scripts labors

15.1. Trouver bash de manire portable


Problme
Vous devez excuter un script bash sur plusieurs machines, mais bash ne se trouve pas toujours au mme endroit (voir la recette 1.11, page 21).

Solution
Utilisez la commande /usr/bin/env dans la ligne shebang, par exemple #!/usr/bin/env bash. Si, sur votre systme, la commande env ne se trouve pas dans /usr/bin, demandez votre administrateur de linstaller, de la dplacer ou de crer un lien symbolique, car il sagit de lemplacement obligatoire. Par exemple, Red Hat a choisi, sans raison valable, /bin/env, mais a pris soin dajouter un lien symbolique lemplacement correct. Vous pouvez galement crer des liens symboliques pour bash lui-mme, mais lemploi de env constitue la bonne solution.

Discussion
Le rle de env est dexcuter un programme dans un environnement modifi , mais, puisquelle recherche dans le chemin la commande indique, elle est parfaitement adapte cette utilisation. Vous pourriez tre tent par #!/bin/sh. Ny succombez pas. Si vos scripts utilisent des fonctionnalits propres bash, ils ne fonctionneront pas sur les machines pour qui /bin/sh nest pas bash en mode shell Bourne (par exemple, BSD, Solaris, Ubuntu 6.10+). Par ailleurs, mme si, pour le moment, vous nutilisez pas de fonctionnalits spcifiques bash, vous risquez doublier plus tard cette contrainte. Si vous vous limitez uniquement aux fonctionnalits POSIX, utilisez alors #!/bin/sh (et ne dveloppez pas sur Linux, voir la recette 15.3, page 337). Dans tous les autres cas, soyez prcis. Parfois, vous verrez quune espace spare #! et /bin/xxx. La raison en est historique. Si certains systmes exigeaient cette espace, ce nest plus vraiment le cas aujourdhui. Il est peu probable quun systme disposant de bash aura besoin de cette espace et elle est donc gnralement supprime. Mais, pour garantir une rtro-compatibilit, utilisez-la. Nous avons choisi #!/usr/bin/env bash dans les scripts et les fonctions les plus longues disponibles en tlchargement (voir la fin de la prface), car cette ligne est compatible avec la majorit des systmes. Cependant, puisque env se sert de la variable $PATH pour trouver bash, cela peut reprsenter un problme de scurit (voir la recette 14.2, page 294), quoique mineur, notre avis.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.2. Dfinir une variable $PATH de type POSIX

335

Il est assez surprenant de constater que le traitement de la ligne shebang nest pas cohrent entre les systmes, alors que nous employons env pour des questions de portabilit. De nombreux systmes, y compris Linux, acceptent le passage dun seul argument linterprteur. Par consquent, #!/usr/bin/env bash - gnre une erreur :
/usr/bin/env: bash -: Aucun fichier ou rpertoire de ce type

En effet, linterprteur est /usr/bin/env et le seul argument autoris est bash -. Dautres systmes, comme BSD et Solaris, nont pas cette restriction. Puisque le caractre - final est une pratique scuritaire courante (voir la recette 14.2, page 294) et puisquil est reconnu uniquement sur certains systmes, il sagit l dun problme de scurit et de portabilit. Vous pouvez utiliser le caractre - final pour amliorer sensiblement la scurit, mme en rduisant la portabilit, ou inversement. Puisque, de toute manire, env consulte le chemin, vous devez viter de lemployer si la scurit est un aspect important. Par consquent, la non-portabilit de - est tolrable. Nous vous conseillons donc domettre le caractre - final lorsque que vous employez env pour des raisons de portabilit et de prciser explicitement linterprteur et le caractre - final lorsque la scurit est primordiale.

Voir aussi
les pages web suivantes pour plus dinformations sur la ligne shebang (/usr/bin/env) : http://srfi.schemers.org/srfi-22/mail-archive/msg00069.html ; http://www.in-ulm.de/~mascheck/various/shebang/ ; http://homepages.cwi.nl/~aeb/std/hashexclam-1.html ; http://www.faqs.org/faqs/unix-faq/faq/part3/, section 3.16 ( Why do some scripts start with #! ... ? ). la recette 1.11, Obtenir bash pour xBSD, page 21 ; la recette 15.2, Dfinir une variable $PATH de type POSIX, page 335 ; la recette 15.3, Dvelopper des scripts shell portables, page 337 ; la recette 15.6, crire une commande echo portable, page 342.

15.2. Dfinir une variable $PATH de type POSIX


Problme
La machine que vous utilisez dispose doutils anciens ou propritaires (par exemple, Solaris) et vous devez dfinir votre PATH de manire accder aux outils POSIX.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

336

Chapitre 15 Scripts labors

Solution
Utilisez getconf :
PATH=$(PATH=/bin:/usr/bin getconf PATH)

Voici quelques chemins par dfaut compatibles POSIX pour diffrents systmes :
# Red Hat Enterprise Linux (RHEL) 4.3 $ echo $PATH /usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/$USER/b in $ getconf PATH /bin:/usr/bin

# Debian Sarge $ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games $ getconf PATH /bin:/usr/bin

# Solaris 10 $ echo $PATH /usr/bin: $ getconf PATH /usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin

# OpenBSD 3.7 $ echo $PATH /home/$USER/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin :/usr/local/sbin:/usr/games $ getconf PATH /usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin

Discussion
getconf se fonde sur diffrentes variables de configuration du systme. Vous pouvez donc vous en servir pour dfinir un chemin par dfaut. Cependant, si getconf nest pas une commande interne, vous aurez besoin dun chemin minimal pour la trouver. Cest la raison dtre de PATH=/bin:/usr/bin dans la solution. En thorie, la variable utiliser est CS_PATH. En pratique, PATH fonctionne sur toutes nos machines de test, alors que CS_PATH a chou sur les systmes BSD.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.3. Dvelopper des scripts shell portables

337

Voir aussi
http://www.unixreview.com/documents/s=7781/uni1042138723500/ ; la recette 9.11, Retrouver un fichier partir dune liste demplacements possibles, page 202 ; la recette 14.3, Dfinir une variable $PATH sre, page 294 ; la recette 14.9, Trouver les rpertoires modifiables mentionns dans $PATH, page 300 ; la recette 14.10, Ajouter le rpertoire de travail dans $PATH, page 303 ; la recette 16.3, Modifier dfinitivement $PATH, page 376 ; la recette 16.4, Modifier temporairement $PATH, page 377 ; la recette 19.3, Oublier que le rpertoire de travail nest pas dans $PATH, page 488.

15.3. Dvelopper des scripts shell portables


Problme
Vous souhaitez crire un script shell qui doit sexcuter sur diffrentes versions des diffrents systmes dexploitation Unix ou POSIX.

Solution
Tout dabord, essayez la commande interne command avec son option -p pour trouver la version POSIX dun programme, par exemple dans /usr/xpg4 ou /usr/xpg6 sur Solaris :
$ command -p programme args

Ensuite, si possible, prenez la machine Unix la plus ancienne ou la moins performante, et dveloppez le script sur cette plate-forme. Si vous ntes pas certain de la plate-forme la moins performante, choisissez une variante BSD ou Solaris (dans une version la plus ancienne possible).

Discussion
command -p utilise un chemin par dfaut qui permet de trouver les utilitaires POSIX standard. Si vous tes certain que votre script sera toujours excut sur Linux, ne vous proccupez pas de cet aspect. Sinon, vitez de dvelopper des scripts multi-plateformes sur Linux ou Windows (par exemple, avec Cygwin). Voici les problmes lis lcriture de scripts shell multi-plateformes sur Linux : 1. /bin/sh nest pas le shell Bourne, mais /bin/bash en mode Bourne, except lorsquil sagit de /bin/dash (par exemple, sur Ubuntu 6.10). Si les deux sont trs bons, sans tre parfaits, aucun des trois ne fonctionne exactement de la mme manire. En particulier, le comportement de echo peut changer. 2. Linux sappuie sur les outils GNU la place des outils Unix dorigine.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

338

Chapitre 15 Scripts labors

Ne vous mprenez pas. Nous apprcions Linux et lutilisons tous les jours. Mais il ne sagit pas dun vritable Unix : il fonctionne diffremment et emploie les outils GNU. Ceux-ci sont extraordinaires et cest bien le problme. Ils disposent dun grand nombre doptions et de fonctionnalits absentes des autres plates-formes. Par consquent, votre script se terminera de manire trange, quel que soit le soin que vous lui aurez apport. En revanche, la compatibilit de Linux est telle que les scripts crits pour dautres systmes de type Unix fonctionneront pratiquement toujours sur ce systme. Ils ne seront peut-tre pas parfaits (par exemple, le comportement par dfaut de echo est dafficher un caractre \n la place dun saut de ligne), mais seront gnralement satisfaisants. En vrit, plus vous utilisez les fonctions du shell, moins vous dpendez de programmes externes dont lexistence et le comportement ne sont pas garantis. Mme si bash est beaucoup plus performant que sh, il fait partie des outils qui peuvent tre absents ou prsents. Une variante de sh existera sur quasiment tous les systmes Unix ou de type Unix, mais elle ne sera pas toujours celle que vous croyez. Par ailleurs, si les options longues des outils GNU facilitent la lecture du code, elles ne sont pas toujours disponiblessur les autres systmes. Ainsi, au lieu dcrire sort --field-separator=, fichier_non_tri > fichier_tri, vous devez employer sort -t, fichier_non_tri > fichier_tri pour amliorer la portabilit. Ne soyez pas dcourag. Le dveloppement sur des systmes non-Linux est plus facile que jamais. Si vous disposez dj de tels systmes, ce nest videmment pas un problme. Dans le cas contraire, il est aujourdhui facile de les obtenir gratuitement. Solaris et les systmes BSD fonctionnent tous dans des environnements virtuels, comme VMware Player ou Server (gratuits), disponibles pour Windows et Linux (bientt pour Mac). Si vous disposez dun Macintosh sous OS X, vous utilisez dj un systme BSD. Vous pouvez galement tester facilement des scripts dans un environnement virtuel comme VMware (voir la recette 15.4, page 339). En revanche, cette solution nest pas envisageable avec les systmes de type AIX et HP-UX car ils sont incompatibles avec une architecture x86 et ne fonctionnent donc pas sous VMware. Une fois encore, si vous disposez de ces systmes, utilisez-les. Sinon, consultez la recette 1.15, page 25.

Voir aussi
help command ; http://fr.wikipedia.org/wiki/Dash ; http://fr.wikipedia.org/wiki/Bourne-Again_shell ; http://www.opensolaris.org/os/article/2006-02-27_getting_started_with_opensolaris_ using_vmware/ ; http://www.testdrive.hp.com/os/ ; http://www.testdrive.hp.com/faq/ ; http://www.polarhome.com/ ; http://www.faqs.org/faqs/hp/hpux-faq/preamble.html ; lhistoire dUnix, http://www.levenez.com/unix/ ; la recette 1.15, Obtenir bash sans linstaller, page 25 ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.4. Tester des scripts sous VMware


la recette 15.4, Tester des scripts sous VMware, page 339 ; la recette 15.6, crire une commande echo portable, page 342 ; la section Options et squences dchappement de echo, page 539.

339

15.4. Tester des scripts sous VMware


Problme
Vous devez dvelopper des scripts multi-plateformes, mais vous ne disposez pas des systmes ou du matriel adquats.

Solution
Si les plates-formes cibles fonctionnent sur larchitecture x86, tlchargez le logiciel gratuit VMware Server et installez-les. Vous pouvez galement trouver des machines virtuelles prconfigures sur le site de VMware, le site du distributeur ou du fournisseur du systme dexploitation ou sur Internet. Cette solution est incompatible avec les systmes comme AIX et HP-UX qui ne fonctionnent pas sur une architecture x86 et donc pas sous VMware. Une fois encore, si vous disposez de ces systmes, utilisez-les. Sinon, consultez la recette 1.15, page 25.

Discussion
Le test des scripts shell nest gnralement pas une opration gourmande en ressources. Un matriel dentre de gamme, capable dexcuter VMware ou un autre outil de virtualisation similaire, fera donc laffaire. Nous citons VMware, car ses versions Server et Player sont gratuites, fonctionnent avec Linux et Windows (bientt avec Mac) et sont trs simples utiliser. Il existe certainement dautres solutions. Si vous installez VMware Server sur un serveur Linux, vous naurez mme pas besoin de linterface graphique sur la machine hte. Vous pouvez utiliser la console VMware de type VNC partir dune autre machine Linux ou Windows, sans linterface graphique. Pour un shell de test, une machine virtuelle avec 128 Mo de RAM, parfois moins, sera suffisante. Configurez un partage NFS afin dy enregistrer les scripts et les donnes de tests, puis connectez-vous au systme de test par telnet ou, mieux, SSH. Pour vous aider, voici un exemple simple bas sur VMware Player : 1. Tlchargez le logiciel gratuit VMware Player pour Windows ou Linux partir de http://www.vmware.com/fr/products/player/. 2. Obtenez limage dune machine virtuelle prconfigure : a. Ubuntu Linux 5.10 (driv de Debian), Firefox 1.0.7 et Gnome 2.12.1 constitutuent la base du botier virtuel Browser Appliance v1.0.0 de VMware (258 Mo http://www.vmware.com/vmtn/appliances/directory/browserapp.html). b. PC-BSD est une distribution bureautique base sur BSD et KDE (718 Mo http://www.pcbsd.org/?p=download#vmware).

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

340

Chapitre 15 Scripts labors

3. Dcompressez le fichier tlcharg et ouvrez-le dans VMware Player, en crant un nouvel identifiant unique si cela vous est demand. Aprs le dmarrage, qui peut prendre du temps, vous obtenez un systme Ubuntu 5.10 avec Gnome et bash 3.0 ou bien un systme BSD avec KDE avec bash 3.1 (au moment de lcriture de ces lignes). Vous pouvez galement excuter deux instances de VMware Player (ou utiliser VMware Server) pour disposer des deux environnements. Notez que ces deux distributions utilisent une interface graphique et demandent donc beaucoup plus de mmoire et de puissance processeur quune installation minimale du shell. Elles sont mentionnes ici pour servir dexemples. Malgr leurs besoins en ressources, elles sont particulirement utiles car il sagit dimages officielles et non dimages communautaires dont la qualit et la fiabilit peuvent varier.
Le botier virtuel Browser Appliance de VMware dispose des outils VMware, contrairement PC-BSD. Ces deux systmes se comporteront donc de manire lgrement diffrente vis--vis de la capture du clavier et de la souris de la machine hte. Prtez attention au message affich dans le coin infrieur gauche de la fentre de VMware Player.

Pour plus dinformations sur les possibilits de VMware, consultez Google et les forums de VMware.

Voir aussi
http://www.vmware.fr/ ; http://www.vmware.com/fr/products/player/ ; http://www.vmware.com/vmtn/appliances/ ; http://www.vmware.com/support/ws55/doc/new_guest_tools_ws.html ; http://www.ubuntu.fr/ ; http://www.pcbsd.org/ ; la recette 1.11, Obtenir bash pour xBSD, page 21 ; la recette 1.15, Obtenir bash sans linstaller, page 25.

15.5. crire des boucles portables


Problme
Vous devez crire une boucle for, mais souhaitez quelle soit compatible avec les anciennes versions de bash.

Solution
La mthode suivante fonctionne avec les versions de bash-2.04+ :
$ for ((i=0; i<10; i++)); do echo $i; done

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.5. crire des boucles portables


0 1 2 3 4 5 6 7 8 9

341

Discussion
Les versions rcentes de bash acceptent dautres critures plus plaisantes de cette boucle, mais elles ne sont pas rtro-compatibles. Depuis bash-3.0+, vous pouvez employer la syntaxe for {x..y} :
$ for i in {1..10}; do echo $i; done 1 2 3 4 5 6 7 8 9 10

Si votre systme dispose de la commande seq, vous pouvez galement crire :


$ for i in $(seq 1 10); do echo $i; done 1 2 3 4 5 6 7 8 9 10

Voir aussi
help for ; man seq ; la recette 6.12, Boucler avec un compteur, page 135 ; la recette 6.13, Boucler avec des valeurs en virgule flottante, page 136 ; la recette 17.22, crire des squences, page 469.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

342

Chapitre 15 Scripts labors

15.6. crire une commande echo portable


Problme
Vous crivez un script qui doit sexcuter sur plusieurs versions dUnix et de Linux. Il a besoin dune commande echo au comportement cohrent, mme lorsque linteprteur de commandes nest pas bash.

Solution
Utilisez printf "%b" message ou vrifiez le systme et fixez xpg_echo laide de shopt -s xpg_echo. Si vous omettez la chane de format "%b" (comme dans printf message), printf tente dinterprter les caractres % contenus dans message, ce que vous ne souhaitez probablement pas. Le format "%b" est une extension de la commande printf standard qui empche cette mauvaise interprtation et dveloppe galement les squences dchappement dans message. La dfinition de xpg_echo est moins cohrente car elle ne fonctione quavec bash. Vous ouvez lemployer si vous tes certain que linterprteur de commandes sera toujours bash et non sh ou un autre shell similaire qui ne reconnat pas xpg_echo. Lutilisation de printf vous oblige remplacer toutes les commandes echo, mais cette instruction est dfinie par POSIX et devrait se comporter de manire cohrente sur tous les shells POSIX. Plus prcisment, vous devez crire printf "%b" la place de echo.
Si vous crivez $b la place de %b, vous nobtenez pas le rsultat escompt. Le message affich est vide, car le format est nul, sauf si $b est dfinie. Dans ce cas, le rsultat dpend de la valeur de $b. Ce bogue risque dtre difficile trouver, car $b et %b ont une apparence trs similaire :
$ printf "%b" "OK" OK $ printf "$b" "Dysfonctionnement" $

Discussion
Avec certains shells, la commande interne echo se comporte diffremment du programme externe echo employ par quelques systmes. Sous Linux, cette diffrence nest pas toujours facile dtecter car /bin/sh est gnralement bash (en gnral, mais il peut galement sagir de dash sur Ubuntu 6.10+) et des cas similaires existent sur certaines versions de BSD. Le comportement diffre dans lexpansion des squences dchappement. Les commandes echo internes au shell ont tendance ne pas les dvelopper, contrairement aux versions externes (par exemple, /bin/echo et /usr/bin/echo). Mais, une fois encore, cela varie dun systme lautre.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.6. crire une commande echo portable


Linux classique (/bin/bash) :
$ type -a echo echo is a shell builtin echo is /bin/echo $ builtin echo "un\tdeux\ntrois" un\tdeux\ntrois\n $ /bin/echo "un\tdeux\ntrois" un\tdeux\ntrois\n $ echo -e "un\tdeux\ntrois" un deux trois $ /bin/echo -e "un\tdeux\ntrois" un deux trois

343

$ shopt -s xpg_echo $ builtin echo "un\tdeux\ntrois" un deux trois $ shopt -u xpg_echo $ builtin echo "un\tdeux\ntrois" un\tdeux\ntrois\n

BSD classique (/bin/csh, puis /bin/sh) :


$ which echo echo: shell built-in command. $ echo "un\tdeux\ntrois" un\tdeux\ntrois\n $ /bin/echo "un\tdeux\ntrois" un\tdeux\ntrois\n $ echo -e "un\tdeux\ntrois" -e un\tdeux\ntrois\n $ /bin/echo -e "un\tdeux\ntrois" -e un\tdeux\ntrois\n $ printf "%b" "un\tdeux\ntrois" un deux trois

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

344
$ /bin/sh $ echo "un\tdeux\ntrois" un\tdeux\ntrois\n $ echo -e "un\tdeux\ntrois" un deux trois $ printf "%b" "un\tdeux\ntrois" un deux trois

Chapitre 15 Scripts labors

Solaris 10 (/bin/sh) :
$ which echo /usr/bin/echo $ type echo echo is a shell builtin $ echo "un\tdeux\ntrois" deux un trois $ echo -e "un\tdeux\ntrois" -e un deux trois $ printf "%b" "un\tdeux\ntrois" un deux trois

Voir aussi
help printf ; man 1 printf ; http://www.opengroup.org/onlinepubs/009695399/functions/printf.html ; la recette 2.3, Mettre en forme la sortie, page 34 ; la recette 2.4, crire la sortie sans le saut de ligne, page 35 ; la recette 15.1, Trouver bash de manire portable, page 334 ; la recette 15.3, Dvelopper des scripts shell portables, page 337 ; la recette 19.11, Constater un comportement trange de printf, page 497 ; la section printf, page 540.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.7. Dcouper lentre si ncessaire

345

15.7. Dcouper lentre si ncessaire


Problme
Vous souhaitez dcouper lentre uniquement si son contenu dpasse une certaine limite, mais la commande split cre toujours au moins un nouveau fichier.

Solution
# bash Le livre de recettes : fonc_decouper #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # Dcouper l'entre en segments de taille fixe uniquement si elle # dpasse une certaine limite. # Usage : Decouper <fichier> <prfixe> <option de limite> <arg de limite> # Exemple : Decouper $sortie ${sortie}_ --lines 100 # Voir split(1) et wc(1) pour les options. function Decouper { local fichier=$1 local prefixe=$2 local type_limite=$3 local taille_limite=$4 local option_wc # Vrifications initiales. if [ -z "$fichier" ]; then printf "%b" "Decouper : nom de fichier absent !\n" return 1 fi if [ -z "$prefixe" ]; then printf "%b" "Decouper : prfixe du fichier de sortie absent !\n" return 1 fi if [ -z "$type_limite" ]; then printf "%b" "Decouper : option de limite (ex. --lines) absente, voir 'man split' !\n" return 1 fi if [ -z "$taille_limite" ]; then printf "%b" "Decouper : taille de limite (ex. 100) absente, voir 'man split' !\n" return 1 fi # Convertir les options de split en option de wc. Toutes les options # ne sont pas reconnues par wc/split sur tous les systmes. case $type_limite in -b|--bytes) option_wc='-c';;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

346
-C|--line-bytes) option_wc='-L';; -l|--lines) option_wc='-l';; esac

Chapitre 15 Scripts labors

# Si la limite est dpasse. if [ "$(wc $option_wc $fichier | awk '{print $1}')" -gt $taille_limite ]; then # Faire quelque chose. split --verbose $type_limite $taille_limite $fichier $prefixe fi } # Fin de la fonction Decouper.

Discussion
En fonction de votre systme, certaines options (par exemple, -C) ne seront peut-tre pas disponibles pour split ou wc.

Voir aussi
recette 8.13, Compter les lignes, les mots ou les caractres dans un fichier, page 187.

15.8. Afficher la sortie en hexadcimal


Problme
Vous souhaiter examiner la sortie en mode hexadcimal afin de vrifier la prsence dun caractre despacement ou non imprimable.

Solution
Dirigez la sortie vers hexdump et utilisez loption -C pour obtenir une sortie canonique :
$ hexdump 00000000 00000010 00000020 0000002c -C 4c 0a 0a nom_fichier 69 67 6e 65 20 31 0a 4c 69 67 6e 65 20 34 0a 4c 69 67 6e 65 20 4c 69 67 6e 65 20 32 0a 0a 4c 69 67 6e 65 20 35 36 0a 0a 0a |Ligne 1.Ligne 2.| |.Ligne 4.Ligne 5| |..Ligne 6...|

Par exemple, nl insre des espaces (code ASCII 20), puis le numro de ligne, puis une tabulation (code ASCII 09) :
$ nl -ba nom_fichier | hexdump 00000000 20 20 20 20 20 31 09 00000010 20 20 20 20 32 09 4c 00000020 20 20 20 33 09 0a 20 00000030 6e 65 20 34 0a 20 20 00000040 65 20 35 0a 20 20 20 00000050 20 37 09 4c 69 67 6e 00000060 38 09 0a 20 20 20 20 0000006b -C 4c 69 67 6e 65 20 31 69 67 6e 65 20 32 0a 20 20 20 20 34 09 4c 20 20 20 35 09 4c 69 20 20 36 09 0a 20 20 65 20 36 0a 20 20 20 20 39 09 0a 0a 20 69 67 20 20 20 | 1.Ligne 1. | 20 | 2.Ligne 2. | 67 | 3.. 4.Lig| 6e |ne 4. 5.Lign| 20 |e 5. 6.. | 20 | 7.Ligne 6. | |8.. 9..|

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.8. Afficher la sortie en hexadcimal

347

Discussion
hexdump est un utilitaire BSD disponible galement sur de nombreux distributions Linux. Sur dautres systmes, en particulier Solaris, il nest pas install par dfaut. Vous pouvez obtenir un affichage en octal avec la commande od, mais le rsultat est plus difficile lire :
$ nl -ba nom_fichier | 0000000 2020 2020 3120 0000020 2020 2020 0932 0000040 2020 3320 0a09 0000060 656e 3420 200a 0000100 2065 0a35 2020 0000120 3720 4c09 6769 0000140 0938 200a 2020 0000153 $ nl -ba nom_fichier | 0000000 20 20 20 20 20 0000020 20 20 20 20 32 0000040 20 20 20 33 09 0000060 6e 65 20 34 0a 0000100 65 20 35 0a 20 0000120 20 37 09 4c 69 0000140 38 09 0a 20 20 0000153 od -x 4c09 6769 694c 6e67 2020 2020 2020 2020 2020 3620 656e 3620 2020 0939 656e 2065 3420 0935 0a09 200a 000a 3120 0a32 4c09 694c 2020 2020 200a 2020 6769 6e67 2020 2020

od 31 09 0a 20 20 67 20

-tx1 09 4c 4c 69 20 20 20 20 20 20 6e 65 20 20

69 67 20 20 20 20 39

67 6e 20 20 36 36 09

6e 65 20 35 09 0a 0a

65 20 34 09 0a 20

20 32 09 4c 20 20

31 0a 4c 69 20 20

0a 20 69 67 20 20

20 20 67 6e 20 20

Il existe galement un script Perl simple que vous pourrez trouver http://www.khngai. com/perl/bin/hexdump.txt :
$ ./hexdump.pl nom_fichier /0 /1 0000 : 4C 69 0010 : 0A 4C 0020 : 0A 0A /2 67 69 4C /3 6E 67 69 /4 65 6E 67 /5 20 65 6E /6 31 20 65 /7 0A 34 20 /8 4C 0A 36 /9/ A 69 67 4C 69 0A 0A /B /C /D /E /F 6E 65 20 32 0A 67 6E 65 20 35 0A 0123456789ABCDEF Ligne 1.Ligne 2. .Ligne 4.Ligne 5 ..Ligne 6...

Voir aussi
man hexdump ; man od ; http://www.khngai.com/perl/bin/hexdump.txt ; http://gnuwin32.sourceforge.net/packages/hextools.htm ; la section Tableau des valeurs ASCII, page 555.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

348

Chapitre 15 Scripts labors

15.9. Utiliser la redirection du rseau de bash


Problme
Vous souhaitez envoyer ou recevoir du trafic rseau trs simple, mais vous ne disposez daucun outil du type netcat.

Solution
Si votre version de bash (2.04+) a t compile avec loption --enable-net-redirections (ce nest pas le cas sous Debian et ses variantes), vous pouvez lutiliser directement. Lexemple suivant est galement donn la recette 15.10, page 349 :
$ exec 3<> /dev/tcp/www.ippages.com/80 $ echo -e "GET /simple/?se=1 HTTP/1.0\n" >&3 $ cat <&3 HTTP/1.1 200 OK Date: Tue, 28 Nov 2006 08:13:08 GMT Server: Apache/2.0.52 (Red Hat) X-Powered-By: PHP/4.3.9 Set-Cookie: smipcomID=6670614; expires=Sun, 27-Nov-2011 08:13:09 GMT; path=/ Pragma: no-cache Cache-Control: no-cache, must-revalidate Content-Length: 125 Connection: close Content-Type: text/plain; charset=ISO-8859-1 72.NN.NN.225 (US-United States) http://www..com Tue, 28 Nov 2006 08:13:09 UTC/GMT flagged User Agent - reduced functionality
Comme nous lavons mentionn, cette solution ne fonctionnera probablement pas sous Debian et ses variantes, comme Ubuntu, puisque ces distributions ne compilent pas bash avec --enable-net-redirections.

Discussion
Comme lexplique la recette 15.12, page 356, il est possible dutiliser exec pour rediriger de manire permanente des descripteurs de fichiers au sein de la session shell en cours. La premire commande place lentre et la sortie sur le descripteur de fichier 3. La deuxime ligne envoie une commande simple au serveur web indiqu sur la premire ligne. Notez que lagent utilisateur apparatra sous la rfrence "-" du ct du serveur web, do lavertissement flagged User Agent . La troisime commande affiche simplement le rsultat. Les protocoles TCP et UDP sont tous deux pris en charge. Voici un exemple simple denvoi de messages syslog un serveur distant (pour une utilisation relle, nous vous conseillons demployer logger, qui est beaucoup plus convivial et robuste) :

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.10. Dterminer mon adresse


echo "<133>$0[$$]: Test de message syslog depuis bash" > /dev/udp/loghost.exemple.fr/514

349

PuisquUDP est un protocole de type datagramme, lexemple est beaucoup plus simple que celui bas sur TCP. <133> correspond la valeur de priorit syslog pour local0.notice, calcule conformment la RFC 3164. Consultez la RFC 4.1.1 PRI Part et la page de manuel de logger. $0 est le nom et $$ lidentifiant de processus du programme en cours. Ce nom sera -bash pour le shell de connexion.

Voir aussi
man logger ; la RFC 3164 : The BSD Syslog Protocol, http://www.faqs.org/rfcs/rfc3164.html ; la recette 15.10, Dterminer mon adresse, page 349 ; la recette 15.12, Rediriger la sortie pour toute la dure dun script, page 356 ; la recette 15.14, Journaliser vers syslog depuis un script, page 359 ; lannexe B, Exemples fournis avec bash, page 559, plus particulirement ./functions/ gethtml.

15.10. Dterminer mon adresse


Problme
Vous souhaitez connatre ladresse IP de votre machine.

Solution
Il nexiste aucune bonne manire dobtenir cette information qui fonctionnera sur tous les systmes dans tous les cas. Nous prsenterons donc plusieurs solutions possibles. Premirement, vous pouvez analyser la sortie produite par ifconfig afin dy trouver des adresses IP Les exemples suivants retournent la premire adresse IP qui ne correspond . pas une boucle de retour ou rien si aucune interface nest configure ou active.
# bash Le livre de recettes : trouver_ip # IPv4 - avec awk, cut et head. $ /sbin/ifconfig -a | awk '/(cast)/ { print $2 }' | cut -d':' -f2 | head -1 # IPv4 - avec Perl, juste pour le plaisir. $ /sbin/ifconfig -a | perl -ne 'if ( m/^\s*inet (?:addr:)?([\d.]+).*?cast/ ) { print qq($1\n); exit 0; }'

# IPv6 - avec awk, cut et head. $ /sbin/ifconfig -a | egrep 'inet6 addr: |address: ' | cut -d':' -f2- | cut -d'/' -f1 | head -1 | tr -d ' '

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

350

Chapitre 15 Scripts labors

# IPv6 - avec Perl, juste pour le plaisir. $ /sbin/ifconfig -a | perl -ne 'if ( m/^\s*(?:inet6)? \s*addr(?:ess)?: ([09A-Fa-f:]+)/ ) { print qq($1\n); exit 0; }'

Deuximement, vous pouvez obtenir votre nom dhte et en dduire une adresse IP . Cette solution est peu fiable car les systmes actuels, en particulier les stations de travail, peuvent avoir des noms dhte incomplets ou incorrects et/ou se trouver sur un rseau dynamique qui noffre pas une recherche inverse adquate. Utilisez-la en connaissance de cause.
$ host $(hostname)

Troisimement, vous tes peut-tre plus intress par ladresse externe routable de votre machine que par son adresse interne (RFC 1918). Dans ce cas, vous pouvez employer un hte externe, comme http://www.ippages.com/ ou FollowMeIP (voir ci-aprs) pour connatre ladresse de votre pare-feu ou de votre priphrique NAT. Linconvnient de cette mthode rside dans le fait que les systmes autres que Linux ne disposent pas toujours dun outil de type wget. lynx ou curl fonctionneront galement, mais, en gnral, ils ne sont pas installs par dfaut (Mac OS X 10.4 dispose de curl). Notez que ladresse IP est volontairement masque dans les exemples suivants :
$ wget -qO - http://www.ippages.com/simple/ 72.NN.NN.225 (US-United States) http://www.ippages.com Mon, 27 Nov 2006 21:02:23 UTC/GMT (5 of 199 allowed today) alternate access in XML format at: http://www.ippages.com/xml alternate access via SOAP at: http://www.ippages.com/soap/server.php alternate access via RSS feed at: http://www.ippages.com/rss.php alternate access in VoiceXML format at: http://www.ippages.com/voicexml $ wget -qO - http://www.ippages.com/simple/?se=1 72.NN.NN.225 (US-United States) http://www.ippages.com Tue, 28 Nov 2006 08:11:36 UTC/GMT $ wget -qO - http://www.ippages.com/simple/?se=1 | cut -d' ' -f1 72.NN.NN.225

$ lynx -dump http://www.ippages.com/simple/?se=1 | cut -d' ' -f1 72.NN.NN.225 $ curl -s http://www.ippages.com/simple/?se=1 | cut -d' ' -f1 72.NN.NN.225

Si vous navez pas accs aux programmes prcdents, mais que votre version de bash (2.04+) a t compile avec --enable-net-redirections (ce nest pas le cas sous Debian et ses variantes), utilisez le shell lui-mme. Pour plus de dtails, consultez la recette 15.9, page 348.
$ exec 3<> /dev/tcp/www.ippages.com/80 $ echo -e "GET /simple/?se=1 HTTP/1.0\n" >&3 $ cat <&3 HTTP/1.1 200 OK

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.10. Dterminer mon adresse

351

Date: Tue, 28 Nov 2006 08:13:08 GMT Server: Apache/2.0.52 (Red Hat) X-Powered-By: PHP/4.3.9 Set-Cookie: smipcomID=6670614; expires=Sun, 27-Nov-2011 08:13:09 GMT; path=/ Pragma: no-cache Cache-Control: no-cache, must-revalidate Content-Length: 125 Connection: close Content-Type: text/plain; charset=ISO-8859-1 72.NN.NN.225 (US-United States) http://www..com Tue, 28 Nov 2006 08:13:09 UTC/GMT flagged User Agent - reduced functionality

$ exec 3<> /dev/tcp/www.ippages.com/80 $ echo -e "GET /simple/?se=1 HTTP/1.0\n" >&3 $ egrep '^[0-9.]+ ' <&3 | cut -d' ' -f1 72.NN.NN.225

FollowMeIP est un peu diffrent. Il en existe un client, http://ipserver.fmip.org/, mais vous nen avez pas rellement besoin. Vous remarquerez que le port utilis nest pas standard et que cette solution ne fonctionnera pas avec un filtrage en sortie strict (sur le pare-feu).
# Utiliser telnet. $ telnet ipserver.fmip.org 42750 2>&1 | egrep '^[0-9]+' 72.NN.NN.225

# Utiliser bash directement (plus facile, si disponible). $ exec 3<> /dev/tcp/ipserver.fmip.org/42750 && cat <&3 72.NN.NN.225

Discussion
Le code awk et Perl montr dans la premire solution est intressant cause des variantes des systmes dexploitation dcrites ici. Cependant, les lignes qui nous concernent contiennent toutes Bcast ou broadcast (ou inet6 addr: ou address:)1. Par consquent, une fois ces lignes obtenues, il suffit de les analyser pour trouver le champ voulu. Bien entendu, Linux nous complique la tche en utilisant un format diffrent. Tous les systmes nexigent pas la prsence du chemin (si vous ntes pas root) ou un argument -a ifconfig, mais ils lacceptent tous. Dans tous les cas, il est donc prfrable dinvoquer /sbin/ifconfig -a. Voici des exemples de sortie de ifconfig provenant de diffrentes machines :

1. N.d.T. : si votre version de ifconfig a t traduite en franais, la sortie contiendra probablement adr: ou adresse:.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

352

Chapitre 15 Scripts labors

# Linux $ /sbin/ifconfig eth0 Link encap:Ethernet HWaddr 00:C0:9F:0B:8F:F6 inet addr:192.168.99.11 Bcast:192.168.99.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:33073511 errors:0 dropped:0 overruns:0 frame:827 TX packets:52865023 errors:0 dropped:0 overruns:1 carrier:7 collisions:12922745 txqueuelen:100 RX bytes:2224430163 (2121.3 Mb) TX bytes:51266497 (48.8 Mb) Interrupt:11 Base address:0xd000 lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:659102 errors:0 dropped:0 overruns:0 frame:0 TX packets:659102 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:89603190 (85.4 Mb) TX bytes:89603190 (85.4 Mb)

$ /sbin/ifconfig eth0 Link encap:Ethernet HWaddr 00:06:29:33:4D:42 inet addr:192.168.99.144 Bcast:192.168.99.255 Mask:255.255.255.0 inet6 addr: fe80::206:29ff:fe33:4d42/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1246774 errors:14 dropped:0 overruns:0 frame:14 TX packets:1063160 errors:0 dropped:0 overruns:0 carrier:5 collisions:65476 txqueuelen:1000 RX bytes:731714472 (697.8 MiB) TX bytes:942695735 (899.0 MiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:144664 errors:0 dropped:0 overruns:0 frame:0 TX packets:144664 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:152181602 (145.1 MiB) TX bytes:152181602 (145.1 MiB) Link encap:IPv6-in-IPv4 inet6 addr: ::127.0.0.1/96 Scope:Unknown UP RUNNING NOARP MTU:1480 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:101910 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)

sit0

# NetBSD $ /sbin/ifconfig -a pcn0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500 address: 00:0c:29:31:eb:19

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.10. Dterminer mon adresse


media: Ethernet autoselect (autoselect) inet 192.168.99.56 netmask 0xffffff00 broadcast 192.168.99.255 inet6 fe80::20c:29ff:fe31:eb19%pcn0 prefixlen 64 scopeid 0x1 lo0: flags=8009<UP,LOOPBACK,MULTICAST> mtu 33196 inet 127.0.0.1 netmask 0xff000000 inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2 ppp0: flags=8010<POINTOPOINT,MULTICAST> mtu 1500 ppp1: flags=8010<POINTOPOINT,MULTICAST> mtu 1500 sl0: flags=c010<POINTOPOINT,LINK2,MULTICAST> mtu 296 sl1: flags=c010<POINTOPOINT,LINK2,MULTICAST> mtu 296 strip0: flags=0 mtu 1100 strip1: flags=0 mtu 1100

353

# OpenBSD, FreeBSD $ /sbin/ifconfig lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 33224 inet 127.0.0.1 netmask 0xff000000 inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x5 le1: flags=8863<UP,BROADCAST,NOTRAILERS,RUNNING,SIMPLEX,MULTICAST> mtu 1500 address: 00:0c:29:25:df:00 inet6 fe80::20c:29ff:fe25:df00%le1 prefixlen 64 scopeid 0x1 inet 192.168.99.193 netmask 0xffffff00 broadcast 192.168.99.255 pflog0: flags=0<> mtu 33224 pfsync0: flags=0<> mtu 2020

# Solaris $ /sbin/ifconfig -a lo0: flags=1000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4> mtu 8232 index 1 inet 127.0.0.1 netmask ff000000 pcn0: flags=1004843<UP,BROADCAST,RUNNING,MULTICAST,DHCP,IPv4> mtu 1500 index 2 inet 192.168.99.159 netmask ffffff00 broadcast 192.168.99.255 # Mac $ /sbin/ifconfig lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384 inet 127.0.0.1 netmask 0xff000000 inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280 stf0: flags=0<> mtu 1280 en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 inet6 fe80::20d:93ff:fe65:f720%en0 prefixlen 64 scopeid 0x4 inet 192.168.99.155 netmask 0xffffff00 broadcast 192.168.99.255 ether 00:0d:93:65:f7:20 media: autoselect (100baseTX <half-duplex>) status: active supported media: none autoselect 10baseT/UTP <half-duplex>
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

354

Chapitre 15 Scripts labors

10baseT/UTP <full-duplex> 10baseT/UTP <full-duplex,hw-loopback> 100baseTX <half-duplex> 100baseTX <full-duplex> 100baseTX <full-duplex,hw-loopback> fw0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 2030 lladdr 00:0d:93:ff:fe:65:f7:20 media: autoselect <full-duplex> status: inactive supported media: autoselect <full-duplex>

Voir aussi
man awk ; man curl ; man cut ; man head ; man lynx ; man perl ; man wget ; http://www.ippages.com/ ou http://www.showmyip.com/ ; http://ipserver.fmip.org/ ; http://abcdrfc.free.fr/rfc-vf/rfc1918.html ; la recette 15.9, Utiliser la redirection du rseau de bash, page 348 ; la recette 15.12, Rediriger la sortie pour toute la dure dun script, page 356.

15.11. Obtenir lentre depuis une autre machine


Problme
Votre script doit obtenir son entre depuis une autre machine, peut-tre pour vrifier si un fichier existe ou si un processus est en cours dexcution.

Solution
Utilisez SSH avec des cls publiques et une substitution de commandes. Pour cela, configurez SSH de manire ne pas entrer un mot de passe, comme lexplique la recette 14.21, page 321. Ensuite, ajustez la commande excute par SSH pour quelle affiche exactement ce dont votre script a besoin en entre. Puis, utilisez simplement une substitution de commandes.
#!/usr/bin/env bash # bash Le livre de recettes : subst_commande HOTE_DISTANT='hote.exeple.fr' # Requis. FICHIER_DISTANT='/etc/passwd' # Requis. UTILISATEUR_SSH='user@' # Facultatif, fixer '' pour ne pas utiliser.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.11. Obtenir lentre depuis une autre machine


#ID_SSH='-i ~/.ssh/foo.id' ID_SSH=''

355

# Facultatif, fixer '' pour ne pas utiliser.

resultat=$( ssh $ID_SSH $UTILISATEUR_SSH$HOTE_DISTANT \ "[ -r $FICHIER_DISTANT ] && echo 1 || echo 0" ) || { echo "Echec de la commande !" >&2; exit 1; } if [ $resultat = 1 ]; then echo "$FICHIER_DISTANT prsent sur $HOTE_DISTANT" else echo "$FICHIER_DISTANT absent de $HOTE_DISTANT" fi

Discussion
Cet exemple sappuie sur plusieurs oprations intressantes. Tout dabord, vous remarquerez le fonctionnement de $UTILISATEUR_SSH et de $ID_SSH. Elles jouent un rle uniquement lorsquelles possdent une valeur, mais sont ignores lorsquelles sont vides. Cela nous permet de placer ces valeurs dans un fichier de configuration et le code dans une fonction, ou les deux.
# Ligne rsultante lorsque les variables ont une valeur : ssh -i ~/.ssh/foo.id user@hote.exemple.fr [...] # Sans valeur : ssh hote.exemple.fr [...]

Ensuite, nous configurons la commande excute par SSH afin quil existe toujours une sortie (0 ou 1), puis nous vrifions que $resultat nest pas vide. Il sagit dune manire de sassurer que la commande SSH sest excute (voir galement la recette 4.2, page 73). Si $resultat est vide, nous regroupons la commande dans un bloc de code { } pour afficher un message derreur et quitter le script. Mais, puisque nous obtenons toujours une sortie de la commande SSH, nous devons tester la valeur. Il nest pas possible dappeler simplement if [ $resultat ]; then. Si nous nutilisons pas le bloc de code, nous naffichons lavertissement que si la commande SSH a retourn un rsultat vide, mais nous quittons toujours le script. Pour en comprendre la raison, relisez bien le code. Il est en effet trs facile de tomber dans ce pige. De mme, si nous utilisons un sous-shell ( ) la place du bloc de code { }, nous nobtenons pas le rsultat escompt car linstruction exit 1 quitte le sous-shell et non le script. Celui-ci continue son excution mme aprs lchec de la commande SSH le code apparat presque correct et ce bogue est difficile trouver. Le dernier cas de test peut tre crit de la manire suivante. La version employer dpend de votre style et du nombre dinstructions excuter dans chaque situation. Dans ce cas, cela na pas dimportance.
[ $resultat = 1 ] && echo "$FICHIER_DISTANT prsent sur $HOTE_DISTANT" \ || echo "$FICHIER_DISTANT absent de $HOTE_DISTANT"

Enfin, nous prenons soin de la mise en forme afin que les lignes ne soient pas trop longues, que le code reste lisible et que notre objectif soit clair.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

356

Chapitre 15 Scripts labors

Voir aussi
la recette 2.14, Enregistrer ou runir la sortie de plusieurs commandes, page 44 ; la recette 4.2, Connatre le rsultat de lexcution dune commande, page 73 ; la recette 14.21, Utiliser SSH sans mot de passe, page 321 ; la recette 17.18, Filtrer la sortie de ps sans afficher le processus grep, page 463 ; la recette 17.19, Dterminer si un processus sexcute, page 464.

15.12. Rediriger la sortie pour toute la dure dun script


Problme
Vous souhaitez rediriger la sortie dun script entier sans avoir modifier chaque instruction echo ou printf.

Solution
Utilisez une fonctionnalit peu connue de la commande exec pour rediriger STDOUT ou STDERR :
# Facultatif, conserver lancienne erreur standard. exec 3>&2 # Les sorties vers STDERR sont rediriges vers un fichier de # journalisation des erreurs. exec 2> /chemin/vers/erreur_log # Emplacement du script dont lerreur standard est redirige # de manire globale. # Dsactiver la redirection en inversant STDERR et en fermant FH3. exec 2>&3-

Discussion
Normalement, exec remplace le shell en cours dexcution par la commande passe en argument, dtruisant ainsi le shell dorigine. Cependant, lorsquaucune commande nest prcise, elle permet de manipuler la redirection du shell en cours. Cette redirection ne se limite pas STDOUT ou STDERR, mais il sagit des deux cibles les plus courantes.

Voir aussi
help exec ; la recette 15.9, Utiliser la redirection du rseau de bash, page 348.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.13. Contourner les erreurs liste darguments trop longue

357

15.13. Contourner les erreurs liste darguments trop longue


Problme
Vous recevez une erreur liste darguments trop longue lors de lexcution dune commande impliquant une expansion des caractres gnriques du shell.

Solution
Utilisez la commande xargs, conjointement find, pour dcomposer la liste des arguments. Pour les cas simples, remplacez ls par une boucle for ou une commande find :
$ ls /chemin/avec/beaucoup/beaucoup/de/fichiers/*e* -/bin/bash: /bin/ls: Liste darguments trop longue # Petite dmonstration. Les caractres ~ servent dillustration. $ for i in ./des_fichiers/*e*; do echo "~$i~"; done ~./des_fichiers/fichier avec |~ ~./des_fichiers/fichier avec ;~ ~./des_fichiers/fichier avec :~ ~./des_fichiers/fichier avec des espaces~ ~./des_fichiers/fichier avec un signe =~ ~./des_fichiers/Fichier incluant un saut de ligne~ ~./des_fichiers/fichier normal~ ~./des_fichiers/Un fichier avec des [crochets]~ ~./des_fichiers/Un fichier avec des (parentheses)~ $ find ./des_fichiers -name '*e*' -exec echo ~{}~ \; ~./des_fichiers/fichier avec ;~ ~./des_fichiers/fichier avec des espaces~ ~./des_fichiers/fichier normal~ ~./des_fichiers/Un fichier avec des [crochets]~ ~./des_fichiers/fichier avec :~ ~./des_fichiers/fichier avec |~ ~./des_fichiers/fichier avec un signe =~ ~./des_fichiers/Fichier incluant un saut de ligne~ ~./des_fichiers/Un fichier avec des (parentheses)~ $ for i in /chemin/avec/beaucoup/beaucoup/de/fichiers/*e*; do echo "$i"; done [Cela fonctionne, mais la sortie est trop longue pour la donner.]

$ find /chemin/avec/beaucoup/beaucoup/de/fichiers/ -name '*e*' [Cela fonctionne, mais la sortie est trop longue pour la donner.]

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

358

Chapitre 15 Scripts labors

Lexemple prcdent fonctionne parfaitement avec la commande echo, mais lorsque vous passez "$i" dautres programmes, en particulier dautres constructions du shell, la variable $IFS et dautres oprations danalyse peuvent entrer en scne. Les outils GNU find et xargs tiennent compte de ce point avec find -print0 et xargs -0. (Nous ne savons pas pourquoi les arguments sont -print0 et -0, au lieu dtre cohrents.) Ces arguments indiquent find demployer le caractre nul (qui ne peut apparatre dans un nom de fichier) la place dun caractre espace comme sparateur des lments de la sortie et xargs de lutiliser comme sparateur des lments de lentre. Ainsi, mme les fichiers dont les noms contiennent des caractres tranges seront correctement traits.
$ find /chemin/avec/beaucoup/beaucoup/de/fichiers/ -name '*e*' -print0 | xargs -0 proggy

Discussion
Par dfaut, bash (et sh) retourne tels quels les motifs sans correspondance. Autrement dit, si aucun fichier ne correspond son motif, la variable $i de la boucle for prend la valeur ./des_fichiers/*e*. Vous pouvez invoquer shopt -s nullglob pour que les motifs de noms de fichiers qui ne correspondent aucun fichier deviennent une chane nulle. Vous pourriez penser que la boucle for utilise dans le cas simple conduit au mme problme que la commande ls, mais il nen est rien, comme lexplique Chet Ramey :
ARG_MAX fixe la limite sur lespace total utilis par les appels systme de type exec*. Ainsi, le noyau connat la taille maximale du tampon allouer. Cela concerne les trois arguments de execve : nom du programme, vecteur des arguments et environnement. La commande ls choue car le nombre doctet total des arguments de execve dpasse ARG_MAX. La boucle for russit car tout se fait de manire interne : mme si lintgralit de la liste est gnre et enregistre, execve nest jamais appel.

Faites attention car find pourrait trouver un trs grand nombre de fichiers. En effet, par dfaut, cette commande parcourt rcursivement tous les sous-rpertoires, contrairement ls. Certaines versions de find disposent dune option -d qui contrle la profondeur de parcours. La boucle for constitue probablement la solution la plus simple. Pour connatre la taille limite fixe sur votre systme, utilisez la commande getconf ARG_MAX. Elle varie de manire importante, comme le montre le tableau 15-1 (voir galement getconf LINE_MAX). Tableau 15-1. Limites du systme
Systme HP-UX 11 Solaris (8, 9, 10) NetBSD 2.0.2, OpenBSD 3.7, OS/X Linux (Red Hat, Debian, Ubuntu) FreeBSD 5.4 ARG_MAX (octets) 2 048 000 1 048 320 262 144 131 072 65 536

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.14. Journaliser vers syslog depuis un script

359

Voir aussi
http://www.gnu.org/software/coreutils/faq/coreutils-faq.html#Argument-list-too-long ; la recette 9.2, Traiter les noms de fichiers contenant des caractres tranges, page 193.

15.14. Journaliser vers syslog depuis un script


Problme
Vous souhaitez que votre script envoie des messages de journalisation syslog.

Solution
Utilisez logger, Netcat ou les fonctions bash de redirection du rseau. logger est install par dfaut sur la plupart des systmes et constitue une solution simple pour envoyer des messages aux services syslog local. En revanche, il ne permet pas de communiquer avec des htes distants. Pour cela, vous pouvez vous tourner vers bash ou Netcat.
$ logger -p local0.notice -t $0[$$] message de test

Netcat est appel le couteau suisse de TCP/IP . En gnral, il nest pas install par dfaut. Par ailleurs, son ct outil de piratage peut le voir interdit par certaines politiques de scurit. Cependant, les fonctions de redirection du rseau disponibles dans bash permettent dobtenir des rsultats trs similaires. Pour plus de dtails sur la partie <133>$0[$$], consultez les explications de la recette 15.9, page 348.
# Netcat $ echo "<133>$0[$$]: Test dun message syslog depuis Netcat" | nc -w1 -u loghost 514

# bash $ echo "<133>$0[$$]: Test dun message syslog depuis bash" \ > /dev/udp/loghost.exemple.fr/514

Discussion
logger et Netcat disposent dun grand nombre de fonctionnalits, que nous ne pouvons inclure ici. Consultez leur page de manuel respective.

Voir aussi
man logger ; man nc ; la recette 15.9, Utiliser la redirection du rseau de bash, page 348.

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

360

Chapitre 15 Scripts labors

15.15. Envoyer un message lectronique depuis un script


Problme
Vous souhaitez que votre script puisse envoyer des courriers lectroniques, avec ou sans pices jointes.

Solution
Pour mettre en uvre les solutions proposes, un client de messagerie, comme mail, mailx ou mailto, et un agent de transfert du courrier (MTA Message Transfer Agent) doivent tre installs et oprationnels. Dautre part, votre environnement de messagerie doit tre correctement configur. Malheureusement, toutes ces hypothses ne sont pas toujours satisfaites et les solutions devront tre soigneusement testes dans lenvironnement cible. La premire manire denvoyer un courrier lectronique depuis un script consiste crire le code qui gnre et envoie le message :
# Simple. cat corps_message | mail -s "Objet du message" dest1@exemple.fr dest2@exemple.fr

Ou :
# Pice jointe uniquement. $ uuencode /chemin/vers/fichier_piece_jointe nom_piece_jointe | mail -s "Objet du message" dest1@exemple.fr dest2@exemple.fr

Ou :
# Pice jointe et corps. $ (cat corps_message ; uuencode /chemin/vers/fichier_piece_jointe nom_piece_jointe) | mail -s "Objet du message" dest1@exemple.fr dest2@exemple.fr

En pratique, ce nest pas toujours aussi simple. Tout dabord, alors que uuencode sera probablement install, mail et ses amis pourront faire dfaut, ou bien leurs possibilits varieront. Dans certains cas, mail et mailx sont le mme programme, avec des liens physiques ou symboliques. Dans une utilisation relle, vous souhaiterez mettre en place une certaine abstraction afin de faciliter la portabilit. Par exemple, mail fonctionne avec Linux et BSD, mais mailx est obligatoire avec Solaris puisque sa version de mail ne reconnat pas loption -s. mailx fonctionne avec certaines distributions Linux (par exemple, Debian), mais pas avec dautres (par exemple, Red Hat). Dans notre code, nous choisissons le client de messagerie en fonction du nom dhte, mais une commande uname -o serait prfrable.
# bash Le livre de recettes : exemple_email # Fixer certains paramtres de messagerie. Utiliser une # instruction case avec uname ou hostname pour ajuster # ces paramtres l'environnement.
[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.15. Envoyer un message lectronique depuis un script

361

case $HOSTNAME in *.societe.fr ) MAILER='mail' ;; # Linux et BSD. hote1.* ) MAILER='mailx' ;; # Solaris, BSD et certains Linux. hote2.* ) MAILER='mailto' ;; # Pratique, si install. esac DESTINATAIRES='dest1@exemple.fr dest2@exemple.fr' OBJET="Donnes de $0" [...] # Crer le corps comme un fichier ou une variable avec echo, # printf ou un here document. Crer ou modifier $OBJET et/ou # $DESTINATAIRES en fonction des besoins. [...] ( echo $corps_message ; uuencode $piece_jointe $(basename $piece_jointe) ) \ | $MAILER -s "$OBJET" "$DESTINATAIRES"

Notez que lenvoi de pices jointes dpend galement du client utilis pour lire le message rsultant. Les clients modernes, comme Thunderbird et Outlook, dtecteront un message uuencod et le prsenteront comme une pice jointe. Ce ne sera peut-tre pas le cas avec dautres clients. Vous pouvez toujours enregistrer le message et le passer uudecode (cet outil est suffisamment intelligent pour sauter le corps du message et ne traiter que la pice jointe), mais ce nest pas aussi convivial. La deuxime manire denvoyer un courrier lectronique depuis un script consiste externaliser cette tche cron. Bien que les fonctionnalits de cron varient dun systme lautre, toutes les versions permettent denvoyer par courrier lectronique la sortie dune tche son propritaire ou lutilisateur dsign par la variable MAILTO. Vous pouvez donc exploiter ce fonctionnement pour envoyer des messages lectroniques, en supposant que votre infrastructure de messagerie soit oprationnelle. La bonne manire dcrire un script excut par cron (et dautant diront pour tout script ou outil Unix) consiste le rendre silencieux, except lorsquil rencontre un avertissement ou une erreur. Si ncessaire, ajoutez une option -v pour le passer en mode plus bavard, mais ne lexcutez pas dans ce mode depuis cron, tout au moins aprs avoir termin les tests. En effet, comme nous lavons not, cron envoie par courrier lectronique lintgralit de la sortie de la tche. Si vous recevez un message de cron chaque fois que le script sexcute, vous finirez par les ignorer. En revanche, si le script est silencieux, except en cas de problme, vous ne recevrez un avis que dans ce cas.

Discussion
mailto est une version multimdia et compatible MIME de mail. Vous pouvez donc viter lemploi de uuencode pour envoyer des pices jointes, mais il nest pas aussi rpandu que mail et mailx. En cas de problmes, elm ou mutt peut tre utilis la place de mail, mailx ou mailto, mais il est peu probable que ces outils soient installs par dfaut. Par ailleurs, certaines versions de ces programmes acceptent loption -r qui permet de prciser une adresse de retour. mutt dispose galement dune option -a qui facilite lenvoi de pices jointes.
cat "$corps_message" | mutt -s "$objet" -a "$piece_jointe" "$destinataires"

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

362

Chapitre 15 Scripts labors

Vous pouvez galement vous intresser mpack, mais il est peu probable quil soit install par dfaut. Consultez le dpt de logiciels de votre systme ou bien tlchargez le code source depuis ftp://ftp.andrew.cmu.edu/pub/mpack/. Voici ce quen dit sa page de manuel :
Le programme mpack encode le fichier nomm en un ou plusieurs messages MIME. Les messages rsultants sont envoys par courrier lectronique un ou plusieurs destinataires, sont crits dans un fichier nomm ou un ensemble de fichiers, ou bien sont posts dans diffrents groupes de discussion.

Le chapitre 8 du livre Introduction aux scripts shell de Nelson H.F. Beebe et Arnold Robbins (ditions OReilly) propose une autre manire de grer les noms et les emplacements des clients de messagerie :
# bash Le livre de recettes : exemple_email_iss # Extrait du chapitre 8 du livre Introduction aux scripts shell. for MAIL in /bin/mailx /usr/bin/mailx /usr/sbin/mailx /usr/ucb/mailx /bin/mail /usr/bin/mail; do [ -x $MAIL ] && break done [ -x $MAIL ] || { echo 'Client de messagerie non trouv !' >&2; exit 1; }

uuencode est une ancienne mthode de conversion des donnes binaires en un texte ASCII avant leur transfert sur des lignes qui ne prennent pas en charge le format binaire, cest--dire Internet avant quil ne devienne lInternet et le Web. Nous savons de source sre que ce type de lignes existe encore. Mme si vous nen rencontrerez jamais, il nest pas inutile de savoir convertir une pice jointe en un format ASCII que tout client de messagerie moderne saura reconnatre. Il existe galement les utilitaires uudecode et mimencode. Sachez que les fichiers uuencods sont 30 % plus volumineux que leurs versions binaires. Vous pouvez donc les compresser avant de les uuencoder. Le problme du courrier lectronique, mises part les diffrences entre les clients de messagerie (MUA Mail User Agent) comme mail et mailx, est quil sappuie sur de nombreux composants cooprant. Ce point a t exacerb par les messages non sollicits car les administrateurs de messagerie ont d svrement verrouiller les serveurs, ce qui nest pas sans effet sur les scripts. Nous pouvons uniquement vous conseiller de soigneusement tester votre solution et de contacter vos administrateurs systme et de messagerie en cas de besoin. Vous risquez galement de rencontrer des problmes avec certaines distributions Linux orientes stations de travail, comme Ubuntu, car elles ninstallent ou nexcutent aucun agent de transfert de courrier. En effet, elles supposent que vous utiliserez un client graphique complet, comme Evolution ou Thunderbird. Dans ce cas, les clients de messagerie en ligne de commande et lenvoi de courrier partir de cron ne fonctionneront pas. Consultez le support en ligne de votre distribution pour trouver de laide.

Voir aussi
man mail ; man mailx ;

[05/03/08]

Elodie FRITSCH <elodie.fritsch@total.com>

bash Le livre de recettes

15.16. Automatiser un processus plusieurs phases


man mailto ; man mutt ; man uuencode ; man cron ; man 5 crontab.

363

15.16. Automatiser un processus plusieurs phases


Problme
Vous souhaitez automatiser une tche ou un processus long, mais il peut ncessiter une intervention manuelle et vous devez donc tre en mesure de le redmarrer partir de diffrents points. Vous avez besoin dune instruction de type GOTO, mais elle existe pas dans bash.

Solution
Utilisez une instruction case pour dcomposer votre script en sections ou phases. Tout dabord, nous dfinissons une solution standard pour obtenir des rponses de lutilisateur :
# bash Le livre de recettes : fonc_choisir function choisir { # Laisser l'utilisateur faire un choix et retourner une rponse # normalise. Le traitement de la rponse par dfaut et de la # suite du processus est du ressort d'une construction if/then # aprs le choix dans le code principal. local reponse printf "%b" "\a" # Faire retentir la sonnerie. read -p "$*" reponse case "$reponse" in [oO1] ) choix='o';; [nN0] ) choix='n';; * ) choix="$reponse";; esac } # Fin de la fonction choisir.

Ensuite, nous dfinissons les diffrentes phases :


# bash Le livre de recettes : utiliser_phases # Boucle principale. until [ "$phase" = "Fini." ]; do

[05/03