Guide avancé d'écriture des scripts Bash


Scripting 

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

Mendel Cooper

Brindle-Phlogiston Associates
1.7

05 Janvier 2003

Historique des versions
Version 0.1 14 Juin 2000 Revu par : mc
Première sortie.
Version 0.2 30 Octobre 2000 Revu par : mc
Bugs corrigés, plus quelques informations supplémentaires et quelques exemples de scripts.
Version 0.3 12 Février 2001 Revu par : mc
Une autremise à jour majeure.
Version 0.4 08 Juillet 2001 Revu par : mc
Encore des corrections de bugs, bien plus d'informations, plus de scripts - une révision complète et un agrandissement du livre.
Version 0.5 03 Septembre 2001 Revu par : mc
Mise à jour majeure. Corrections de bugs, ajout d'informations, chapitres et sections réorganisés.
Version 1.0 14 Octobre 2001 Revu par : mc
Corrections de bugs, réorganisation, ajout d'informations. Version stable.
Version 1.1 06 Janvier 2002 Revu par : mc
Corrections des bugs, ajout d'informations et de scripts.
Version 1.2 31 Mars 2002 Revu par : mc
Corrections des bugs, ajout d'informations et de scripts.
Version 1.3 02 Juin 2002 Revu par : mc
Version 'TANGERINE': Quelques corrections, bien plus de matériel, et ajout de scripts.
Version 1.4 16 Juin 2002 Revu par : mc
Version 'MANGO': Quelques erreurs de frappe clavier corrigés, plus d'informations et de scripts.
Version 1.5 13 Juillet 2002 Revu par : mc
Version 'PAPAYA': Quelques corrections de bugs, bien plus d'ajout d'informations et de scripts.
Version 1.6 29 Septembre 2002 Revu par : mc
Version 'POMEGRANATE': Quelques corrections de bugs, plus d'informations, un script supplémentaire.
Version 1.7 05 Janvier 2003 Revu par : mc
Version 'COCONUT': Quelques corrections de bugs, plus d'informations, un script de plus.

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

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


Dédicace

Pour Anita, la source de toute magie

Table des matières
Part 1. Introduction
1. Pourquoi la programmation Shell?
2. Lancement avec un << #! >>
Part 2. Bases
3. Caractères spéciaux
4. Introduction aux variables et aux paramètres
5. Guillemets
6. Sortie et code de sortie (ou d'état)
7. Tests
8. Opérations et thèmes relatifs
Part 3. Après les bases
9. Les variables revisitées
10. Boucles et branchements
11. Commandes internes et intégrées
12. Filtres externes, programmes et commandes
13. Commandes système et d'administration
14. Substitution de commandes
15. Expansion arithmétique
16. Redirection d'E/S (entrées/sorties)
17. Documents en ligne
18. Récréation
Part 4. Thèmes avancés
19. Expressions rationnelles
20. Sous-shells
21. Shells restreints
22. Substitution de processus
23. Fonctions
24. Alias
25. Constructeurs de listes
26. Tableaux
27. Fichiers
28. /dev et /proc
29. Des Zéros et des Nulls
30. Déboguage
31. Options
32. Trucs et astuces
33. Ecrire des scripts avec style
34. Divers
35. Bash, version 2
36. Notes finales
36.1. Note de l'auteur
36.2. A propos de l'auteur
36.3. Outils utilisés pour produire ce livre
36.4. Crédits
Bibliographie
A. Scripts contribués
B. Petit guide sur Sed et Awk
B.1. Sed
B.2. Awk
C. Codes de sortie ayant une signification particulière
D. Une introduction détaillée sur les redirections d'entrées/sorties
E. Localisation
F. Commandes d'historique
G. Un exemple de fichier .bashrc
H. Convertir des fichiers Batch DOS en Scripts Shell
I. Exercices
I.1. Analyse de scripts
I.2. Ecriture de scripts
J. Droits d'utilisation
K. Copyright
Liste des tableaux
11-1. Identifiants de jobs
31-1. Options de bash
34-1. Nombres représentant les couleurs des séquences d'échappement
B-1. Opérateurs sed basiques
B-2. Exemples
C-1. Codes de sortie << réservés >>
H-1. Mots clés / variables / opérateurs des fichiers batch, et leur équivalent shell
H-2. Commandes DOS et leur équivalent UNIX
Liste des exemples
2-1. cleanup: Un script pour nettoyer les fichiers de trace dans /var/log
2-2. cleanup: Une version améliorée et généralisée du script précédent.
3-1. Blocs de code et redirection d'E/S
3-2. Sauver le résultat d'un bloc de code dans un fichier
3-3. Lancer une boucle en tâche de fond
3-4. Sauvegarde de tous les fichiers modifiés dans les dernières 24 heures
4-1. Affectation de variable et substitution
4-2. Affectation de variable : de base
4-3. Affectation de variable , de base et élaboré
4-4. Entier ou chaîne?
4-5. Paramètres positionnels
4-6. wh, recherche d'un nom de domaine avec whois
4-7. Utiliser shift
5-1. Afficher des variables bizarres
5-2. Caractères d'échappement
6-1. exit / code de sortie
6-2. Inverser une condition en utilisant !
7-1. Où est le vrai?
7-2. Equivalences de test, /usr/bin/test, [ ], et /usr/bin/[
7-3. Tests arithmétiques en utilisant (( ))
7-4. Test de liens cassés
7-5. comparaisons arithmétiques et de chaînes de caractères
7-6. vérification si une chaîne nulle
7-7. zmost
8-1. Plus grand diviseur commun
8-2. Utiliser des opérations arithmétiques
8-3. Tests de condition composés en utilisant && et ||
8-4. Représentation des constantes numériques
9-1. $IFS et espaces blancs
9-2. Saisie avec délai
9-3. Encore une fois, saisie avec délai
9-4. read avec délai
9-5. Suis-je root?
9-6. arglist: Affichage des arguments avec $* et $@
9-7. Comportement de $* et $@ inconsistent
9-8. $* et $@ lorsque $IFS est vide
9-9. variable tiret bas
9-10. Insérer une ligne blanche entre les paragraphes d'un fichier texte
9-11. Convertir des formats de fichiers graphiques avec une modification du nom du fichier
9-12. Autres moyens d'extraire des sous-chaînes
9-13. Utiliser la substitution et les messages d'erreur
9-14. Substitution de paramètres et messages d'<< usage >>
9-15. Longueur d'une variable
9-16. Correspondance de modèle dans la substitution de paramètres
9-17. Renommer des extensions de fichiers:
9-18. Utiliser la recherche de modèles pour analyser des chaînes de caractères diverses
9-19. Modèles correspondant au préfixe ou au suffixe d'une chaîne de caractères
9-20. Utiliser declare pour typer des variables
9-21. Références indirectes
9-22. Passer une référence indirecte à awk
9-23. Générer des nombres aléatoires
9-24. Piocher une carte au hasard dans un tas
9-25. Laisser tourner RANDOM plusieurs fois
9-26. Réinitialiser RANDOM
9-27. Nombres pseudo aléatoires, en utilisant awk
9-28. Manipulation, ç la façon du C, de variables
10-1. Des boucles for simples
10-2. Boucle for avec deux paramètres dans chaque élément de la [liste]
10-3. Fileinfo: opérer sur une liste de fichiers contenue dans une variable
10-4. Agir sur des fichiers à l'aide d'une boucle for
10-5. in [liste] manquant dans une boucle for
10-6. Générer la [liste] dans une boucle for avec la substitution de commandes
10-7. Un remplaçant de grep pour les fichiers binaires
10-8. Afficher tous les utilisateurs du système
10-9. Rechercher les auteurs de tous les binaires d'un répertoire
10-10. Afficher les liens symboliques dans un répertoire
10-11. Liens symboliques dans un répertoire, sauvés dans un fichier
10-12. Une boucle for à la C
10-13. Utiliser efax en mode batch
10-14. Simple boucle while
10-15. Une autre boucle while
10-16. Boucle while avec de multiples conditions
10-17. Syntaxe à la C pour une boucle while
10-18. Boucle until
10-19. Boucles imbriquées
10-20. Effets de break et continue dans une boucle
10-21. Sortir de plusieurs niveaux de boucle
10-22. Continuer à un plus haut niveau de boucle
10-23. Utiliser << continue N >> dans une tâche courante
10-24. Utiliser case
10-25. Créer des menus en utilisant case
10-26. Utiliser la substitution de commandes pour générer la variable case
10-27. Simple correspondance de chaîne
10-28. Vérification d'une entrée alphabétique
10-29. Créer des menus en utilisant select
10-30. Créer des menus en utilisant select dans une fonction
11-1. printf en action
11-2. Affectation d'une variable, en utilisant read
11-3. Qu'arrive-t'il quand read n'a pas de variable
11-4. Entrée à plusieurs lignes par read
11-5. Détecter les flèches de direction
11-6. Utiliser read avec la redirection de fichier
11-7. Modifier le répertoire courant
11-8. Laisser let faire un peu d'arithmétique.
11-9. Montrer l'effet d'eval
11-10. Forcer une déconnexion
11-11. Une version de << rot13 >>
11-12. Utiliser eval pour forcer une substitution de variable dans un script Perl
11-13. Utiliser set avec les paramètres de position
11-14. Réaffecter les paramètres de position
11-15. << Déconfigurer >> une variable
11-16. Utiliser export pour passer une variable à un script awk embarqué
11-17. Utiliser getopts pour lire les options/arguments passés à un script
11-18. << Inclure >> un fichier de données
11-19. Un script (inutile) qui se charge lui-même
11-20. Effets d'exec
11-21. Un script lançant exec sur lui-même
11-22. Attendre la fin d'un processus avant de continuer
11-23. Un script qui se tue lui-même
12-1. Utilisation de ls pour créer une liste de fichiers à graver sur un CDR
12-2. incorrectname élimine dans le répertoire courant les fichiers dont le nom contient des caractères incorrects et des espaces blancs.
12-3. Effacer un fichier par son numéro d'inode
12-4. Fichier de traces utilisant xargs pour surveiller les logs système
12-5. copyrep, copier les fichiers du répertoire courant vers un autre en utilisant xargs
12-6. Utiliser expr
12-7. Utiliser date
12-8. Analyse de frequence d'apparition de mot
12-9. Quels fichiers sont des scripts ?
12-10. Générer des nombres aléatoires de 10 chiffres
12-11. Utiliser tail pour surveiller le fichier des traces système
12-12. Emuler << grep >> dans un script
12-13. Chercher les mots dans une liste pour tester leur validité
12-14. toupper: Transforme un fichier en majuscule.
12-15. lowercase: Change tous les noms de fichier du répertoire courant en minuscule.
12-16. du: Converti les fichiers text DOS vers UNIX.
12-17. rot13: rot13, cryptage ultra-faible.
12-18. Générer des puzzles << Crypto-Citations >>
12-19. Affichage d'un fichier formatté.
12-20. utiliser column pour formatter l'affichage des répertoires
12-21. nl: Un script d'autonumérotation.
12-22. Utiliser cpio pour déplacer un répertoire complet
12-23. Déballer une archive rpm
12-24. supprimer les commentaires de programmes C
12-25. Explorer /usr/X11R6/bin
12-26. Une commande strings << améliorée >>
12-27. Utiliser cmp pour comparer deux fichiers à l'intérieur d'un script.
12-28. basename et dirname
12-29. Vérifier l'intégrité d'un fichier
12-30. Décoder des fichier codés avec uudecode
12-31. Un script qui s'envoie un courrier
12-32. Paiement mensuel sur une hypothèque
12-33. Conversion de base
12-34. Une autre façon d'invoquer bc
12-35. Calculer PI
12-36. Convertir une valeur décimale en hexadecimal
12-37. Factoring
12-38. Calculer l'hypoténuse d'un triangle
12-39. Utiliser seq pour générer l'incrément d'une boucle
12-40. Utiliser getopt pour analyser les paramètres de la ligne de commande
12-41. Capturer une saisie
12-42. Effacer les fichiers de façon sure
12-43. Générateur de nom de fichier
12-44. Convertir des mètres en miles
12-45. Utiliser m4
13-1. configurer un caractère d'effacement
13-2. mot de passe secret: Désactiver l'écho du terminal
13-3. Détection de l'appui sur une touche
13-4. pidof aide à la suppression d'un processus
13-5. Vérifier une image
13-6. Création d'un système de fichiers dans un fichier
13-7. Ajoute un nouveau disque dur
13-8. killall, à partir de /etc/rc.d/init.d
14-1. Trucs de script stupides
14-2. Générer le contenu d'une variable à partir d'une boucle
16-1. Rediriger stdin en utilisant exec
16-2. Rediriger stdout en utilisant exec
16-3. Rediriger à la fois stdin et stdout dans le même script avec exec
16-4. Boucle while redirigée
16-5. Autre forme de boucle while redirigée
16-6. Boucle until redirigée
16-7. Boucle for redirigée
16-8. Rediriger la boucle for (à la fois stdin et stdout)
16-9. Redirigé un test if/then
16-10. Fichier de données << nom.données >> pour les exemples ci-dessus
16-11. Enregistrer des événements
17-1. fichierstupide: Crée un fichier stupide de deux lignes
17-2. broadcast: Envoie un message à chaque personne connectée
17-3. Message multi-lignes en utilisant cat
17-4. Message multi-lignes, aves les tabulations supprimées
17-5. Document en ligne avec une substitution de paramètre
17-6. Substitution de paramètres désactivée
17-7. upload: Envoie un fichier vers le répertoire incoming chez << Sunsite >>
17-8. Documents en ligne et fonctions
17-9. Document en ligne << Anonyme >>
17-10. Décommenter un bloc de code
17-11. Un script auto-documenté
20-1. Etendue des variables dans un sous-shell
20-2. Lister les profiles utilisateurs
20-3. Exécuter des processus en parallèle dans les sous-shells
21-1. Exécuter un script en mode restreint
23-1. Simple fonction
23-2. Fonction prenant des paramètres
23-3. Maximum de deux nombres
23-4. Convertire des nombres en chiffres romains
23-5. Tester les valeurs de retour importantes dans une fonction
23-6. Comparer deux grands entiers
23-7. Vrai nom pour un utilisateur
23-8. Visibilité de la variable locale
23-9. Récursion, en utilisant une variable locale
24-1. Alias à l'intérieur d'un script
24-2. unalias: Configurer et supprimer un alias
25-1. Utiliser une << liste and >> pour tester des arguments de la ligne de commande
25-2. Un autre test des arguments de la ligne de commande en utilisant une << liste and >>
25-3. Utiliser des << listes or >> en combinaison avec une << liste and >>
26-1. Utilisation d'un tableau simple
26-2. Formattage d'un poème
26-3. Quelques propriétés spéciales des tableaux
26-4. Des tableaux vides et des éléments vides
26-5. Un viel ami: Le tri Bubble Sort
26-6. Application complexe des tableaux  : Crible d' Eratosthene
26-7. Emuler une pile
26-8. Application complexe des tableaux Exploration d'une étrange série mathématique
26-9. Simuler un tableau à deux dimensions, puis son test
28-1. Trouver le processus associé à un PID
28-2. Etat de la connexion
29-1. Cacher le cookie jar
29-2. Créer un fichier de swap en utilisant /dev/zero
29-3. Créer un disque ram
30-1. Un script bugué
30-2. Mot clé manquant
30-3. test24, un autre script bogué
30-4. Tester une condition avec un << assert >>
30-5. Récupérer la sortie
30-6. Nettoyage après un Control-C
30-7. Tracer une variable
32-1. Problèmes des sous-shell
32-2. Envoyer dans un tube la sortie de echo pour un read
34-1. script d'invocation
34-2. Un script d'invocation légèrement plus complexe
34-3. Un script d'invocation autour d'un script awk
34-4. Perl inclus dans un script Bash
34-5. Combinaison de scripts Bash et Perl
34-6. Un script (inutile) qui s'appelle récursivement
34-7. Un script (utile) qui s'appelle récursivement
34-8. Une base de données d'adresses << colorisée >>
34-9. Afficher du texte coloré
34-10. Astuce de valeur de retour
34-11. Une astuce permettant de renvoyer plus d'une valeur de retour
34-12. Passer et renvoyer un tableau
34-13. Un peu de fun avec des anagrammes
35-1. Expansion de chaîne de caractères
35-2. Références de variables indirectes - la nouvelle façon
35-3. Simple application de base de données, utilisant les références de variables indirectes
35-4. Utiliser des tableaux et autres astuces pour gérer quatre mains aléatoires dans un jeu de cartes
A-1. manview: Visualiser des pages man formatées
A-2. mailformat: Formater un courrier électronique
A-3. rn: Un utilitaire simple pour renommer des fichiers
A-4. blank-rename: Renommer les fichiers dont le nom contient des espaces
A-5. encryptedpw: Charger un fichier sur un site ftp, en utilisant un mot de passe crypté en local
A-6. copy-cd: Copier un CD de données
A-7. collatz: Séries de Collatz
A-8. days-between: Calculer le nombre de jours entre deux dates
A-9. makedict: Créer un << dictionnaire >>
A-10. soundex: Conversion phonétique
A-11. << life: Jeu de la Vie >>
A-12. Fichier de données pour le << Jeu de la Vie >>
A-13. behead: Supprimer les en-têtes des courriers électroniques et des nouvelles
A-14. ftpget: Télécharger des fichiers via ftp
A-15. password: Générer des mots de passe aléatoires de 8 caractères
A-16. fifo: Faire des sauvegardes journalières, en utilisant des tubes nommés
A-17. primes: Générer des nombres aléatoires en utilisant l'opérateur modulo
A-18. tree: Afficher l'arborescence d'un répertoire
A-19. string: Manipuler les chaînes de caractères comme en C
A-20. obj-oriented: Bases de données orientées objet
G-1. Exemple de fichier .bashrc
H-1. VIEWDATA.BAT: Fichier Batch DOS
H-2. viewdata.sh: Conversion du script shell VIEWDATA.BAT
type_correct="ASCII C program text" if [ "$type" != "$type_correct" ] then echo echo "Ce script fonctionne uniquement sur les fichiers C." echo exit $E_MAUVAIS_TYPE_FICHIER fi # Script sed assez complexe: #-------- sed ' /^\/\*/d /.*\/\*/d ' $1 #-------- # Facile à comprendre si vous prenez quelques heures pour apprendre les #+ concepts de sed. # Il est possible d'ajouter une ligne supplémentaire au script sed pour gérer #+ le cas où la ligne de code a un commentaire le suivant, sur la même ligne. # Ceci est laissé en exercice (difficile). # De même, le code ci-dessus supprime les lignes avec un "*/" ou "/*", # ce qui n'est pas un effet désirable. exit 0 # ---------------------------------------------------------------- # Le code ci-dessous ne s'exécutera pas à cause du 'exit 0' ci-dessus. # Stephane Chazelas suggère l'alternative suivante: usage() { echo "Usage: `basename $0` fichier-C" >&2 exit 1 } BIZARRE=`echo -n -e '\377'` # ou BIZARRE=$'\377' [[ $# -eq 1 ]] || usage case `file "$1"` in *"C program text"*) sed -e "s%/\*%${BIZARRE}%g;s%\*/%${BIZARRE}%g" "$1" \ | tr '\377\n' '\n\377' \ | sed -ne 'p;n' \ | tr -d '\n' | tr '\377' '\n';; *) usage;; esac # Ceci ne fonctionne pas avec, par exemple: # printf("/*"); # or # /* /* commentaire intégré bogué */ # # Pour gérer tous les cas spécifiques (commentaires dans des chaînes, # commentaires dans des chaînes où se trouve un \", \\" ...) la seule façon est # d'écrire un analyseur C # (lex ou yacc peut-être?). exit 0
which

which commande-xxx donne le chemin complet vers << commande-xxx >>. C'est utile pour trouver si une commande ou un utilitaire particulier est installé sur le système.

$bash which rm
/usr/bin/rm

whereis

Similair à which, ci-dessus, whereis commande-xxx donne le chemin complet vers << commande-xxx >>, mais aussi sa page man.

$bash whereis rm
rm: /bin/rm /usr/share/man/man1/rm.1.bz2

whatis

whatis fichierxxx recherche << ficheirxxx >> dans la base de données whatis. C'est utile pour identifier les commandes système et les fichiers de configuration importants. Considérez-le en tant que commande man simplifiée.

$bash whatis whatis
whatis               (1)  - search the whatis database for complete words

Exemple 12-25. Explorer /usr/X11R6/bin

#!/bin/bash

# Que sont tous ces mystérieux binaires dans /usr/X11R6/bin?

REPERTOIRE="/usr/X11R6/bin"
# Essayez aussi "/bin", "/usr/bin", "/usr/local/bin", etc.

for fichier in $REPERTOIRE/*
do
  whatis `basename $fichier`   # affiche des informations sur le binaire.
done

exit 0
# Vous pouvez souhaiter rediriger la sortie de ce script, de cette façon:
# ./what.sh >>whatis.db
# ou la visualiser une page à la fois sur stdout,
# ./what.sh | less

Voir aussi Exemple 10-3.

vdir

Affiche une liste détaillée du contenu du répertoire. L'effet est similaire à ls -l.

Il fait partie de GNU fileutils.

bash$ vdir
total 10
 -rw-r--r--    1 bozo  bozo      4034 Jul 18 22:04 data1.xrolo
 -rw-r--r--    1 bozo  bozo      4602 May 25 13:58 data1.xrolo.bak
 -rw-r--r--    1 bozo  bozo       877 Dec 17  2000 employment.xrolo

bash ls -l
total 10
 -rw-r--r--    1 bozo  bozo      4034 Jul 18 22:04 data1.xrolo
 -rw-r--r--    1 bozo  bozo      4602 May 25 13:58 data1.xrolo.bak
 -rw-r--r--    1 bozo  bozo       877 Dec 17  2000 employment.xrolo
	      

locate, slocate

La commande locate cherche les fichiers en utilsant une base de données stockée pour ce seul but. La commande slocate est la version sécurisée de locate (qui pourrait être un alias de slocate).

$bash locate hickson
/usr/lib/xephem/catalogs/hickson.edb

readlink

Déréférence le fichier sur lequel pointe un lien symbolique.

bash$ readlink /usr/bin/awk
../../bin/gawk
	      

strings

Utiliser la commande strings pour trouver les chaînes de caractères affichables dans un fichier binaire ou de données. Elle listera les séquences de caractères affichables trouvées dans le fichier cible. C'est intéressant pour un examen rapide (et sale) d'un core dump ou pour regarder un fichier image inconnu (strings fichier-image | more pourrait afficher quelque chose comme JFIF, qui identifierait le fichier en tant que graphique jpeg). Dans un script, vous devriez probablement analyser la sortie de strings avec grep ou sed. Voir Exemple 10-7 et Exemple 10-9.

Exemple 12-26. Une commande strings << améliorée >>

#!/bin/bash
# wstrings.sh: "word-strings" (commande "strings" améliorée)
#
#  Ce script filtre la sortie de "strings" en la comparant avec une liste de
#+ mots communs.
#  Ceci élimine efficacement tout le bruit et n'affiche que les mots reconnus.

# =================================================================
#               Vérification standard de(s) argument(s) du script
ARGS=1
E_MAUVAISARGS=65
E_AUCUNFICHIER=66

if [ $# -ne $ARGS ]
then
  echo "Usage: `basename $0` nomfichier"
  exit $E_MAUVAISARGS
fi

if [ -f "$1" ]                        # Vérifie si le fichier existe.
then
    nom_fichier=$1
else
    echo "Le fichier \"$1\" n'existe pas."
    exit $E_AUCUNFICHIER
fi
# =================================================================


LONGUEUR_CHAINE_MINIMUM=3                 #  Longueur minimum d'une chaîne.
FICHIER_MOTS=/usr/share/dict/linux.words  #  Dictionnaire.
                                          #  Vous pouvez spécifier un autre
					  #+ fichier de mots, à condition que
					  #+ son format soit d'un mot par ligne.


listemots=`strings "$1" | tr A-Z a-z | tr '[:space:]' Z | \
tr -cs '[:alpha:]' Z | tr -s '\173-\377' Z | tr Z ' '`

# Traduit la sortie de la commande 'strings' avec de multiples passes de 'tr'.
#  "tr A-Z a-z" réalise une conversion en minuscule.
#  "tr '[:space:]'" change les espaces blancs par des Z.
#  "tr -cs '[:alpha:]' Z" change les caractères non alphabetiques en Z.
#+ et ne conserve qu'un seul Z pour les Z successifs.
#  "tr -s '\173-\377' Z" convertit tous les caractères après 'z' en Z
#+ et ne conserve qu'un seul Z pour les Z successifs
#+ ce qui supprime tous les caractères bizarres que la précédente passe aurait
#+ oublié de gérer.
#  Finalement, "tr Z ' '" convertit tous les Z en espaces blancs,
#+ ce qui sera vu comme des mots séparés dans la boucle ci-dessous.

#  Notez que la technique de remplissage de la sortie de 'tr' vers lui-même,
#+ mais avec different arguments et/ou options à chaque passe.


for mot in $listemots    # Important:
                         # $listemots ne doit pas être entre guillemets ici.
                         # "$listemots" ne fonctionne pas.
                         # Pourquoi?
do

  longueur_chaine=${#mot}                     # Longueur de la chaîne.
  if [ "$longueur_chaine" -lt "$LONGUEUR_CHAINE_MINIMUM" ]
  then   # Ne pas tenir compte des petites chaînes.
    continue
  fi

  grep -Fw $mot "$FICHIER_MOTS"       # Correspond seulement aux mots complets.

done  


exit 0

Comparaison

diff, patch

diff: utilitaire de comparaison de fichiers flexible. Il compare les fichiers cibles ligne par ligne, séquentiellement. Dans certaines applications, telles que la comparaison de dictionnaires de mots, il peut être utile pour filtrer les fichiers avec sort et uniq avant de les envoyer via un tube à diff. diff fichier-1 fichier-2 affiche en sortie les lignes qui différent des deux fichiers, avec des symbôles indiquant à quel fichier appartient la ligne en question.

L'option --side-by-side de diff affiche en sortie chaque fichier comparé, ligne pat ligne, dans des colonnes séparées, et avec les lignes ne correspondant pas marquées. Les options -c et -u rendent la sortie de la commande plus facile à interpréter.

Il existe de nombreuses interfaces agréables pour diff, comme spiff, wdiff, xdiff et mgdiff.

Astuce

La commande diff renvoie un état de sortie de 0 si les fichiers comparés sont identiques et 1 si ils ne le sont pas. Cela permet d'utiliser diff dans une construction de test à l'intérieur d'un script shell (voir ci-dessous).

Une utilisation commune de diff est de générer des fichiers de différence à utiliser avec patch. L'option -e permet la génération de tels fichiers, à utiliser avec des scripts ed ou ex.

patch: utilitaire de gestion de versions. Suivant un fichier de différences généré par diff, patch peut mettre à jour une version précédente d'un paquetage en une nouvelle version. Il est bien plus convenable de distribuer un fichier << diff >> sensiblement plus petit que le corps entier du paquetage revu. Les correctifs (<< patches >>) du noyau sont devenus la méthode préférée pour distribuer les fréquentes mises à jour du noyau Linux.

patch -p1 <correctif
# Prend toutes les modifications indiquées dans 'correctif'
# et les applique aux fichiers référencés.
# Ceci met à jour le paquetage en une nouvelle version.

Applicquer un correctif au noyau:

cd /usr/src
gzip -cd patchXX.gz | patch -p0
# Mettre à jour le source du noyau en utilisant 'patch'.
# De la documentation du noyau Linux, "README",
# par un auteur anonyme (Alan Cox?).

Note

La commande diff peut aussi comparer récursivement les répertoires (et les fichiers qui s'y trouvent).

bash$ diff -r ~/notes1 ~/notes2
Only in /home/bozo/notes1: fichier02
 Only in /home/bozo/notes1: fichier03
 Only in /home/bozo/notes2: fichier04
	      

Astuce

Utiliser zdiff pour comparer des fichiers gzipped.

diff3

Une version étendue de diff qui compare trois fichiers en une fois. Cette commande renvoie un état de sortie de 0 si l'exécution est réussie, mais malheureusement, cela ne donne aucune information sur le résultat de la comparaison.

bash$ diff3 fichier-1 fichier-2 fichier-3
====
 1:1c
   Ceci est la ligne 1 de "fichier-1"
 2:1c
   Ceci est la ligne 1 de "fichier-2"
 3:1c
   Ceci est la ligne 1 de "fichier-3"
	      

sdiff

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

cmp

La commande cmp est une version simplifiée de diff, ci-dessus. Alors que diff reporte les différences entre deux fichiers, cmp montre simplement à quel point ils diffèrent.

Note

Comme diff, cmp renvoie un état de sortie de 0 si les fichiers comparés sont identiques et de 1 si ils diffèrent. Ceci permet une utilisation dans une construction de test à l'intérieur d'un script shell.

Exemple 12-27. Utiliser cmp pour comparer deux fichiers à l'intérieur d'un script.

#!/bin/bash

ARGS=2  # Deux arguments attendus par le script.
E_MAUVAISARGS=65
E_ILLISIBLE=66

if [ $# -ne "$ARGS" ]
then
  echo "Usage: `basename $0` fichier1 fichier2"
  exit $E_MAUVAISARGS
fi

if [[ ! -r "$1" || ! -r "$2" ]]
then
  echo "Les deux fichiers à comparer doivent exister et être lisibles."
  exit $E_ILLISIBLE
fi

cmp $1 $2 &> /dev/null  # /dev/null enterre la sortie de la commande "cmp".
#   cmp -s $1 $2  a le même résultat ("-s" option de silence pour "cmp")
#   Merci à Anders Gustavsson pour nous l'avoir indiqué.
#
# Fonctionne aussi avec 'diff', c'est-à-dire   diff $1 $2 &> /dev/null

if [ $? -eq 0 ]         # Teste du code de sortie de la commande "cmp".
then
  echo "Le fichier \"$1\" est identique au fichier \"$2\"."
else  
  echo "Le fichier \"$1\" diffère du fichier \"$2\"."
fi

exit 0

Astuce

Utiliser zcmp sur des fichiers gzip.

comm

Utilitaire de comparaison de fichiers versatile. Les fichiers doivent être triés pour qu'il soit utile.

comm -options premier-fichier second-fichier

comm fichier-1 fichier-2 affiche trois colonnes:

  • colonne 1 = lignes uniques à fichier-1

  • colonne 2 = lignes uniques à fichier-2

  • colonne 3 = lignes communes aux deux.

Les options permettent la sortie d'une ou plusieurs colonnes.

  • -1 supprime la colonne 1

  • -2 supprime la colonne 2

  • -3 supprime la colonne 3

  • -12 supprime les deux colonnes 1 et 2, etc.

Utilitaires

basename

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

dirname

Supprime le basename d'un nom de fichier, en affichant que le chemin.

Note

basename et dirname peuvent s'exécuter sur des chaînes de caractères arbitraires. L'argument n'a pas besoin de référer un fichier existant, voire même un fichier (voir Exemple A-8).

Exemple 12-28. basename et dirname

#!/bin/bash

a=/home/bozo/daily-journal.txt

echo "Nom de base       de /home/bozo/daily-journal.txt = `basename $a`"
echo "Nom du répertoire de /home/bozo/daily-journal.txt = `dirname $a`"
echo
echo "Mon répertoire personnel est `basename ~/`."   # Fonctionne aussi avec ~.
echo "Le chemin de mon répertoire personnel est `dirname ~/`."  # Fonctionne aussi avec ~.

exit 0
split

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

sum, cksum, md5sum

Ces utilitaires ont pour but de vérifier une somme de contrôle. Une somme de dontrôle est un nombre calculé à partir du contenu d'un fichier, dans le but de vérifier son intégrité. Un script peut se référer à une liste de sommes de contrôle pour des raisons de sécurité, comme pour s'assurer que des fichiers clés du système n'ont pas été modifié ou corrompu. Pour les applications de sécurité, utilisez la commande md5sum en 128 bits (message digest checksum).

bash$ cksum /boot/vmlinuz
1670054224 804083 /boot/vmlinuz


bash$ md5sum /boot/vmlinuz
0f43eccea8f09e0a0b2b5cf1dcf333ba  /boot/vmlinuz
	      

Notez que cksum affiche aussi la taille, en octet, du fichier cible.

Exemple 12-29. Vérifier l'intégrité d'un fichier

#!/bin/bash
# file-integrity.sh: Vérifie si les fichiers d'un répertoire donné ont été
#                    modifié.

E_REP_INEXISTANT=70
E_MAUVAIS_FICHIER_BD=71

fichierdb=File_record.md5
# Fichier pour stocker les enregistrements.


init_base_donnees ()
{
  echo ""$repertoire"" > "$fichierdb"
  # Ecrit le nom du répertoire sur la première ligne du fichier.
  md5sum "$repertoire"/* >> "$fichierdb"
  # Ajoute les sommes de contrôle md5 et les noms de fichiers.
}

verifie_base_donnees ()
{
  local n=0
  local nomfichier
  local somme_controle

  # ------------------------------------------------- #
  #  Cette vérification du fichier ne devrait être
  #+ inutile mais c'est mieux d'être sain que désolé.

  if [ ! -r "$fichierdb" ]
  then
	  echo "Incapable de lire les somme de contrôle du fichier de base de données!"
    exit $E_MAUVAIS_FICHIER_BD
  fi
  # ------------------------------------------------- #

  while read enregistrement[n]
  do

    repertoire_verifie="${enregistrement[0]}"
    if [ "$repertoire_verifie" != "$repertoire" ]
    then
      echo "Les répertoires ne correspondent pas!"
      # Essaie d'utiliser un fichier d'un autre répertoire.
      exit $E_REP_INEXISTANT
    fi

    if [ "$n" -gt 0 ]   # Pas de nom de répertoire.
    then
      nomfichier[n]=$( echo ${enregistrement[$n]} | awk '{ print $2 }' )
      #  md5sum écrit les enregistrements après,
      #+ effectue un contrôle des sommes, puis du fichier.
      somme_controle[n]=$( md5sum "${nomfichier[n]}" )

      if [ "${enregistrement[n]}" = "${somme_controle[n]}" ]
      then
        echo "${nomfichier[n]} non modifié."
      else
        echo "${nomfichier[n]} : ERREUR DE SOMME DE CONTROLE!"
	# Le fichier a été changé depuis la dernière vérification.
      fi

    fi  


    let "n+=1"
  done <"$fichierdb"       #  Lit les sommes de contrôle à partir du fichier de
                           #+ base de données.

}  

# =================================================== #
# main ()

if [ -z  "$1" ]
then
  repertoire="$PWD"     #  Si non spécifi,;
else                    #+ utilise le répertoire courant.
  repertoire="$1"
fi  

clear                   # Efface l'écran.

# ------------------------------------------------------------------ #
if [ ! -r "$fichierdb" ] # Besoin de créer un fichier de base de données.
  then
	  echo "Configuration de la base de données, \""$repertoire"/"$fichierdb"\"."; echo
    init_base_donnees
  fi  
# ------------------------------------------------------------------ #

verifie_base_donnees          # Fait le vrai travail.

echo 

#  Vous pouvez souhaiter rediriger stdout vers un fichier spécialement si le
#+ répertoire vérifié a de nombreux fichiers.

#  Pour une explication sur la vérificaton d'intégrité.
#+ considérez le paquetage #+ http://sourceforge.net/projects/tripwire/.

exit 0
shred

Efface de façon sécurisé un fichier en l'écrasant (en écrivant dessus) plusieurs fois avec des octets aléatoires avant de le supprimer. Cette commande a le même effet que Exemple 12-42, mais le fait de façon plus élégante et plus approfondie.

Il fait partie des utilitaires GNU fileutils.

Attention

Des technologies avancées peuvent toujours retrouvées le contenu d'un fichier, même après l'utilisation de shred.

Coder et crypter

uuencode

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

uudecode

Ceci inverse le codage, décode des fichiers passés par uuencode et récupère les binaires originaux.

Exemple 12-30. Décoder des fichier codés avec uudecode

#!/bin/bash

lignes=35        # Permet 35 lignes pour l'entête (très généreux generous).

for Fichier in *   # Teste tous les fichiers du répertoire courant...
do
  recherche1=`head -$lignes $Fichier | grep begin | wc -w`
  recherche2=`tail -$lignes $Fichier | grep end | wc -w`
  #  Les fichiers uuencodés ont un "begin" près du début et un "end" près de
  #+ la fin.
  if [ "$recherche1" -gt 0 ]
  then
    if [ "$recherche2" -gt 0 ]
    then
      echo "uudecoding - $Fichier -"
      uudecode $Fichier
    fi  
  fi
done  

#  Notez que lancer ce script sur lui-même le trompe et croie qu'il est un
#+ fichier uuencodé, parce qu'il contient les mots "begin" et "end".

# Exercice:
# Modifier ce script pour vérifier un entête de newsgroup.

exit 0

Astuce

La commande fold -s est utile (parfois dans un tube) pour décoder de long messages téléchargés à partir des groupes de nouvelles Usenet.

mimencode, mmencode

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

crypt

A un moment, il était l'utilitaire de cryptage standard sous UNIX. [2] Des régulations gouvernementales, basées sur la politique, ont interdit l'export de logiciels de cryptage, ce qui a résulté en la disparition de la commande crypt de la majeure partie du monde UNIX, et il est toujours manquant sur la plupart des distributions Linux. Heureusement, des programmeurs ont réalisé un certain nombre d'alternatives, dont celle de l'auteur cruft (voir Exemple A-5).

Divers

mktemp

Crée un fichier temporaire avec un nom << unique >>.

PREFIX=nom_fichier
fichier_temporaire=`mktemp $PREFIX.XXXXXX`
#                          ^^^^^^ A besoin d'au moins 6 emplacements
#+                                dans le modèle de nom de fichier.
echo "nom de fichier_temporaire = $fichier_temporaire"
# nom fichier_temporaire = nom_fichier.QA2ZpY
#                 ou quelque chose de similaire...

make

Utilitaire pour construire et compiler des paquetages binaires. Il peut aussi être utilisé pour tout type d'opérations qui seraient déclenchées par une modification des fichiers source.

La commande make vérifie le Makefile, une liste de dépendances de fichiers et les opérations à réaliser.

install

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

ptx

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

more, less

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

Notes

[1]

Un tar czvf archive_name.tar.gz * incluera les fichiers commençant par un point pour les répertoires compris dans le répertoire courant. C'est une << fonctionnalité >> non documentée de GNU tar.

[2]

C'est un système de chiffrement symétrique de bloc, employé pour crypter des fichiers sur un seul système ou sur un réseau local, par opposition à la classe de chiffrement publique, dont pgp est un exemple bien connu.