Programmation shell sous GNU/Linux

Bash Shell

Qu'est ce que le shell sous GNU/Linux ?

Le shell est à Linux ce que MS-DOS est à Windows.

En effet, sur un système GNU/Linux, il est très important de connaitre les bases du shell.
Même si les interfaces graphique sont là pour simplifier les opérations, il est parfois nécessaire, voir indispensable, de savoir utiliser des commandes GNU/Linux.

Parfois, il n'y a pas toujours de programmes avec de belles interfaces graphiques pour faire ce que nous souhaitons.
Parfois, il est plus simple et plus rapide de faire telle ou telle action via les lignes de commandes.

Rechercher des fichiers, créer des répertoires, rechercher du texte dans plusieurs fichiers, renommer en masse tout un ensemble de fichier selon certains critères, se déplacer au sein de l'arborescence GNU/Linux etc etc ...

Et enfin, le plus important, écrire ses propres scripts afin d'automatiser certaines actions répétitives.

Extraits du livre "Programmation Shell sous Unix / Linux" aux éditions ENI que je conseil très fortement de se procurer.

Programmation shell sous Linux

Les mécanismes du shell

Commandes internes et externes

Les commandes externes

Une commande externe est un fichier présent dans l'arborescence.

Quand un utilisateur exécute la commande ls, le shell demande au noyau Linux d'exécuter le fichier /bin/ls

Sont considérés comme commandes externes, tous les fichiers au format binaire exécutable ainsi que tous les fichiers au format texte représentant un script de commandes.

La commande file indique le type de données contenues dans un fichier.

$ file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, stripped

$ file monscript
monscript: POSIX shell script text executable

L'argument de la commande file est un nom de fichier indiqué en relatif ou en absolu

Etiquettes: 

Les commandes internes

Une commande interne est intégrée au processus shell.

Elle n'a aucune correspondance avec un fichier sur le disque.

La commande type indique si une commande est interne ou externe.

$ type cd
cd est une primitive du shell

$ type ls
ls est un alias vers « ls --color=auto »

La commande type prend en argument le nom d'une commande. Si cette dernière n'est pas une commande interne, elle est recherchée dans les répertoires indiqués dans la variable PATH

Etiquettes: 

Les commandes internes et externes

Certaines commandes ont une implémentation interne et externe.

  • La commande interne est exécutée en priorité.
  • L'exécution d'une commande interne est plus rapide.
  • La commande type, indique que la commande est interne mais ne précise pas qu'elle peut également être une commande externe.

La commande pwd est une commande interne :

$ type pwd
pwd est une primitive du shell

La commande pwd possède également une implémentation externe :

$ ls -l /bin/pwd
-rwxr-xr-x 1 root root 34376 2010-09-21 20:33 /bin/pwd

Pour forcer l'utilisation de la commande externe, il faut indiquer l'emplacement de la commande :

$ /bin/pwd
/home
$ cd /bin
$ ./pwd
/bin

Affichage à l'écran

La commande interne echo permet d'afficher des données à l'écran.

$ echo Ceci est un exemple
Ceci est un exemple

Il faut échapper l'apostrophe avec l'antislash

$ echo Un exemple avec l\'apostrophe
Un exemple avec l'apostrophe

Il faut échapper les guillemets avec l'antislash

$ echo Un exemple avec des \"guillemets\"
Un exemple avec des "guillemets"

En bash, il faut obligatoirement utilisé l'option "-e" pour utiliser les caractères spéciaux suivants.

  • Le caractère "\n" sert à provoquer un saut de ligne :

$ echo -e "Un exemple\navec un saut de ligne"
Un exemple
avec un saut de ligne

  • Le caractère "\c" supprime le saut de ligne naturel de la commande echo :

Sans le caractère "\c"

$ echo -e "Une ligne" ; echo -e "Une autre ligne"
Une ligne
Une autre ligne

Avec le caractère "\c"

$ echo -e "Une ligne\c" ; echo -e "Une autre ligne"
Une ligneUne autre ligne

L'option -n remplace le caractère "\c"

$ echo -n "Une ligne" ; echo "Une autre ligne"
Une ligneUne autre ligne

  • Le caractère "\t" sert à afficher une tabulation :

$ echo -e "Voici 1 tabulation\tet la suite"
Voici 1 tabulation      et la suite

  • Le caractère "\\" permet d'afficher un antislash :

$ echo -e "Afficher un antislash\\"
Afficher un antislash\

Liste des caractères spéciaux :

\0NNN Valeur d'un caractère exprimé en octal
\\ Antislash
\a Bip
\b Effacement du caractère précédent
\c Suppression du saut de ligne en fin de ligne
\f Saut de page
\n Saut de ligne
\r Retour chariot
\t Tabulation horizontale
\v Tabulation verticale

 

Le caractère ~ (tilde)

Le caractère "~" représente le répertoire HOME de l'utilisateur.

Par exemple, pour l'utilisateur "toto"

$ cd ~
$ pwd
/home/toto

Copier le fichier "fichier1" du répertoire /tmp dans le répertoire /home/toto

$ cd /tmp
$ cp fichier1 ~

Copier le fichier "fichier2" du répertoire /tmp dans le répertoire /home/toto/mesfichiers

$ cd /tmp
$ cp fichier2 ~/mesfichiers

Si le caractère "~" est immédiatement suivi d'un mot, ce dernier est considéré comme un nom d'utilisateur.

Copier le fichier "fichier3" du répertoire /home/toto vers le répertoire /home/titi

$ cd ~
$ pwd
/home/toto
$ cp fichier3 ~titi

Copier le fichier "fichier4" du répertoire /home/toto vers le répertoire /home/titi/tmp

$ cd ~
$ pwd
/home/toto
$ cp fichier4 ~titi/tmp

 

La commande interne cd

La commande cd, sans arguments, permet à l'utilisateur de se placer dans son répertoire HOME.

$ pwd
/tmp
$ cd
$ pwd
/home/toto

Identique en utilisant le caractère "~"

$ cd ~

Pour se rendre dans le répertoire HOME de tata :

$ pwd
/home/toto
$ cd ~tata
$ pwd
/home/tata

Pour revenir au répertoire précédent avec la commande "cd -" :

$ cd -
$ pwd
/home/toto

Remplacement de noms de fichiers

Expressions simples

Attention, certaines de ces expressions ne fonctionnent pas toutes avec les mêmes shell.
Pour certaines, elles sont compatibles BASH, pour d'autres ZSH et certaines ne fonctionnent qu'avec KSH.

Le caractère "*" permet de remplacer n'importe quel caractère.

$ ls
fichier1 fichier2.a monfichier tOnfichier.b

  • Afficher uniquement les fichiers se terminant par ".a" :

$ ls *.a
fichier2.a

  • Afficher uniquement les fichiers commançant par "f" :

$ ls f*
fichier1 fichier2.a

Le caractère "?" représente un caractère quelconque.

  • Afficher uniquement les fichiers ayant une extension composée d'un seul caractère :

$ ls *.?
fichier2.a tOnfichier.b

  • Afficher uniquement les fichiers composés de 8 caractères :

$ ls ????????
fichier1

Les caractères "[ ]" permettent d'indiquer la liste des caractères que l'on recherche à une position bien précise dans le noms des fichiers.

  • Afficher les fichiers commençant par la lettre "f" ou "t" et se terminant
    par le "." suivi d'une lettre minuscule :

$ ls [ft]*.[a-z]
fichier2.a tOnfichier.b

  • Afficher tous les fichiers ayant en deuxième caractère une lettre majuscule, ou un chiffre,
    ou la lettre "e" :

% ls ?[A-Z0-9e]*
cOucou  f1chier  F2chier  Hello

  • Afficher tous les fichiers ne commançant pas par une lettre minuscule :

$ ls [!a-z]*
1coucou  Coucou  F2chier  Fichier  Hello

  • Supprimer tous les fichiers se terminant par .b ou .c :

% rm -i *.b *.c
rm : supprimer fichier vide «fichier1.b» ? y
rm : supprimer fichier vide «fichier1.c» ? y

 

Expressions complexes

Les expressions complexes sont compatible BASH à condition d'activer l'option extglob avec la commande shopt (shopt -s extglob).

$ ls
1coucou   coucou.t    fichier159159159.log  fichier161.log  fichier.log
cOucou    coucou.uvw  fichier159159.log     fichier1.a      Hello.txt
Coucou    f1chier     fichier159160.log     fichier1.abc
coucou.r  F2chier     fichier159.log        fichier1.b
coucou.s  Fichier     fichier160.log        fichier1.c

  • ?(expression)

L'expression sera présente 0 ou 1 fois.

Afficher tous les fichiers dont le nom contient 0 ou 1 fois "159"

$ ls fichier?(159).log
fichier159.log  fichier.log

  • *(expression)

L'expression sera présente entre 0 et x fois.

Afficher tous les fichiers dont le nom contient 0 ou x fois "159"

$ ls fichier*(159).log
fichier159159159.log  fichier159159.log  fichier159.log  fichier.log

  • +(expression)

L'expression sera présente entre 1 et x fois.

Afficher tous les fichiers dont le nom contient 1 ou x fois "159"

$ ls fichier+(159).log
fichier159159159.log  fichier159159.log  fichier159.log

  • @(expression)

L'expression sera présente exactement 1 fois.

Afficher tous les fichiers dont le nom contient 1 fois "159"

$ ls fichier@(159).log
fichier159.log

  • !(expression)

L'expression ne sera pas présente.

Afficher tous les fichiers dont le nom ne contient pas 1 fois "159"

$ ls fichier!(159).log
fichier159159159.log  fichier159160.log  fichier161.log
fichier159159.log     fichier160.log     fichier.log

Afficher tous les fichiers dont le nom ne contient pas "fichier"

$ ls !(fichier*)
1coucou  Coucou    coucou.s  coucou.uvw  F2chier  Hello.txt
cOucou   coucou.r  coucou.t  f1chier     Fichier

Une barre verticale dans une expression correspond à "ou bien"

Afficher tous les fichiers dont le nom contient 1 fois "159" ou "160"

$ ls fichier@(159|160).log
fichier159.log  fichier160.log

Afficher tous les fichiers dont le nom contient 1 ou x fois "159" ou "161"

$ ls fichier+(159|161).log
fichier159159159.log  fichier159159.log  fichier159.log  fichier161.log

Afficher tous les fichiers dont le nom contient 1 fois ( 1 ou x fois "159" ou "161")

$ ls fichier@(+(159)|+(161)).log
fichier159159159.log  fichier159159.log  fichier159.log  fichier161.log

Séparateur de commandes

Le caractère ";" permet d'écrire plusieurs commandes sur une même ligne. Toutes les commandes sont exécutées dans l'ordre.

$ pwd
/home/toto
$ ls
1coucou   coucou.t    fichier159159159.log  fichier161.log  fichier.log
cOucou    coucou.uvw  fichier159159.log     fichier1.a      Hello.txt
Coucou    f1chier     fichier159160.log     fichier1.abc
coucou.r  F2chier     fichier159.log        fichier1.b
coucou.s  Fichier     fichier160.log        fichier1.c
$ mkdir tmp; chmod 755 tmp; chown toto:toto tmp; cd tmp; pwd
/home/toto/tmp

Redirections

Les redirections sont souvent utilisées dans les commandes Linux.
Elles permettent de récupérer le résultat d'une ou plusieurs commandes dans un ou plusieurs fichiers ou de faire lire le contenu d'un fichier à une commande.

Entrée et sorties standard des commandes

Les commandes Linux ont par défaut 3 descripteurs de fichier différent.

  • Entrée standard :

L'entrée standard d'une commande correspond au descripteur de fichier 0.
Les commandes qui attendent des informations de la part de l'utilisateur déclenche une requête de lecture sur le descripteur 0.
Par défaut, ce descripteur est associé au terminal et donc par une saisie au clavier.

  • Sortie standard :

La sortie standard d'une commande correspond au descripteur de fichier 1.
Le résultat d'une commande est, par défaut, affichée à l'écran via ce descripteur mais il peut être redirigé dans un fichier.

  • Sortie d'erreur standard :

La sortie d'erreur standard d'une commande correspond au descripteur de fichier 2.
Quand une commande rencontre une erreur, celle-ci est retournée à l'écran via le descripteur 2 mais elle peut également être retournée dans un fichier.

Redirection des sorties vers un fichier

Cette redirection permet d'écrire dans un fichier le résultat d'une commande au lieu de l'afficher à l'écran.

  • Sortie standard (descripteur de fichier 1)

Simple redirection

$ commande > fichier
ou
$ commande 1> fichier

Si le fichier n'existe pas, il est automatiquement créé. S'il existe déjà, il est tout simplement écrasé.

Récupérer le résultat de la commande ls dans un fichier "liste"

$ ls > liste
$ cat liste
1coucou
cOucou
Coucou

Double redirection

Elle permet de concaténer le résultat d'une commande au contenu d'un fichier déjà existant.

$ commande >> fichier
ou
$ commande 1>> fichier

Si le fichier n'existe pas, il est automatiquement créé. S'il existe déjà, le résultat de la commande est ajouté au fichier.

$ pwd >> liste
$ cat liste
1coucou
cOucou
Coucou
/home/toto

  • Sortie d'erreur standard (descripteur de fichier 2)

Simple redirection

$ commande 2> fichier

$ ls /root 2> erreur
$ cat erreur
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée

Double redirection

$ commande 2>> fichier

$ mkdir /root/tmp 2>> erreur
$ cat erreur
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée
mkdir: impossible de créer le répertoire «/root/tmp»: Permission non accordée

  • Sortie standard et sortie d'erreur standard

Il est possible de rediriger plusieurs sorties sur une même ligne de commande.

$ commande 1> fichier1 2> fichier2
ou
$ commande 2> fichier2 1> fichier1

$ find /etc -name smb.conf 1> resultat 2> erreur
$ cat resultat
/etc/samba/smb.conf
$ cat erreur
find: "/etc/lvm/cache": Permission non accordée
find: "/etc/lvm/backup": Permission non accordée
find: "/etc/lvm/archive": Permission non accordée

Si l'on ne souhaite pas afficher et/ou enregistrer les erreurs retournées, il est possible de rediriger le descripteur 2 vers un fichier spécial existant sur tous les systèmes Linux "/dev/null"

$ find /etc -name smb.conf 1> resultat 2> /dev/null
$ cat resultat
/etc/samba/smb.conf
$ cat /dev/null
$
 

Redirection de l'entrée standard

La redirection de l'entrée standard concerne toutes les commandes attendant une saisie de la part de l'utilisateur sur le descripteur 0 (saisie écran).

$ mail toto
>Coucou
>Comment vas tu ?
>^d (équivalent de CTRL+d)
$

La commande mail lit sur l'entrée standard toutes les données saisies à l'écran. La saisie se termine par la fonction CTRL+d.
Ensuite, la commande mail envoie ces données dans un message à l'utilisateur indiqué (toto).

Il est tout à fait possible d'écrire le contenu du message dans un fichier et de "l'injecter" à la commande mail sur son descripteur 0.

$ commande 0< fichier
ou
$ commande < fichier

$ echo "Coucou, comment vas tu ?" > message
$ cat message
Coucou, comment vas tu ?
$ mail toto < message

 

Redirections avancées

Rediriger les descripteurs 1 et 2 vers le même fichier :

$ commande 1> fichier1 2>&1
ou
$ commande 2> fichier2 1>&2

Le principe consiste à écrire dans un fichier le résulat du descripteur 1 dans le fichier "fichier1" (1> fichier1) puis le résultat du descripteur 2 vers le descripteur 1 (2>&1) et par conséquent dans le fichier "fichier1".

$ find /etc -name smb.conf 1> resultat 2>&1
$ cat resultat
find: "/etc/lvm/cache": Permission non accordée
find: "/etc/lvm/backup": Permission non accordée
find: "/etc/lvm/archive": Permission non accordée
/etc/samba/smb.conf

Attention :

$ commande 2>&1 1> fichier1

Cette syntaxe n'agit pas de la même manière que les précédentes.

En effet, nous indiquons dans un premier temps de rediriger le descripteur 2 (sortie d'erreur standard) vers le descripteur 1, c'est à dire vers la sortie standard qui est, par défaut, l'affichage à l'écran, puis dans un second temps nous redirigeons le descripteur 1 (sortie standard) vers le fichier "fichier1".
En conclusion, tous les messages d'erreurs seront affichés à l'écran et le résulat de la commande dans le fichier "fichier1".

Cela revient à saisir :

$ commande 1> fichier1

La double redirection en lecture :

Elle est principalement utilisée dans les scripts shell.
Elle permet de connecter l'entrée standard d'une commande sur une portion du script

Par exemple, avec la commande mail, cela permet de terminer la saisie du texte avec un caractère ou une chaine de caractère.

$ mail toto<<end
> bonjour
> ceci est un test
> end
$

Pour mettre fin à la commande mail, il suffit simplement de saisir l'expression inscrite juste après les doubles chevrons "<<". Dans l'exemple, la commande se termine après avoir saisi "end".
Identique à la fonction ^d (CTRL+d) en console.

Utilisation dans un script :

$ cat envoiMail.sh
#!/bin/sh
mail toto<<end
Bonjour,
Ceci est un test
Voici la liste des fichiers presents dans ton repertoire :
`ls -lhtr`
end
exit 0

Exécution du script

$ sh ./envoiMail.sh

Fermeture d'un descripteur :

Il est tout à fait possible de fermé un descripteur d'entrée ou de sortie en utilisant les symboles "<&-" ou ">&-" ou "2>&-".

Descripteur d'entrée standard "0" :

$ commande <&-

Descripteur de sortie standard "1" :

$ commande >&-

Descripteur de sortie d'erreur standard "2" :

$ commande 2>&-

Tubes de communications

Un tube de communication, ou pipe en anglais, permet de faire communiquer 2 commandes.
Ce "tube" est représenté par la barre verticale "|".
Le résultat de la commande de gauche est envoyé dans le tube et récupéré par la commande de droite.
C'est à dirte que la sortie standard "1" de la commande de gauche est connecté directement à l'entrée standard "0" de la commande de droite.
La sortie d'erreur standard "2" de la commande de gauche n'est pas envoyée dans le tube.

Pour que cette utilisation est un sens il faut impérativement que la commande de gauche utilise la sortie standard "1" et que la commande de droite utilise l'entrée standard "0".

Les commandes lisant leur entrée standard sont facilement identifiable car elles demandent une saisie au clavier.

Envoyer par mail la liste des users connectés :

$ who | mail toto

Envoyer par mail la liste des fichiers d'un répertoire :

$ ls -lht | mail toto

Envoyer un message à un utilisateur connecté au système :

$ echo "coucou, comment vas tu ?" | write tata

Afficher le nombre de fichiers d'un répertoire :

$ ls | wc -l
29

Envoyer par mail le nombre de fichiers d'un répertoire :

$ ls | wc -l | mail toto

Afficher avec pagination le contenu d'un fichier :

$ cat /etc/passwd | more

Lister le contenu d'un répertoire avec pagination :

$ ls -lht /etc | more

Afficher uniquement certains éléments d'un fichier :

$ cat /etc/passwd | grep root | cut -d':' -f1,7
root:/bin/bash

Envoyer par mail certains éléments d'un fichier :

$ cat /etc/passwd | grep root | cut -d':' -f1,7 | mail toto

Afficher à l'écran et enregistrer dans un fichier le contenu d'un répertoire :

$ ls -t | tee listeFichiers
listeFichiers
envoiMail.sh
message
script.sh
$ cat listeFichiers
listeFichiers
envoiMail.sh
message
script.sh

Il est donc tout à fait possible de cumuler autant de commandes que l'on souhaite à partir du moment où l'on respecte l'ordre des commandes et des sorties / entrées standard.

Rediriger la sortie d'erreur standard dans un tube

$ ls -l /root 2>&1 | tee listeFichiers
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée
$ cat listeFichiers
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée

 

Regrouper des commandes

Le regroupement de commandes est utilisé pour rediriger les sorties standards de plusieurs commandes vers un même fichier ou vers un tube ou pour exécuter des commandes dans un même environnement.

$ date ; ls > listeFichiers
lundi 19 septembre 2011, 08:47:11 (UTC+0200)
$ cat listeFichiers
1coucou
cOucou
Coucou
coucou.r
coucou.s
coucou.t
coucou.uvw
envoiMail.sh

Dans l'exemple ci-dessus, la commande date et ls sont regroupées grâce au caractère ";".
Le résultat de la commande date est affiché à l'écran alors que le résultat de la commande ls est redirigé dans le fichier "listeFichiers".

Afin de rediriger la sortie standard des 2 commandes au même endroit, ils faut obligatoirement les regroupées avec des parenthèses "()" ou des accolades "{}".

$ ( date ; ls ) > listeFichiers
$ cat listeFichiers
lundi 19 septembre 2011, 08:51:58 (UTC+0200)
1coucou
cOucou
Coucou
coucou.r
coucou.s
coucou.t
coucou.uvw
envoiMail.sh

Les commandes regroupées entre parenthèses sont exécutées par le shell enfant alors que les commandes regroupées entre accolades sont exécutées par le shell parent.

Regroupement avec parenthèses :

$ pwd
/home/toto
$ ( mkdir temp ; cd temp ; touch fichierTemp ; pwd ; ls ) > listeFichiers
$ cat listeFichiers
/home/toto/temp
fichierTemp
$ pwd
/home/toto

Après exécution des commandes, le répertoire courant est le même qu'avant exécution.

Regroupement avec accolades :

$ pwd
/home/toto
$ { mkdir temp ; cd temp ; touch fichierTemp ; pwd ; ls ; } > listeFichiers
$ pwd
/home/toto/temp
$ cat ../listeFichiers
/home/toto/temp
fichierTemp

Après exécution des commandes, le répertoire courant n'est plus le même qu'avant exécution.

  • L'exécution de commandes regroupées entre accolades est plus rapide que les commandes regroupées entre parenthèses.
  • Le regroupement entre accolades doit obligatoirement se terminer par un point-virgule.
    Les accolades doivent toujours être suivies et précédées par une espace.

     

Généralement, le regroupement entre parenthèses est plus utilisé que le regroupement entre accolades car la syntaxe est plus simple et cela ne modifie pas le shell courant mais pour un gain de performances le regroupement entre accolades est préférable.

Exécuter des commandes en arrière-plan

Pour exécuter des commandes en arrière-plan, il suffit de rajouter à la fin le caractère "&".
Cela permet de récupérer le shell aussitôt après sans être obligé d'attendre la fin de l'exécution de la commande précédente.
Par contre, il est préférable d'utiliser la redirection de la sortie standard et de la sortie d'erreur standard.

$ find /etc -name passwd 1>listePasswd 2>/dev/null &
$ echo "On peut saisir d'autre commandes en attendant de voir le message ci-dessous une fois le processus terminé"
[1]+  Exit 1                  find /etc -name passwd > listePasswd 2> /dev/null
$ cat listePasswd
/etc/passwd
/etc/webmin/passwd
/etc/pam.d/passwd

Paramétrer son environnement de travail

Les variables d'environnement

Le commande set permet d'obtenir la liste des variables paramétrées pour le shell courant.

$ set
BASH=/bin/bash
HOME=/home/toto
HOSTNAME=myhostname
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
PS1='\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h$
PS2='> '
PS4='+ '
SHELL=/bin/bash
...

Le caractère $ permet d'obtenir la valeur d'une variable :

$ echo $BASH
/bin/bash

Pour créer ou modifier une variable :

$ VARIABLE='valeur'
$ echo $VARIABLE
valeur

Attention, car toutes variables créées ou modifiées de cette manière ne sont valables que pour la session en cours.

$ MAVARIABLE='<= Ceci est le contenu de ma variable =>'
$ echo $MAVARIABLE
<= Ceci est le contenu de ma variable =>

Les principales variables :

HOME : cette variable contient le chemin du répertoire d'accueil de l'utilisateur.

PATH : cette variable contient une liste de répertoire dans lesquels le shell recherche toutes les commandes qui sont exécutées.
Si une commande est exécutée et qu'elle ne se trouve dans aucun des répertoires indiqués dans la variable PATH, une erreur sera retournée en indiquant que la commande est introuvable.
Si l'on souhaite exécuter une commande qui se trouve dans un répertoire non indiqué dans la variable PATH, il suffit tout simplement de modifier le contenu de la variable.

$ PATH=$PATH:/leRepertoireDeMaCommande

Pour modifier la variable PATH définitivement, il suffit d'ajouter une ligne au fichier ~/.bashrc :

> export PATH=$PATH:/mon_autre_repertoire

Ou modifier directement le fichier /etc/environment :

> PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/mon_autre_repertoire"

Pour exécuter une commande présente dans le répertoire courant, il suffit d'ajouter à la variable PATH la valeur ":." ou ":".

PWD : cette variable contient le chemin du répertoire courant.

PS1 : cette variable contient la chaine de caractères correspondant au prompt ($ en règle générale).

Pour faire apparaitre la valeur du répertoire courant dans le prompt :

$ PS1='$PWD$ '
ou
$ PS1='\w$ '

BASH : Séquence d'échappement permettant de paramétrer la variable PS1

Séquence échappement Valeur
\u Nom de l'utilisateur
\h Nom de la machine
\w Répertoire courant
\W Partie terminale du répertoire courant

PS2 : cette variable contient la chaine de caractères correspondant au prompt secondaire (> en règle générale).

$ echo 'ceci est le début
> de ma chaine de texte
> et tant que je ne saisi pas
> le caractère apostrophe
> ça continue
> '
ceci est le début
de ma chaine de texte
et tant que je ne saisi pas
le caractère apostrophe
ça continue
$

$ mail toto<<fin
> un autre exemple
>
> fin
$

TERM : cette variable contient le type du terminal de l'utilisateur.

LOGNAME : cette variable contient le nom de l'utilisateur connecté.

Exporter des variables :

Il est parfois nécessaire d'exporter une variable car toutes ne le sont pas.
Pour savoir quelles sont les variables exportées d'office il suffit d'utiliser la commande env.

$ env
TERM=xterm
SHELL=/bin/bash
CDPATH=:.:/home/
USER=toto
...

Pour exporter une variable :

$ MAVARIABLE='maValeur'
$ export MAVARIABLE

ou

$ export MAVARIABLE='maValeur'

Par exemple, en BASH, pour utiliser le programme nano (éditeur de texte) comme éditeur par défaut du programme mutt (client mail en ligne de commande), il suffit d'inscrire dans son fichier ~/.bashrc :

export EDITOR=/usr/bin/nano

et pour une prise en compte immédiate, sans être obligé de se reconnecter, saisir dans la console :

$ export EDITOR=/usr/bin/nano
Et pour vérifier :
$ set | grep EDITOR
EDITOR=/usr/bin/nano
$ env | grep EDITOR
EDITOR=/usr/bin/nano

Les options du shell

Pour activer ou désactiver les options du shell, il suffit d'utiliser la commande set avec les options -o et +o.

Activation :

$ set -o option

Désactivation :

$ set +o option

Pour visualiser  la liste des options disponibles ainsi que leur état, saisir dans une console :

$ set -o
allexport       off
braceexpand     on
emacs           on
errexit         off
errtrace        off
functrace       off
hashall         on
histexpand      on
history         on
ignoreeof       off
interactive-comments    on
keyword         off
monitor         on
noclobber       off
noexec          off
noglob          off
nolog           off
notify          off
nounset         off
onecmd          off
physical        off
pipefail        off
posix           off
privileged      off
verbose         off
vi              off
xtrace          off

Détails de certaines options :

ignoreeof

Pour quitter le shell, il existe 3 méthodes :

  • La commande exit.
  • La commande logout.
  • La combinaison des touches ^d (CTRL+d).

Si l'option ignoreeof est activée, il n'est plus possible de quitter le shell en appuyant sur ^d.

noclobber

Quand une redirection est faite vers un fichier déjà existant, celui-çi est automatiquement écrasé sans confirmation. Pour inverser se fonctionnement, il suffit d'activer l'option noclobber.

On vérifie l'état de l'option noclobber

$ set -o | grep noclobber
noclobber       off

On redirige le résultat de la commande ls vers le fichier liste

$ ls -l > liste

On redirige le résultat de la commande pwd vers le fichier liste déjà existant

$ pwd > liste

On active l'option noclobber

$ set -o noclobber

On vérifie que l'option noclobber est bien activée

$ set -o | grep noclobber
noclobber       on

On redirige le résultat de la commande pwd vers le fichier liste déjà existant

$ pwd > liste
-bash: liste : impossible d'écraser le fichier existant

On force la redirection de la commande pwd vers le fichier liste déjà existant

$ pwd >| liste

emacs & vi

Ces 2 options permettent de paramétrer le rappel des commandes.
En ksh, ces 2 options sont désactivées.
En bash, seule l'option emacs est activée.

xtrace

Cette option est utilisée pour déboguer les scripts shell.

Les alias

Les alias permettent de créer des raccourcis vers des commandes et de les personnaliser.

Pour visualiser la liste des alias déjà existant :

$ alias
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -lF'
alias ls='ls --color=auto'
alias tarc='tar -cvzf'
alias tarx='tar -xvzf'

Pour visualiser un alias en particulier :

$ alias la
alias la='ls -A'

Pour créer un alias :

$ alias c='clear'
$ alias rm='rm -i'
$ rm liste
rm : supprimer fichier «liste» ? y

En bash, pour créer un alias définitivement, il suffit de le renseigner dans le fichier ~/.bashrc

Pour supprimer un alias :

$ unalias c
$ c
c : commande introuvable

Historique des commandes

Le shell enregistre toutes les commandes saisies dans un fichier texte.
En bash, il s'agit du fichier ~/.bash_history
En ksh, il s'agit du fichier ~/.sh_history
Pour utiliser le rappel des commandes, le shell utilise soit emacs soit vi.
En bash, c'est l'option emacs qui est activée par défaut mais il est possible d'utiliser vi.
En ksh, les 2 options sont par défaut désactivées.
Le choix d'activer l'un, désactive l'autre.

$ set -o | grep "^emacs\|^vi"
emacs           on
vi              off

Pour les utilisateurs non habitués à l'éditeur vi, il est préférable d'utiliser emacs car le rappel des commandes se fait avec les flèches du clavier.

Les fichiers d'environnement

Les fichiers d'environnement sont utilisés pour stocker de manière permanente tout le paramétrage de l'environnement de l'utilisateur.

Ce sont des scripts shell qui contiennent un ensemble de commandes Linux.

Certains scripts sont exécutés uniquement par le shell de connexion et d'autres par un shell ordinaire.

Le shell de connexion est lancé immédiatement après l'identification de l'utilisateur.

Le shell de connexion exécute en premier le script /etc/profile. Ce script contient le paramétrage commun à tous les utilisateurs.
Il recherche ensuite dans le répertoire d'accueil de l'utilisateur un script dont le nom dépend du shell utilisé.

En sh / ksh :

Le script de connexion se nomme ~/.profile

En bash :

L'un des 3 scripts suivants dans l'ordre

  1. ~/.bash_profile
  2. ~/.bash_login
  3. ~/.profile

 

En bash, le shell de connexion exécute en premier le script /etc/profile, puis le script ~/.profile et enfin le script ~/.bashrc.

C'est dans ce dernier fichier, ~/.bashrc, que l'on peut définir de nouveaux alias, exporter automatiquement de nouvelles variables, paramétrer l'auto-complétion, définir les variables PS1 et PS2, paramétrer le nombre de commandes à historiser, etc etc.

Les bases de la programmation shell

Les variables utilisateur

Le shell permet de définir ou redéfinir des variables pour l'environnement de l'utilisateur.

Il est également possible de définir d'autres variables dites variables utilisateur qui vont permettre de stocker des informations utiles durant l'exécution de scripts.

Le nomage des variables

  • Le premier caractère doit être obligatoirement une lettre minuscule, majuscule ou un tiret bas [a-zA-Z_]
  • Les caractères suivants peuvent être des lettres en minuscule, majuscule, des chiffres de 0 à 9 ou un tiret bas [a-zA-Z0-9_]

Définition d'une variable

$ variable1=valeur1
$ echo $variable1
valeur1
$

Il ne faut surtout pas mettre d'espace avant et après le signe égal (=) ainsi que dans la valeur de la variable.

Si un espace est placé avant le signe '=', le shell va interpréter la chaine 'variable1' comme étant une commande et la chaine '=valeur1' comme étant un paramètre de la commande variable1.

Pour pouvoir affecter une valeur contenant un espace, il faut obligatoirement le protéger avec des simples quotes.

$ variable2='valeur2 valeur21'
$ echo $variable2
valeur2 valeur21
$

Pour supprimer le contenu d'une variable, il faut utiliser la commande unset :

$ variable3=valeur3
$ echo $variable3
valeur3
$ set | grep variable3
variable3=valeur3
$ unset variable3
$ echo $variable3
$ set | grep variable3
$

Concaténer des variables avec une chaine de caractères :

Un exemple qui produit une erreur

$ fichier=monFichier
$ dateJour=20111006
$ nouveauFichier=$fichier_$dateJour
$ echo $nouveauFichier
20111006
$

Le but de cet exemple est de concaténer la variable $fichier, un tiret bas '_', puis la variable $dateJour.
Le résultat final est erroné car au moment d'affecter la nouvelle variable nouveauFichier (ligne 3), le shell interprète $fichier_ comme étant une variable à part entière puisque le tiret bas '_' est autorisé dans le nommage des variables.
La variable $fichier_ n'existant pas, celle ci est égale à "blanc".
Pour indiquer au shell qu'il faut interpréter le tiret bas '_' comme étant une chaine de caractères, il faut obligatoirement entourer la variable qui le précède avec des {}.

$ fichier=monFichier
$ dateJour=20111006
$ nouveauFichier=${fichier}_$dateJour
$ echo $nouveauFichier
monFichier_20111006
$

Substituer des variables :

Le shell offre la possibilité d'attribuer une valeur par défaut aux variables non initialisées ou au contraire initialisées.

Expression ${variable:-valeur}

  • Si la variable n'est pas vide, l'expression est égale au contenu de la variable
  • Si la variable est vide, l'expression est égale au contenu de valeur

$ jour=mardi
$ echo "Nous sommes : ${jour:-dimanche}"
Nous sommes : mardi
$ unset jour
$ echo "Nous sommes : ${jour:-dimanche}"
Nous sommes : dimanche
$ echo $jour
$

Expression ${variable:=valeur}

  • Si la variable n'est pas vide, l'expression est égale au contenu de la variable
  • Si la variable est vide, l'expression et la variable sont égale au contenu de valeur

$ jour=mardi
$ echo "Nous sommes : ${jour:=dimanche}"
Nous sommes : mardi
$ echo $jour
mardi
$ unset jour
$ echo "Nous sommes : ${jour:=dimanche}"
Nous sommes : dimanche
$ echo $jour
dimanche
$

Expression ${variable:+valeur}

  • Si la variable n'est pas vide, l'expression est égale au contenu de valeur
  • Si la variable est vide, l'expression est égale à variable, donc vide

$ bool=1
$ echo "Si bool=1 alors j'affiche : ${bool:+true}"
Si bool=1 alors j'affiche : true
$ unset bool
$ echo "Si bool=1 alors j'affiche : ${bool:+true}"
Si bool=1 alors j'affiche :
$

Expression ${variable:?message}

  • Si la variable n'est pas vide, l'expression est égale au contenu de la variable
  • Si la variable est vide, l'expression est égale au nom de la variable suivi du contenu de message

Dans un script, si la variable est vide, le message est affiché et le script se termine aussitôt

$ nombre=56
$ echo ${nombre:?"nombre indefini"}
56
$ unset nombre
$ echo ${nombre:?"nombre indefini"}
-bash: nombre: nombre indefini
Affichage du message par défaut
$ echo ${nombre:?}
-bash: nombre : parametre vide ou non defini
$

Substitution de commandes

Les caractères de substitution permettent de remplacer une commande par le résultat de son exécution.

Par exemple, comment faire pour afficher un message affichant la date du jour.

$ echo "Nous sommes le date"
Nous sommes le date
$

Il suffit d'entourer la commande avec les caractères de substitution :

  • Les quotes inversés `commande`
  • $(commande)

$ echo "Nous sommes le `date`"
Nous sommes le vendredi 7 octobre 2011, 08:39:49 (UTC+0200)
$ echo "Nous sommes le $(date)"
Nous sommes le vendredi 7 octobre 2011, 08:40:05 (UTC+0200)
$

Idem pour initialiser une variable :

$ date=`date`
$ echo $date
vendredi 7 octobre 2011, 08:42:34 (UTC+0200)
$

Les caractères de protection

Les caractères de protection servent à utiliser les caractères spéciaux du shell comme n'importe quels caractères.

Les simples quotes

Les simples quotes désactivent tous les caractères spéciaux du shell. Ils doivent être utilisés en nombre pair sur une ligne de commande.

$ echo $PWD
/home/toto

Avec les simples quotes, la fonction du caractère '$' est désactivée :

$ echo '$PWD'
$PWD
$

Afficher tous les fichiers d'un dossier à l'aide de la fonction 'echo' et du caractère spécial '*'

$ echo *
1coucou cOucou Coucou coucou.r ...

Avec les simples quotes, la fonction du caractère '*' est désactivée :

$ echo '*'
*
$

$ echo Bonjour $(logname)
Bonjour toto
$ echo 'Bonjour $(logname)'
Bonjour $(logname)
$

La quote ne se protège pas elle-même.
Tant que les quotes ne sont pas par pair, le shell affiche le prompt secondaire

$ echo 'J'affiche un quote supplémentaire'
>

L'antislash '\'

L'antislash désactive la fonctionnalité du caractère qui le suit.

$ echo La liste des fichiers * du dossier $PWD
La liste des fichiers 1coucou cOucou Coucou coucou.r du dossier /volume1/home/ronan/atuer/toto
$ echo La liste des fichiers \* du dossier \$PWD
La liste des fichiers * du dossier $PWD
$

L'antislash se protège lui-même

$ echo \\
\
$

$ echo L\'antislash protège la quote
L'antislash protège la quote
$

Le premier antislash protège le second, le troisième protège le '$'

$ echo \\\$PWD
\$PWD
$

Les guillemets

Les guillemets protège tous les caractères spéciaux du shell à l'exception du dollar $, des quotes inversés ``, du dollar et des parenthèses $( ), de l'antislash \ et des guillemets ".

$ echo "
> La variable \$HOME est substituée par $HOME
> La commande \$(logname) est exécutée : $(logname)
> Je peux afficher des > et des |
> Et même des guillemets \"."
La variable $HOME est substituée par /home/toto
La commande $(logname) est exécutée : toto
Je peux afficher des > et des |
Et même des guillemets ".
$

En règle générale, il est préférable d'utiliser la commande echo avec des guillemets

Récapitulatif des caractères spéciaux

Caractères Signification
espace - tabulation - saut de ligne Séparateurs de mots sur la ligne de commande
& Arrière-plan
|   <<   <   >   >> Tubes et redirections
() et {} Regroupement de commandes
; Séparateur de commandes
*   ?   [ ]   ?( )   +( )   *( )   !( )   @( ) Caractères de génération de noms de fichier
$ et ${ } Valeur d'une variable
``   $( ) Substitution de commandes
' '   " "   \ Caractères de protection

 

Interprétation d'une ligne de commande

Les caractères spéciaux du shell sont interprétés dans un ordre bien précis.

  1. Isolement des mots séparés par les caractères espace, tabulation, saut de ligne.
  2. Traitement des caractères de protections ( ' ', " ", \).
  3. Substitution des variables ($)
  4. Substitution des commandes (``, $( ) )
  5. Substitution des caractères de génération des noms de fichiers (*, ?, [ ], ?( ), +( ), *( ), !( ), @( ))
  6. Traitement des tubes et redirections (|, <<, <, >, >>)
  7. Lancement de la commande

Exemple :

$ echo "\$HOME --> $HOME
> Mon login --> $(logname)
> Nous sommes le `date`
> La liste des fichiers :" * | nl > test ; cat test
     1  $HOME --> /home/toto
     2  Mon login --> toto
     3  Nous sommes le jeudi 20 octobre 2011, 08:27:05 (UTC+0200)
     4  La liste des fichiers : fichier1 fichier2 fichier3 fichier4
$

Interprétation :

  1. \$HOME (traitement des caractères de protection)
  2. $HOME (substitution des variables)
  3. $(logname) et `date` (substitution des commandes)
  4. * (substitution des caractères de génération des noms de fichiers)
  5. | nl > test (traitement des tubes et redirections)
  6. echo "...." ; cat test (exécution des commandes)

Ecriture et exécution d'un script shell

Un script shell est un fichier texte contenant des commandes Linux internes ou externes.

Le nom du script peut avoir une extension mais cela n'est pas obligatoire.

En règle générale, tous les scripts portent l'extension .sh

Exemple :

$ nl monPremierScript.sh
     1  pwd
     2  echo "Liste des fichiers commençant par la lettre C"
     3  ls -lh c*
$

Exécution :

$ bash monPremierScript.sh
/home/toto
Liste des fichiers commençant par la lettre C
-rw-r--r-- 1 toto toto 0 2011-09-14 13:55 cOucou
-rw-r--r-- 1 toto toto 0 2011-09-15 07:20 coucou.r
-rw-r--r-- 1 toto toto 0 2011-09-15 07:20 coucou.s
-rw-r--r-- 1 toto toto 0 2011-09-15 07:20 coucou.t
-rw-r--r-- 1 toto toto 0 2011-09-15 07:21 coucou.uvw
$

Exécuter un script par le shell enfant

Il existe 3 méthodes pour exécuter un script.

La première méthode consiste à appeler la commande bash, sh, ksh etc.. (l'interpréteur de commande désiré) suivi du nom du script.

$ bash monPremierScript.sh

Dans ce cas, le droit de lecture sur le fichier est suffisant.

$ ls -l monPremierScript.sh
-rw-r--r-- 1 toto toto 68 2011-10-20 08:52 monPremierScript.sh
$

La seconde méthode consiste à utiliser l'entrée standard de la commande bash, sh, ksh etc.. afin de lui faire lire le contenu du script.

$ bash < monPremierScript.sh

Dans ce cas, le droit de lecture sur le fichier est également suffisant.
Cette méthode est très peu utilisée.

La troisième méthode consiste à rendre le script exécutable.

$ chmod u+x monPremierScript.sh
$ ls -l monPremierScript.sh
-rwxr--r-- 1 toto toto 68 2011-10-20 08:52 monPremierScript.sh
$ ./monPremierScript.sh

Choisir un interpréteur de commande :

Pour indiquer au script quel interpréteur de commande utiliser, il faut indiquer sur la première ligne du script les caractères #! suivi du chemin absolu de l'interpréteur.

Par exemple, pour utiliser l'interpéteur de commande BASH, on recherche son chemin absolu :

$ whereis bash
bash: /bin/bash /etc/bash.bashrc /usr/share/man/man1/bash.1.gz
$

La résultat de la commande nous indique le répertoire /bin/bash
Inscrire sur la première ligne du script #! /bin/bash

$ nl monPremierScript.sh
     1  #! /bin/bash
     2  pwd
     3  echo "Liste des fichiers commençant par la lettre C"
     4  ls -lh c*
$

De cette manière, le script sera toujours exécuté avec l'interpréteur de commande BASH.

Exécuter un script par le shell courant

L'exécution d'un script par le shell courant permet de modifier l'environnement du shell courant.

Les droits d'exécution sur le script ne sont pas nécessaires.
L'utilisation de la commande interne "." est nécessaire pour l'exécution.

Par exemple, pour modifier l'éditeur par défaut, il est nécessaire de modifier la variable environnement EDITOR.

$ env | grep EDITOR
EDITOR=/usr/bin/vi
echo $EDITOR
/usr/bin/vi
nano .profile
export EDITOR='/usr/bin/nano'
^o (enregistrer)
^x (quitter)
$ . .profile
$ echo $EDITOR
/usr/bin/nano
env | grep EDITOR
EDITOR=/usr/bin/nano
$

Attention, l'appel de la commande interne "." est obligatoirement suivi d'un espace et d'un script shell.

Les commentaires

Il est possible d'écrire dans un script des commentaires afin de faciliter la compréhension du script.

Les commentaires sont désignés à l'aide du caractère #.
Ne pas confondre avec #! inscrit sur la première ligne du script qui sert à renseigner l'interpréteur de commande.

$ nl monPremierScript.sh
     1    #! /bin/bash
       
     2    # Ceci est un commentaire
     3    # La commande PWD indique le répertoire courant
     4    pwd
     5    # La commande ECHO permet d'afficher du texte et des informations
     6    echo "Liste des fichiers commençant par la lettre C"
     7    # La commande LS permet de lister tous les fichiers commençant par la lettre "c"
     8    ls -lh c*
$

Les variables réservées du shell

Dans un script, il est possible de récupérer en lecture un certain nombre de variables réservées.
Ces variables sont initialisées par le shell et contiennent des informations diverses et variées.

Les paramètres positionnels

Les scripts shell sont capables de récupérer des arguments placés juste après l'appel du script.

  • $# indique le nombre d'arguments passés au script
  • $0 indique le nom du script
  • $1, $2, $3 ... $9 contient la valeur de l'argument 1, 2, 3 ... 9

En KSH et en BASH, il est possible d'aller au delà de 9 arguments en utilisant les variables spéciales ${10}, ${11} etc...
Les accolades sont obligatoires à partir du moment où le nom de la variable contient plus d'un chiffre.

  • $* et $@ contient la liste de tous les arguments

Exemple :

$ nano monSecondScript.sh
#! /bin/bash
echo "Il y a $# arguments passés au script"
echo "Le script se nomme $0"
echo "La valeur du premier argument : $1"
echo "La valeur du second argument : $2"
echo "La valeur du troisième argument : $3"
echo "Les valeurs de tous les arguments : $*"
^o (enregistrer)
^x (quitter)
$ chmod u+x monSecondScript.sh
$ ./monSecondScript.sh arg1 arg2 arg3 arg4 arg5
Il y a 5 arguments passés au script
Le script se nomme ./monSecondScript.sh
La valeur du premier argument : arg1
La valeur du second argument : arg2
La valeur du troisième argument : arg3
Les valeurs de tous les arguments : arg1 arg2 arg3 arg4 arg5
$

Exemple avec 3 arguments :

$ ./monSecondScript.sh 25 + 15
Il y a 3 arguments passés au script
Le script se nomme ./monSecondScript.sh
La valeur du premier argument : 25
La valeur du second argument : +
La valeur du troisième argument : 15
Les valeurs de tous les arguments : 25 + 15
$

Tous les arguments doivent être séparés les uns des autres par un espace ou une tabulation

La commande shift

La commande shift permet de décaler la liste des arguments d'une ou plusieurs positions vers la gauche.

Cette commande est utilisée dans le cas où le premier argument n'a pas à subir le même traitement que les suivants.
Dans ce cas, le premier argument doit être sauvegardé dans une variable et l'exécution de la commande shift entraine le remplacement du premier argument par le second et ainsi de suite.

$ nl monTroisiemeScript.sh
     1  #! /bin/bash
     2  # Ce script doit recevoir en premier argument le nom d'un dossier puis des noms de fichiers pour les suivants
     3  # Affichage des variables avant l'utilisation de la commande shift
     4  echo -e "Affichage avant exécution de la commande shift\n"
     5  echo "1er argument \$1 : $1"
     6  echo "1er argument \$2 : $2"
     7  echo "1er argument \$3 : $3"
     8  echo "1er argument \$4 : $4"
     9  echo "Tous les arguments \$* : $*"
    10  echo -e "Le nombre d'argument \$# : $#\n"
    11  # On sauvegarde le premier argument dans la variable rep
    12  rep=$1
    13  # On décale tous les arguments avec la commande shift
    14  shift
    15  # Affichage des variables après l'exécution de la commande shift
    16  echo -e "Affichage après exécution de la commande shift\n"
    17  echo "1er argument \$1 : $1"
    18  echo "1er argument \$2 : $2"
    19  echo "1er argument \$3 : $3"
    20  echo "1er argument \$4 : $4"
    21  echo "Tous les arguments \$* : $*"
    22  echo -e "Le nombre d'argument \$# : $#\n"
    23  # Création du répertoire
    24  mkdir $rep
    25  # On se positionne dans le nouveau répertoire
    26  cd $rep
    27  # Création des fichiers dans le nouveau répertoire
    28  for fichier in $*
    29  do
    30          touch $fichier
    31  done
$ chmod u+x monTroisiemeScript

Exécution du script :

$ ./monTroisiemeScript.sh test fic1 fic2 fic3 fic4 fic5 fic6
Affichage avant exécution de la commande shift
1er argument $1 : test
1er argument $2 : fic1
1er argument $3 : fic2
1er argument $4 : fic3
Tous les arguments $* : test fic1 fic2 fic3 fic4 fic5 fic6
Le nombre d'argument $# : 7
Affichage après exécution de la commande shift
1er argument $1 : fic1
1er argument $2 : fic2
1er argument $3 : fic3
1er argument $4 : fic4
Tous les arguments $* : fic1 fic2 fic3 fic4 fic5 fic6
Le nombre d'argument $# : 6
ls -l ./test/
total 0
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic1
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic2
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic3
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic4
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic5
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic6
$

Code de retour d'une commande

Toutes les commandes Linux retournent un code d'erreur compris entre 0 et 255.
La valeur 0 représente la valeur vrai (succès de la commande).
Les valeurs supérieur à 0 représente la valeur faux (échec de la commande).
Le code erreur de la dernière commande utilisée est contenu dans la variable $?

$ ls f*
f1chier               fichier159159.log  fichier159.log  fichier161.log  fichier1.abc  fichier1.c   fset
fichier159159159.log  fichier159160.log  fichier160.log  fichier1.a      fichier1.b    fichier.log
$ echo $?
0
$ ls 2*
ls: impossible d'accéder à 2*: Aucun fichier ou dossier de ce type
$ echo $?
2

Dans un script shell, le test du code de retour d'une commande permet d'effectuer différentes actions.

Un script shell étant lui-même une commande, il est possible de lui faire retourner un code d'erreur avec la commande exit.

Par exemple, il est possible d'indiquer dans un script exit 1 afin d'indiquer une erreur rencontrée et/ou exit 0 afin d'indiquer que tout c'est bien déroulé.

Dans l'exemple suivant, le script test s'il reçoit bien au minimum 2 arguments et renvoi le code erreur 1 si c'est faux.

$ nl ./script.sh
1  #! /bin/bash
2  # Le script doit recevoir au minimum 2 arguments
3  if [ $# -lt 2 ]
4  then
5          # Si le nombre d'arguments est inférieur à 2
6          # on retourne le code erreur 1
7          echo "Nombre arguments incorrect"
8          exit 1
9  else
10          # Si le nombre d'arguments est supérieur ou égal à 2
11          # on retourne le code erreur 0
12          echo "Nombre arguments correct"
13          exit 0
14  fi
$

Exécution du script :

$ ./script.sh
Nombre arguments incorrect
$ echo $?
1
$ ./script.sh test
Nombre arguments incorrect
$ echo $?
1
$ ./script.sh test test2
Nombre arguments correct
$ echo $?
0
$

Autres variables spéciales

La variable $$ représente le PID du shell qui interprète le script.
La valeur de cette variable est la même pendant toute la durée d'exécution du script et différente à chaque utilisation du script.

Exemple :

Dans ce script, la variable $$ est utilisée pour générer le nom d'un dossier différent à chaque exécution du script.

$ nl monQuatriemeScript.sh
     1  #! /bin/bash
     2  dossierTemp=dossier_$$
     3  echo "Création du dossier \"$dossierTemp\""
     4  mkdir $dossierTemp
     5  cd $dossierTemp
     6  for (( i=0 ; i<10 ; i++)) do
     7          touch fichier_$i
     8  done
     9  exit 0
$

$ ./monQuatriemeScript.sh
Création du dossier "dossier_26563"
$ ./monQuatriemeScript.sh
Création du dossier "dossier_26581"
$

La variable $! représente le PID d'une commande exécutée en arrière plan.

exemple :

$ nl monCinquiemeScript.sh
     1  #! /bin/bash
     2  echo "Le script est exécuté sous le PID $$"
     3  find /etc -name $1 1> resultat 2> /dev/null &
     4  echo "La commande FIND est en cours d'exécution sous le PID $!"
     5  ps
     6  exit 0
$

$ ./monCinquiemeScript.sh hosts.allow
Le script est exécuté sous le PID 29999
La commande FIND est en cours d'exécution sous le PID 30000
  PID TTY          TIME CMD
19703 pts/0    00:00:00 bash
29999 pts/0    00:00:00 monCinquiemeScr
30000 pts/0    00:00:00 find
30001 pts/0    00:00:00 ps
$

La commande read

La commande read lit son entrée standard et affecte les valeurs saisies dans la ou les variables passées en argument.

Lecture au clavier

$ read var1
coucou
$ echo $var1
coucou
$

Tous les mots saisis sont stockés dans la variables var1

$ read var1
coucou tout le monde
$ echo $var1
coucou tout le monde
$

Le premier mot est stocké dans var1 et le second dans var2

$ read var1 var2
A bientot
$ echo $var1
A
$ echo $var2
bientot
$

Le premier mot est stocké dans var1 et les autres dans var2

$ read var1 var2
A bientot tout le monde
$ echo $var1
A
$ echo $var2
bientot tout le monde
$

Le mot est stocké dans var1 et var2 est vide

$ read var1 var2
Bonjour
$ echo $var1
Bonjour
$ echo $var2
 
$

En KSH, il est possible d'associer un message à la commande read

$ read var1?"Saisir une valeur : "
Saisir une valeur : coucou
$ echo $var1
coucou
$

Code de retour

Le code retour de la commande read est vrai (0) si un texte est saisi et faux (1) si la commande est interrompue en appuyant sur ctrl+d (^d)

$ read var1
coucou
$ echo $?
0
$ echo $var1
coucou
$

$ read var1
[Entrée]
$ echo $?
0
$ echo $var1
 
$

$ read var1
$ ^d
$ echo $?
1
$ echo $var1
 
$

La variable IFS

Cette variable contient les caractères qui permettent de scinder les entrées au clavier.
Par défaut elle contient les caractères espace, tabulation (\t) et saut de ligne (\n)

Le contenu de cette variable peut être modifié

$ OLDIFS="$IFS"
$ IFS="|"                                       # modification du caractère de séparation
$ read var1 var2 var3
colonne1|colonne2|colonne3
$ echo $var1
colonne1
$ echo $var2
colonne2
$ echo $var3
colonne3
$ IFS="$OLDIFS"
$

Les expression $IFS et $OLDIFS doivent obligatoirement être placées entre guillemets pour que les caractères internes ne soient pas interprétés par le shell

En BASH, il est possible d'associer un message à la commande read grâce à l'option -p :

$ read -p "Votre nom ? " NAME
Votre nom ? toto
$ echo $NAME
toto

En y ajoutant l'option -s , il est possible d'utiliser la commande read pour saisir un mot de passe :

$ read -s -p "Votre passwd ? " PASSWD; echo
Votre passwd ?
$ echo $PASSWD
aaaa

Avec l'option -s, il ne faut pas oublier d'ajouter la commande echo pour afficher un saut de ligne.

Exécution de tests

La commande test permet de faire des tests sur des fichiers, des chaines de caractères et des nombres.

Elle renvoie le code retour 0 (vrai) ou 1 (faux) qu'il est possible de consulter en affichant la valeur de $?

Il existe 2 syntaxes pour utiliser la commande test

$ test expression

$ [ expression ]

La paire de crochet signifie la même chose que la commande test.
Les crochets ouvrants et fermants doivent obligatoirement être suivis et précédés d'un espace.

La commande test

Tests sur les fichiers

Expression Code de retour
-b FILE Vrai si le fichier existe et est du type spécial bloc
-c FILE Vrai si le fichier existe et est du type spécial caractère
-d FILE Vrai si le fichier existe et est du type répertoire
-e FILE Vrai si le fichier existe
-f FILE Vrai si le fichier existe et est du type ordinaire
-G FILE Vrai si le fichier existe et si l'utilisateur appartient au groupe propriétaire du fichier
-h FILE Vrai si le fichier existe et est du type lien symbolique
-L FILE Vrai si le fichier existe et est du type lien symbolique (idem -h)
-O FILE Vrai si le fichier existe et si l'utilisateur est le propriétaire du fichier
-r FILE Vrai si le fichier existe et est accessible en lecture
-s FILE Vrai si le fichier existe et n'est pas vide
-S FILE Vrai si le fichier existe et est du type socket
-w FILE Vrai si le fichier existe et est accessible en écriture
-x FILE Vrai si le fichier existe et est exécutable
FILE1 -ef FILE2 Vrai si les fichiers ont le même lien physique
FILE1 -nt FILE2 Vrai si FILE1 est plus récent que FILE2
FILE1 -ot FILE2 Vrai si FILE1 est plus ancien que FILE2

Exemple :

Le fichier /etc/group est un fichier ordinaire

$ test -f /etc/group
$ echo $?
0
$

Le fichier /etc/groupe n'existe pas (test avec l'autre syntaxe)

$ [ -f /etc/groupe ]
$ echo $?
1
$

Le fichier /etc/init.d existe et est un répertoire

$ [ -d /etc/init.d ]
$ echo $?
0
$

Le fichier /etc/group n'est pas un répertoire ou il n'exsite pas

$ test -d /etc/group
$ echo $?
1
$

Le fichier contenu dans la variable file n'est pas un répertoire ou n'existe pas

$ file=/etc/group
$ test -d $file
$ echo $?
1
$

L'utilisateur n'a pas le droit d'écriture sur le fichier /etc/group

$ [ -w /etc/group ]
$ echo $?
1
$

Tests sur les chaines de caractères

Expression Code de retour
-n STRING Vrai si la longueur de la chaine n'est pas égale à 0
-z STRING Vrai si la longueur de la chaine est égale à 0
STRING1 = STRING2 Vrai si les 2 chaines sont égales
STRING1 != STRING2 Vrai si les 2 chaines sont différentes
STRING Vrai si la chaine n'est pas vide (idem -n)

Pour les tests sur les chaines de caractères, il est recommandé de mettre le nom des variables entre guillemets.

Exemple :

Avec les variables suivantes :

$ str1="test1"
$ str2="test2"
$ str3="test1"
$

La variable $str1 n'est pas vide

$ [ -n "$str1" ]
$ echo $?
0
$

$ [ "$str1" ]
$ echo $?
0

$ [ -z "$str1" ]
$ echo $?
1
$

La variable $str4 est vide

$ [ -n "$str4" ]
$ echo $?
1
$

$ [ -z "$str4" ]
$ echo $?
0
$

$ [ "$str4" ]
$ echo $?
1
$

$str1 & $str3 sont identiques

$ [ "$str1" = "$str3" ]
$ echo $?
0
$

$ [ "$str1" != "$str3" ]
$ echo $?
1
$

$str1 & $str2 sont différentes

$ [ "$str1" != "$str2" ]
$ echo $?
0
$

$ [ "$str1" = "$str2" ]
$ echo $?
1
$

Tests sur les nombres

Expression Code de retour
INT1 -eq INT2 Vrai si INT1 est égal à INT2 (=)
INT1 -ge INT2 Vrai si INT1 est supérieur ou égal à INT2 (>=)
INT1 -gt INT2 Vrai si INT1 est supérieur à INT2 (>)
INT1 -le INT2 Vrai si INT1 est inférieur ou égal à INT2 (<=)
INT1 -lt INT2 Vrai si INT1 est inférieur à INT2 (<)
INT1 -ne INT2 Vrai si INT1 est différent de INT2 (!=)

Exemple :

Avec les variables suivantes

$ int1=1
$ int2=2
$ int3=3
$ int4=2
$

$int2 & $int4 sont égaux

$ [ $int2 -eq $int4 ]
$ echo $?
0

$int2 est supérieur ou égal à $int4

$ [ $int2 -ge $int4 ]
$ echo $?
0

$int3 est supérieur ou égal à $int1

$ [ $int3 -ge $int1 ]
$ echo $?
0

$int4 n'est pas supérieur ou égal à $int3

$ [ $int4 -ge $int3 ]
$ echo $?
1

$int3 est supérieur à $int2

$ [ $int3 -gt $int2 ]
$ echo $?
0

$int2 n'est pas supérieur à $int3

$ [ $int2 -gt $int3 ]
$ echo $?
1

$int2 est inférieur ou égal à $int3

$ [ $int2 -le $int3 ]
$ echo $?
0

$int2 est inférieur à $int3

$ [ $int2 -lt $int3 ]
$ echo $?
0

$int2 est inférieur ou égal à $int4

$ [ $int2 -le $int4 ]
$ echo $?
0

$int2 est différent de $int3

$ [ $int2 -ne $int3 ]
$ echo $?
0

$int2 n'est pas différent de $int4

$ [ $int2 -ne $int4 ]
$ echo $?
1
$

Les opérateurs

Opérateur Signification
! Négation
-a ET
-o OU

Les opérateurs sont exécutés avec une priorité bien précise :

  1. ! (négation)
  2. -a (ET)
  3. -o (OU)

 

Le fichier /etc/group n'est pas un répertoire

$ [ ! -d /etc/group ]
$ echo $?
0
$

Le fichier monPremierScript.sh existe et est exécutable

$ [ -f monPremierScript.sh -a -x monPremierScript.sh ]
$ echo $?
0
$

Le fichier monPremierScript.sh n'est pas un répertoire mais il est exécutable

$ [ -d monPremierScript.sh -o -x monPremierScript.sh ]
$ echo $?
0
$

Il est possible de modifier la priorité d'exécution des opérateurs en utilisant des paranthèses.
\(........\)
Les parenthèses doivent être protégées par des antislashes afin de ne pas être interprétées par le shell comme étant un regroupement de commandes.

L'utilisateur doit avoir le droit d'écriture sur le fichier fic1 et le fichier fic4 ou fic7 doit exister

$ ls
fic1  fic2  fic3  fic4  fic5  fic6  script.sh
$ [ -w fic1 -a \( -e fic4 -o -e fic7 \) ]
$ echo $?
0
$

Il doit toujours y avoir un espace autour des opérateurs !, -a et -o.

Exemples d'utilisation

Utilisation de la commande test avec la structure de controle if.

Principe d'utilisation :

if  commande1 
then
     commande2
     commande3
     ...
else
     commande4
     ...
fi

if  commande1  ; then
     commande2
     commande3
     ...
else
     commande4
     ...
fi

La commande1 est exécutée, si son code retour ($?) vaut 0 (vrai) alors les commandes 2 & 3 sont exécutées, sinon c'est la commande4 qui est exécutée (code retour de la commande1 supérieur à 0 - faux).

Exemple :

Dans l'exemple suivant, le script test s'il reçoit bien au minimum 2 arguments.
Dans le cas contraire, le script affiche un message indiquant que le nombre d'arguments est incorrect et se termine en retournant un code erreur 1.

$ nl monTroisiemeScript.sh
     1  #! /bin/bash
     2  # Ce script doit recevoir en premier argument le nom d'un dossier puis des noms de fichiers pour les suivants
     4  # Ce script doit recevoir au minimum 2 arguments
     5  # Le premier étant le nom d'un dossier
     6  # Les suivants étant les noms des fichiers
     7  # On test si il y a 2 arguments au minimum et on retourne le code erreur 1 si c'est faux
     8  if  [ $# -lt 2 ] 
     9  then
    10          echo "Nombre d'arguments incorrect !!!"
    11          exit 1
    12  fi
    13  # On sauvegarde le premier argument dans la variable rep
    14  rep=$1
    15  # On décale tous les arguments avec la commande shift
    16  shift
    17  # Création du répertoire
    18  mkdir $rep
    19  # On se positionne dans le nouveau répertoire
    20  cd $rep
    21  # Création des fichiers dans le nouveau répertoire
    22  for fichier in $*
    23  do
    24          touch $fichier
    25  done
    26  exit 0

$ ./monTroisiemeScript.sh test
Nombre d'arguments incorrect !!!
$ echo $?
1
$

$ ./monTroisiemeScript.sh test3 coucou
$ echo $?
0
$

La commande [[ ]]

La commande [[ ]] est une version améliorée de la commande test.
Tous les opérateurs utilisés avec la commandes test restent valables à l'exception des opérateurs logiques -a et -o respectivement remplacés par && et ||.

Tests sur les chaines

Contrairement à la commande test, il n'est plus nécessaire d'entourer les variables avec des guillemets.

Les différentes syntaxes utilisables :

$ echo $null
 
$ test -z "$null"
$ echo $?
0
$ [ -z "$null" ]
$ echo $?
0
$ [[ -z $null ]]
$ echo $?
0
$ [[ -z "$null" ]]
$ echo $?
0
$ [[ $null = "" ]]
$ echo $?
0
$

Les opérateurs suivants ont été ajoutés :

Opérateurs Code de retour
$chaine = modele Vrai si $chaine correspond au modèle
$chaine != modele Vrai si $chaine ne correspond pas au modèle
$chaine1 < $chaine2 Vrai si $chaine1 est lexicographiquement avant $chaine2
$chaine1 > $chaine2 Vrai si $chaine1 est lexicographiquement après $chaine2

En utilisant les expressions, il est possible de comparer des chaines à des modèles identiques à celles permettant le remplacement des noms de fichiers.

Caractères spéciaux pour modèles de chaines de caractères Signification
Caractères spéciaux valables dans tous les shells :
* 0 à n caractères
? 1 caractère quelconque
[abc] 1 caractère parmis ceux inscrits entre les crochets
[!abc] 1 caractère ne faisant pas partie de ceux inscrits entre les crochets
Caractères spéciaux non valides en Bourne Shell.
En bash, il faut activer l'option extglob (shopt -s extglob)
?(expression) de 0 à 1 fois l'expression
*(expression) de 0 à n fois l'expression
+(expression) de 1 à n fois l'expression
@(expression) 1 fois l'expression
!(expression) 0 fois l'expression
?(expression1 | expression2 | ...)
*(expression1 | expression2 | ...)
+(expression1 | expression2 | ...)
@(expression1 | expression2 |...)
!(expression1 | expression2 | ...)
alternatives

Exemple :

Le script suivant test si le numéro de téléphone saisi correspond bien au format +33240346523 ou 0240346523

$ nl test_telephone.sh
     1  #! /bin/bash
 
     2  echo -e "Saisir un numéro de téléphone : \c"
     3  read telephone
 
     4  # Test si le téléphone saisi est de la forme
     5  # +33240346523 ou 0240346523
 
     6  if [[ $telephone = @(+33)@([1-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9]) ||
     7        $telephone = @(0)@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9]) ]]
     8  then
     9          echo "Le numéro est correct"
    10          exit 0
    11  else
    12          echo "Le numéro est incorrect"
    13          exit 1
    14  fi
$ ./test_telephone.sh
Saisir un numéro de téléphone : 0240020202
Le numéro est correct
$ ./test_telephone.sh
Saisir un numéro de téléphone : +33256985478
Le numéro est correct
$ ./test_telephone.sh
Saisir un numéro de téléphone : 2356958457
Le numéro est incorrect
$ ./test_telephone.sh
Saisir un numéro de téléphone : +33025146987
Le numéro est incorrect
$ ./test_telephone.sh
Saisir un numéro de téléphone : g52365
Le numéro est incorrect
$

Tests logiques

Rappels :

  • Les opérateurs -a et -o sont respectivement remplacés par && et ||
  • Les parenthèses n'ont plus besoin d'être protégées
Commande test ( [ ] ) Commande [[ ]] Signification
\(.....\) (.....) Regroupement d'expressions
! ! Négation
-a && ET logique
-o || OU logique

Exemple avec la commande test :

if [ -w $fic1 -a \( -e $rep1 -o -e $rep2 \) ]
then
.....

Exemple avec la commande [[ ]] :

if [[ -w $fic1 && ( -e $rep1 || -e $rep2 ) ]]
then
.....

Les opérateurs du shell

Ces opérateurs permettent d'exécuter ou non une commande en fonction du code de retour d'une autre commande.

Opérateur Signification
&& ET logique
|| OU logique

 

Evaluation de l'opérateur &&

Syntaxe :

commande1 && commande2

La seconde commande (commande2) est exécutée uniquement si le code de retour de la commande (commande1) est égale à 0 (vrai).

Le code de retour global est égal à 0 (vrai) si le code de retour de chaque commande est égal à 0 (vrai).

Exemple :

Le répertoire temp/temp2 n'existe pas donc la commande cd n'est pas exécutée

$ ls -d temp/temp2
ls: impossible d'accéder à temp/temp2: Aucun fichier ou dossier de ce type
$ pwd
/home/toto
$ [[ -d temp/temp2 ]] && cd temp/temp2
$ echo $?                     # code de retour de la commande [[ ]]
1
$ pwd
/home/toto
$

Le répertoire temp/temp2 existe donc la commande cd est exécutée

$ mkdir temp/temp2
$ pwd
/home/toto
$ [[ -d temp/temp2 ]] && cd temp/temp2
$ echo $?                     # code de retour de la commande globale
0
$ pwd
/home/toto/temp/temp2
$

Le répertoire temp/temp2 existe mais le répertoire temp/temp3 n'existe pas donc la commande cd retourne un code erreur égal à 1

$ [[ -d temp/temp2 ]] && cd temp/temp3
-bash: cd: temp/temp3: Aucun fichier ou dossier de ce type
$ echo $?                     # code de retour de la commande cd
1
$

Ces actions peuvent également être exécutées avec la structure de commande if

$ pwd
/home/toto
$ ls -d temp/temp2
temp/temp2
$ if [[ -d temp/temp2 ]]
> then
> cd temp/temp2
> fi
$ echo $?
0
$ pwd
/home/toto/temp/temp2
$

Evaluation de l'opérateur ||

Syntaxe :

commande1 || commande2

La seconde commande (commande2) est exécutée uniquement si le code de retour de la commande (commande1) est égale à 1 (faux).

Le code de retour global est égal à 0 (vrai) si au moins une des commandes retourne un code égal à 0 (vrai).

Exemple :

Le répertoire temp/temp2 n'existe pas donc la commande echo est exécutée

$ ls -d temp/temp2
ls: impossible d'accéder à temp/temp2: Aucun fichier ou dossier de ce type
$ [[ -d temp/temp2 ]] || echo "Le répertoire n'existe pas"
Le répertoire n'existe pas
$ echo $?
0
$

Le répertoire temp/temp2 existe donc la commande echo n'est pas exécutée

$ mkdir temp/temp2
$ ls -d temp/temp2
temp/temp2
$ [[ -d temp/temp2 ]] || echo "Le répertoire n'existe pas"
$ echo $?
0
$

Ces actions peuvent également être exécutées avec la structure de commande if

$ ls -d temp/temp2
ls: impossible d'accéder à temp/temp2: Aucun fichier ou dossier de ce type
$ if [[ ! -d temp/temp2 ]]
> then
> echo "Le répertoire n'existe pas"
> fi
Le répertoire n'existe pas
$

Ne pas confondre les opérateurs du shell && et || qui effectuent une opération logique entre deux commandes et les opérateurs && et || de la commande [[ ]] qui sont internes à celle-ci

L'arithmétique

La commande expr

Syntaxe :

expr $nbr1 opérateur $nbr2
expr $chaine : expression régulière

Opérateurs :

Attention, certains opérateurs ayant une signification particulière pour le shell, ceux ci doivent être protégés par un antislash.

Opérateurs Signification
Opérateurs arithmétiques
$nb1 + $nb2 Addition
$nb1 - $nb2 Soustraction
$nb1 \* $nb2 Multiplication
$nb1 / $nb2 Division
$nb1 % $nb2 Modulo
Opérateurs de comparaison
$nb1 \> $nb2 VRAI si $nb1 est strictement supérieur à $nb2
$nb1 \>= $nb2 VRAI si $nb1 est supérieur ou égal à $nb2
$nb1 \< $nb2 VRAI si $nb1 est strictement inférieur à $nb2
$nb1 \<= $nb2 VRAI si $nb1 est inférieur ou égal à $nb2
$nb1 = $nb2 VRAI si $nb1 est égal à $nb2
$nb1 != $nb2 VRAI si $nb1 est diférent de $nb2
Opérateurs logiques
$chaine1 \& $chaine2 VRAI si les 2 chaines sont vraies
$chaine1 \| $chaine2 VRAI si l'une des 2 chaines est vraie
Opérateurs divers
-$nb1 Opposé de $nb1
\( expression \) Regroupement
$chaine : expression_reguliere Compare la chaine avec l'expression régulière

Les arguments de la commande expr doivent toujours être séparés par au moins un espace ou une tabulation.

Exemple :

$ nb=3
$ expr $nb + 5
8
$ expr $nb \* 6
18
$ expr $nb / 2
1
$ nb=10
$ expr $nb % 3
1
$ expr $nb - -5
15
$

Récupérer le résultat dans une variable :

$ nb2=`expr $nb - 2`
$ echo $nb2
8
$

Priorité d'exécution des opérateurs :

$ nb=5
$ nb2=`expr $nb \* 3 + 4`
$ echo $nb2
19
$ nb2=`expr $nb \* \( 3 + 4 \)`
$ echo $nb2
35
$

Résultat d'une comparaison VS valeur du code de retour :

$ nb=2
$ expr $nb \>= 1
1                            # Résultat de la comparaison
$ echo $?
0                            # Résultat du code de retour
$ expr $nb \>= 3
0                            # Résultat de la comparaison
$ echo $?
1                            # Résultat du code de retour
$

Différents types de comparaison :

$ nl comparaison.sh
     1  #!/bin/bash
 
     2  # Test sur le nombre d'arguments
     3  if [[ $# -ne 2 ]]
     4  then
     5          echo "Mauvais nombre d'arguments"
     6          echo "Utilisation : $0 expr1 expr2"
     7          exit 1
     8  fi
 
     9  # On compare expr1 et expr2 avec la commande EXPR et redirection du résultat dans /dev/null
    10  if expr $1 \> $2 > /dev/null
    11  then
    12          echo "Comparaison EXPR : $1 est supérieur à $2"
    13  else
    14          echo "Comparaison EXPR : $1 est inférieur à $2"
    15  fi
 
    16  # On compare expr1 et expr2 avec la commande [[ ]] (lexicographiquement)
    17  if [[ $1 > $2 ]]
    18  then
    19          echo "Comparaison lexico.. [[ ]] : $1 est supérieur à $2"
    20  else
    21          echo "Comparaison lexico.. [[ ]] : $1 est inférieur à $2"
    22  fi
 
    23  # On compare expr1 et expr2 avec la commande [[ ]]
    24  if [[ $1 -gt $2 ]]
    25  then
    26          echo "Comparaison [[ ]] : $1 est supérieur à $2"
    27  else
    28          echo "Comparaison [[ ]] : $1 est inférieur à $2"
    29  fi
 
    30  exit 0
$ ./comparaison.sh 9 5
Comparaison EXPR : 9 est supérieur à 5
Comparaison lexico.. [[ ]] : 9 est supérieur à 5
Comparaison [[ ]] : 9 est supérieur à 5
$ ./comparaison.sh 50 9
Comparaison EXPR : 50 est supérieur à 9
Comparaison lexico.. [[ ]] : 50 est inférieur à 9
Comparaison [[ ]] : 50 est supérieur à 9
$ ./comparaison.sh a b
Comparaison EXPR : a est inférieur à b
Comparaison lexico.. [[ ]] : a est inférieur à b
Comparaison [[ ]] : a est inférieur à b
$ ./comparaison.sh t r
Comparaison EXPR : t est supérieur à r
Comparaison lexico.. [[ ]] : t est supérieur à r
Comparaison [[ ]] : t est inférieur à r
$

Le résultat de la commande EXPR est toujours exact que ce soit des chiffres ou des caractères.

Etiquettes: 

La commande (( ))

Syntaxe :

((expression_arithmétique))

Utilisation :

La commande (( )) dispose de nombreux avantages par rapport à la commande expr

  • Opérateurs supplémentaires
  • Les arguments n'ont pas besoin d'être séparés par des espaces
  • Les variables n'ont pas besoin d'être préfixées par le symbole $
  • Les caractères spéciaux du shell n'ont pas besoin d'être protégés par des antislashes
  • Les affectations se font dans la commande
  • Son exécution est plus rapide

Une grande partie des opérateurs proviennent du langage C

Opérateurs Signification
Opérateurs arithmétiques
nbr1 + nbr2 Addition
nbr1 - nbr2 Soustraction
nbr1 * nbr2 Multiplication
nbr1 / nbr2 Division
nbr1 % nbr2 Modulo
Opérateurs travaillant sur les bits
~nbr1 Complément à 1
nbr1 >> nbr2 Décalage sur nbr1 de nbr2 bits à droite
nbr1 << nbr2 Décalage sur nbr1 de nbr2 bits à gauche
nbr1 & nbr2 ET bit à bit
nbr1 | nbr2 OU bit à bit
nbr1 ^ nbr2 OU exclusif bit à bit
Opérateurs de comparaison
nbr1 > nbr2 VRAI si nbr1 est strictement supérieur à nbr2
nbr1 >= nbr2 VRAI si nbr1 est supérieur ou égal à nbr2
nbr1 < nbr2 VRAI si nbr1 est strictement inférieur à nbr2
nbr1 <= nbr2 VRAI si nbr1 est inférieur ou égal à nbr2
nbr1 == nbr2 VRAI si nbr1 est égal à nbr2
nbr1 != nbr2 VRAI si nbr1 est différent de nbr2
Opérateurs logiques
!nbr1 Inverse la valeur de vérité de nbr1
&& ET
|| OU
Opérateurs divers
-nbr1 Opposé de nbr1
nbr1 = expression Assignement
(expression) Regroupement
nbr1 binop= nbr2 binop représente l'un des opérateurs suivants : +, -, /, *, %, >>, <<, &, |, ^. Equivalent à nbr1 = nbr1 binop nbr2

Exemples :

Ajouter 10 à nbr1 (2 méthodes différentes)

$ nbr1=10
$ ((nbr1=nbr1+10))
$ echo $nbr1
20
$ nbr1=10
$ ((nbr1+=10))
$ echo $nbr1
20
$

Test si nbr1 est supérieur à nbr2 et inversement

$ nbr1=5
$ nbr2=6
$ ((nbr1>nbr2))
$ echo $?
1                           # Code retour 1 (faux) car nbr1 n'est pas supérieur à nbr2
$ ((nbr1<nbr2))
$ echo $?
0                           # Code retour 0 (vrai) car nbr1 est inférieur à nbr2
$

Le script suivant compare les 2 arguments passés en paramètre

$ nl comparaison2.sh
     1  #!/bin/bash
 
     2  # Test sur le nombre d'arguments
     3  if (($#!=2))
     4  then
     5          echo "Mauvais nombre d'arguments"
     6          echo "Utilisation : $0 nbr1 nbr2"
     7          exit 1
     8  fi
 
     9  # On compare nbr1 et nbr2 avec la commande (( ))
    10  if (($1<$2))
    11  then
    12          echo "$1 est inférieur à $2"
    13  else
    14          if (($1>$2))
    15          then
    16                  echo "$1 est supérieur à $2"
    17          else
    18                  if (($1==$2))
    19                  then
    20                          echo "$1 est égal à $2"
    21                  else
    22                          echo "Comparaison impossible"
    23                  fi
    24          fi
    25  fi
 
    26  exit 0
$ ./comparaison2.sh 2 8
2 est inférieur à 8
$ ./comparaison2.sh 8 2
8 est supérieur à 2
$ ./comparaison2.sh 8 8
8 est égal à 8
$

Regroupements et tests logiques

$ nbr1=2
$ nbr2=5
$ if (( (nbr1>0) && (nbr2>nbr1) ))
> then
> echo "nbr1 est supérieur à 0 et inférieur à nbr2"
> else
> echo "nbr1 est égal à 0 ou supérieur à nbr2"
> fi
nbr1 est supérieur à 0 et inférieur à nbr2
$

La commande let

La commande let est équivalente à ((expression))

Syntaxe :

let "expression"

Exemple :

Multiplier nbr1 par 3

$ nbr1=5
$ let "nbr1=nbr1*3"
$ echo $nbr1
15
$

Calculer le modulo de nbr1 par 2 et l'affecter à la variable nbr2

$ nbr1=5
$ let "nbr2=nbr1%2"
$ echo $nbr1
5
$ echo $nbr2
1
$

Etiquettes: 

Substitution d'expressions arithmétiques

Il existe les caractères de substitution de commandes mais il existe également les caractères de substitution d'expressions arithmétiques.

Syntaxe :

commande argument1 $((expression_arithmetique)) ... argumentn

Exemple :

Rappel sur la substitution de commandes

$ echo "Nombre de users connectes : `who | wc -l`"
Nombre de users connectes : 1
ou
$ echo "Nombre de users connectes : $(who | wc -l)"
Nombre de users connectes : 1

Substitution d'expressions arithmétiques

$ cpt=1
$ ((cpt+=1))                               # Le résultat de la commande n'est pas affichée
$ echo $cpt
2
$

$ cpt=1
$ echo "Nouveau compteur : `((cpt+=1))`"
Nouveau compteur :                  # Le résultat de la commande n'est pas affichée et le compteur n'est pas incrémenté
$ echo $cpt
1
$

$ cpt=1
$ echo "Nouveau compteur : $((cpt+=1))"
Nouveau compteur : 2                # Le résultat de la commande est affiché et le compteur est incrémenté
$ echo $cpt
2
$

Il ne faut pas confondre (( )) et $(( )).
(( )) est une commande interne au shell.
$(( )) sont des caractères spéciaux du shell à l'égal de `` ou $().

Mise au point d'un script

Le shell propose quelques options qui permettent de débugger des scripts shell.

Option -x

L'option -x permet de débugger un script shell en affichant l'exécution des commandes après traitement des caractères spéciaux du shell.

Les différentes syntaxes permettant d'activer l'option -x :

Activer l'option

  1. set -x
  2. set -o xtrace
  3. bash -x nom_du_script

Désactiver l'option

  1. set +x
  2. set +o xtrace

Exemple :

Dans l'exemple suivant, la variable fichier n'a pas été préfixée par le symbole $.

$ nl liste.sh
     1  #!/bin/bash
     2  for fichier in `ls`
     3  do
     4          if [[ -f fichier ]]
     5          then
     6                  echo "$fichier"
     7          fi
     8  done
$

L'exécution du script ne retourne aucun résultat malgré la présence de fichiers dans le dossier d'exécution du script.

$ ./liste.sh
$

L'exécution du script avec activation de l'option -x démontre effectivement que la variable fichier n'est pas remplacée par sa valeur.

$ bash -x liste.sh
++ ls
+ for fichier in '`ls`'
+ [[ -f fichier ]]
+ for fichier in '`ls`'
+ [[ -f fichier ]]
+ for fichier in '`ls`'
+ [[ -f fichier ]]
...
$

L'activation de l'option de débuggage peut également être lancée directement dans le script.

$ nl liste.sh
     1  #!/bin/bash
     2  set -x
     3  for fichier in `ls`
     4  do
     5          if [[ -f fichier ]]
     6          then
     7                  echo "$fichier"
     8          fi
     9  done
$

Après correction de l'erreur et exécution du script en mode débuggage.

$ nl liste.sh
     1  #!/bin/bash
     2  set -x
     3  for fichier in `ls`
     4  do
     5          if [[ -f "$fichier" ]]
     6          then
     7                  echo "$fichier"
     8          fi
     9  done
$ ./liste.sh
++ ls
+ for fichier in '`ls`'
+ [[ -f 1coucou ]]
+ echo 1coucou
1coucou
+ for fichier in '`ls`'
+ [[ -f 24902 ]]
+ for fichier in '`ls`'
+ [[ -f 25013 ]]
+ for fichier in '`ls`'
+ [[ -f 25031 ]]
+ for fichier in '`ls`'
+ [[ -f 25043 ]]
+ for fichier in '`ls`'
+ [[ -f comparaison2.sh ]]
+ echo comparaison2.sh
comparaison2.sh
...
$

 

Autres options

Fonction Bourne shell, ksh, bash ksh, bash
Lecture des commandes sans exécution et détection des erreurs de syntaxe. set -n
set +n
set -o noexec
set +o noexec
Affichage des commandes avant substitution des caractères spéciaux du shell set -v
set +v
set -o verbose
set +o verbose

Exemple :

Dans l'exemple suivant, un guillemet a été volontairement omis.

$ nl liste.sh
     1  #!/bin/bash
     2  set -n
     3  for fichier in `ls`
     4  do
     5          if [[ -f "$fichier ]]
     6          then
     7                  echo "$fichier"
     8          fi
     9  done
$ ./liste.sh
./liste.sh: line 7: Caractère de fin de fichier (EOF) prématuré lors de la recherche du « " » correspondant
./liste.sh: line 10: argument inattendu pour l'opérateur conditionnel à un argument
./liste.sh: line 10: Erreur de syntaxe : fin de fichier prématurée
$

$ nl liste.sh
     1  #!/bin/bash
     2  set -v
     3  for fichier in `ls`
     4  do
     5          if [[ -f "$fichier ]]
     6          then
     7                  echo "$fichier"
     8          fi
     9  done
$ ./liste.sh
for fichier in `ls`
do
        if [[ -f "$fichier ]]
        then
                echo "$fichier"
        fi
done
./liste.sh: line 7: Caractère de fin de fichier (EOF) prématuré lors de la recherche du « " » correspondant
./liste.sh: line 10: argument inattendu pour l'opérateur conditionnel à un argument
./liste.sh: line 10: Erreur de syntaxe : fin de fichier prématurée
$

Les structures de contrôle

if

La structure de controle if permet de réaliser des tests.
La commande située à droite du if est exécutée.
Si le code retour de la commande ($?) est égal à 0 (vrai), les commandes situées dans le bloc then sont exécutées.
Si le code de retour est supérieur à 0 (faux), ce sont les commandes situées dans le bloc else (optionnel) qui sont exécutées.
Dans le cas où le bloc else n'est pas spécifié, le shell continue à la première commande située sous le fi.

Les différentes syntaxes :

if, then, else, fi

if commande1
then
     commande2
     commande3
     ...
else
     commande4
     ...
fi

if, then, fi

if commande1
then
     commande2
     commande3
     ...
fi

if, then, elif, else, fi

if commande1
then
     commande2
     commande3
     ...
elif commande4
then
     commande5
     ...
else
     commande6
     ...
fi

Le mot clé fi permet de fermer la structure de controle if.
Le mot clé elif n'a pas de fermeture.

Autres syntaxes :

Le mot clé then peut être placé sur la même ligne que le if à condition de les séparer à l'aide d'un point virgule.

if commande1 ; then
     commande2
     commande3
     ...
fi

Plusieurs structures de controles if peuvent être imbriquées les unes dans les autres.

if commande1
then
     commande2
     ...
else
     if commande3
     then
          commande4
          ...
     else
          commande5
          ...
     fi
fi

Exemple :

Le script suivant vérifie si un argument est passé en paramètre et dans le cas contraire demande une saisie clavier à l'utilisateur.
Le script vérifie ensuite que le user saisi existe bien dans le fichier /etc/passwd.

$ nl user_passwd.sh
     1  #!/bin/bash
     2  if [[ $# -ne 1 ]]
     3  then
     4          echo -e "Saisir le nom d'un user : \c"
     5          read user
     6  else
     7          user=$1
     8  fi
     9  if grep -q "^$user:" /etc/passwd
    10  then
    11          echo "Le user $user existe"
    12          echo "Son UID est le : $(grep "^$user:" /etc/passwd | cut -d":" -f3)"
    13          echo "Son GID est le : $(grep "^$user:" /etc/passwd | cut -d":" -f4)"
    14  else
    15          echo "Le user $user n'existe pas !!!"
    16  fi
    17  exit 0
$ ./user_passwd.sh root
Le user root existe
Son UID est le : 0
Son GID est le : 0
$ ./user_passwd.sh
Saisir le nom d'un user : www-data
Le user www-data existe
Son UID est le : 33
Son GID est le : 33
$

Etiquettes: 

case

La structure de controle case permet elle aussi d'effectuer des tests.
Elle permet d'orienter la suite du programme en fonction d'un choix de différentes valeurs.
Quand il y a un nombre important de choix, la commande case est plus appropriée que la commande if.

Syntaxe :

case $variable in
modele1) commande1
     ...
     ;;
modele2) commande2
     ...
     ;;
modele3 | modele4 | modele5 ) commande3
     ...
     ;;
esac

Le shell compare la valeur de la variable aux différents modèle renseignés.
Lorsque la valeur correspond au modèle, les commandes faisant partie du bloc sont exécutées.
Les caractères ;; permettent de fermer le bloc et de mettre fin au case.
Le shell continue à la première commande située sous esac.

Il ne faut surtout pas oublier les caractères ;; car cela engendrera une erreur.

Rappel des caractères spéciaux :

Caractères spéciaux pour modèles de chaines de caractères Signification
Caractères spéciaux valables dans tous les shells :
* 0 à n caractères
? 1 caractère quelconque
[abc] 1 caractère parmis ceux inscrits entre les crochets
[!abc] 1 caractère ne faisant pas partie de ceux inscrits entre les crochets
Caractères spéciaux non valides en Bourne Shell.
En bash, il faut activer l'option extglob (shopt -s extglob)
?(expression) de 0 à 1 fois l'expression
*(expression) de 0 à n fois l'expression
+(expression) de 1 à n fois l'expression
@(expression) 1 fois l'expression
!(expression) 0 fois l'expression
?(expression1 | expression2 | ...)
*(expression1 | expression2 | ...)
+(expression1 | expression2 | ...)
@(expression1 | expression2 |...)
!(expression1 | expression2 | ...)
alternatives

Exemple :

Le script suivant permet de créer, modifier, visualiser et supprimer un fichier dans le répertoire d'exécution du script.
Il prend en argument un nom de fichier et affiche un menu.
Utilisation de case avec imbrication de if.

$ nl file.sh
     1  #!/bin/bash
     2  #set -x
     3  # Si le nombre d'arguments est different de 1 on quitte avec code 1
     4  if [[ $# -ne 1 ]]
     5  then
     6          echo "Nombre d'arguments incorrect"
     7          echo "Usage : $0 file"
     8          exit 1
     9  fi
    10  # On affiche le menu
    11  echo -e "1(Creer) "
    12  echo -e "2(Editer) "
    13  echo -e "3(Afficher) "
    14  echo -e "4(Supprimer)"
    15  echo -e "Votre choix : \c"
    16  # On recupere la valeur saisi
    17  read choix
    18  # Si la valeur saisi est differente de 1, 2, 3 ou 4 on quitte avec code 1
    19  if [[ "$choix" != [1-4] ]]
    20  then
    21          echo "Choix incorrect"
    22          exit 1
    23  fi
    24  # En fonction de la valeur saisi on execute les differentes actions
    25  case "$choix" in
    26  # Si choix = 1 --> creation
    27  1)      if [[ -e "$1" ]]
    28          then
    29                  if [[ -f "$1" ]]
    30                  then
    31                          echo "Fichier $1 deja existant"
    32                  elif [[ -d "$1" ]]
    33                  then
    34                          echo "$1 est un repertoire"
    35                  fi
    36                  exit 1
    37          else
    38                  touch "$1"
    39                  nano "$1"
    40          fi
    41  ;;
    42  # Si choix = 2 --> edition
    43  2)      if [[ -f "$1" ]]
    44          then
    45                  nano "$1"
    46          else
    47                  if [[ -d "$1" ]]
    48                  then
    49                          echo "$1 est un repertoire et ne peut etre edite"
    50                  else
    51                          echo "Fichier $1 inexistant"
    52                  fi
    53                  exit 1
    54          fi
    55  ;;
    56  # Si choix = 3 --> affichage
    57  3)      if [[ -f "$1" ]]
    58          then
    59                  more "$1"
    60          else
    61                  if [[ -d "$1" ]]
    62                  then
    63                          echo "$1 est un repertoire et ne peut etre visualise"
    64                  else
    65                          echo "Fichier $1 inexistant"
    66                  fi
    67                  exit 1
    68          fi
    69  ;;
    70  # Si choix = 4 --> suppression
    71  4)      if [[ -f "$1" ]]
    72          then
    73                  rm "$1"
    74          else
    75                  if [[ -d "$1" ]]
    76                  then
    77                          echo "$1 est un repertoire et ne peut etre supprime"
    78                  else
    79                          echo "Fichier $1 inexistant"
    80                  fi
    81                  exit 1
    82          fi
    83  ;;
    84  # Fin du case
    85  esac
    86  # Tout c'est bien deroule on quitte avec le code 0
    87  exit 0
$ ./file.sh test4
1(Creer)
2(Editer)
3(Afficher)
4(Supprimer)
Votre choix : 1
$

Etiquettes: 

Boucle for

Syntaxe :

La boucle for permet de traiter une liste de valeurs indiquée à droite du mot clé in.
A chaque tour de boucle, la variable var est initialisée avec une des valeurs de la liste.
Elles sont traitées dans l'ordre de leur énumération.

Liste de valeurs citée directement

for var in valeur1 valeur2 valeur3 ... valeurn
do
     commande
done

Liste de valeurs contenue dans une variable

for var in $variable
do
     commande
done

Liste de valeurs générée par substitution de commande

for var in `commande`
do
     commande
done

Liste de valeurs générée par substitution de caractères de génération de noms de fichiers

for var in *.ext
do
     commande
done

Liste par défaut : Arguments de la ligne de commande

for var
do
     commande
done

for var in $*
do
     commande
done

Avec incrémentation d'une variable

for (( var=valeurMin; var<=valeurMax; var++ ))
do
     commande
done

Exemple :

Un script compte à rebours

$ nl boucleFor01.sh
     1  #!/bin/bash
     2  for var in 10 9 8 7 6 5 4 3 2 1 0
     3  do
     4          echo "$var"
     5  done
     6  exit 0
$ ./boucleFor01.sh
10
9
8
7
6
5
...
$

Lister les fichiers d'un ou plusieurs dossiers

$ nl boucleFor02.sh
     1  #!/bin/bash
     2  if [[ $# -lt 1 ]]
     3  then
     4          echo "Nombre d'argument incorrect"
     5          echo "Utilisation $0 dossier1 dossier2 dossiern"
     6          exit 1
     7  fi
     8  for dossier in $*
     9  do
    10          if [[ -d $dossier ]]
    11          then
    12                  echo "Liste des fichiers du dossier $dossier"
    13                  for fichier in `ls $dossier`
    14                  do
    15                          echo "$fichier"
    16                  done
    17          fi
    18  done
    19  exit 0
$ ./boucleFor02.sh coucou 24902 25013 25031
Liste des fichiers du dossier coucou
test
Liste des fichiers du dossier 24902
fichier_0
fichier_1
fichier_2
fichier_3
Liste des fichiers du dossier 25013
fichier_0
fichier_1
fichier_2
Liste des fichiers du dossier 25031
fichier_0
fichier_1
fichier_2
fichier_3
$

En utilisant une variable incrémentée :

$ for (( i=0; i <= 10; i++ )); do echo $i; done
0
1
2
3
4
5
6
7
8
9
10
$

Idem avec la syntaxe suivante :

$ for i in {0..10}; do echo $i; done
0
1
2
3
4
5
6
7
8
9
10
$

Etiquettes: 

Boucle while

Syntaxe :

while commande1
do
     commande2
     ...
done

La boucle while permet d'exécuter les commandes présentes entre le do et le done tant que la commande1 placée à droite du while retourne un code vrai.

Exemple :

Le script suivant demande de saisir 53 et continue tant que c'est faux

$ nl boucleWhile01.sh
     1  #!/bin/bash
     2  nbr=0
     3  while ((nbr!=53))
     4  do
     5          echo -e "Saisir 53 : \c"
     6          read nbr
     7
     8  done
     9  exit 0
$ ./boucleWhile01.sh
Saisir 53 : rt
Saisir 53 : 54
Saisir 53 : R4
Saisir 53 : 53
$

Le script suivant affiche le compteur tant qu'il est inférieur à 10

$ nl boucleWhile02.sh
     1  #!/bin/bash
     2  cpt=0
     3  while ((cpt<10))
     4  do
     5          echo "Le compteur vaut : $cpt"
     6          ((cpt+=1))
     7  done
     8  exit 0
$ ./boucleWhile02.sh
Le compteur vaut : 0
Le compteur vaut : 1
Le compteur vaut : 2
Le compteur vaut : 3
Le compteur vaut : 4
Le compteur vaut : 5
Le compteur vaut : 6
Le compteur vaut : 7
Le compteur vaut : 8
Le compteur vaut : 9
$

Le script suivant effectue une somme des nombres saisis

$ nl boucleWhile03.sh
     1  #!/bin/bash
     2  somme=0
     3  echo "Saisir un nombre, ^d pour afficher la somme"
     4  while read nombre
     5  do
     6          if [[ $nombre != +([0-9]) ]]
     7          then
     8                  echo "$nombre n'est pas un nombre"
     9                  continue
    10          fi
    11          ((somme+=nombre))
    12  done
    13  echo "La somme est de : $somme"
    14  exit 0
$ ./boucleWhile03.sh
Saisir un nombre, ^d pour afficher la somme
56
32
89
9.6
9.6 n'est pas un nombre
g8
g8 n'est pas un nombre
54
La somme est de : 231
$

Le mot clé continue permet de remonter aussitôt à la boucle while sans exécuter la commande suivante

Attention aux boucles infinies

Ce script provoqe une boucle infinie car il manque l'incrémentation du compteur

$ nl boucleWhile04.sh
     1  #!/bin/bash
     2  cpt=0
     3  while ((cpt<10))
     4  do
     5          echo "Le compteur vaut : $cpt"
     6  done
     7  exit 0
$

Le shell propose également la commande interne : qui renvoie toujours vrai et permet donc de faire une boucle infinie avec un while.

$ nl boucleWhile05.sh
     1  #!/bin/bash
     2  while :
     3  do
     4          echo "Boucle infinie"
     5  done
     6  exit 0
$

En bash et ksh, la commande true propose exactement la même chose.

$ nl boucleWhile05.sh
     1  #!/bin/bash
     2  while true
     3  do
     4          echo "Boucle infinie"
     5  done
     6  exit 0
$

Etiquettes: 

until

Syntaxe :

until commande1
do
     commande2
     ...
done

A l'inverse de while, la commande until exécute les commandes situées entre le do et le done tant que la commande située à droite du until retourne un code faux.

Exemple :

Le script suivant boucle tant que le nombre saisi n'est pas égal à 53

$ nl boucleUntil01.sh
     1  #!/bin/bash
     2  nbr=0
     3  until ((nbr==53))
     4  do
     5          echo -e "Saisir 53 : \c"
     6          read nbr
     7  done
     8  exit 0
$ ./boucleUntil01.sh
Saisir 53 : 45
Saisir 53 : rt
Saisir 53 : fd
Saisir 53 : 53
$

Le script suivant permet, en tâche de fond, de surveiller un répertoire donné et d'informer l'utilisateur de l'arrivée d'un fichier attendu dans ce répertoire.
Pour plus de sécurité sur l'intégrité du fichier attendu, un fichier témoin devra être créé à la suite du fichier attendu puisque le principal contrôle se fera sur l'existence de ce fichier.

$ nl boucleUntil02.sh
     1    #!/bin/bash
     2    # Il doit y avoir au minimum 2 paramètres en arguments et un maximum de 3
     3    if [[ $# -lt 2 || $# -gt 3 ]]
     4    then
     5        echo "Utilisation : $0 repertoire fichier [ temoin ]"
     6        exit 1
     7    fi
     8    # Le premier argument doit être un répertoire
     9    if [[ ! -d $1 ]]
    10    then
    11        echo "$1 n'est pas un répertoire"
    12        exit 2
    13    fi
    14    # Nom du fichier témoin par défaut
    15    ficTemoin=${3:-temoin}
    16    # Exécution de la boucle en attendant l'arrivée du fichier témoin avec une pause toutes les 3 secondes
    17    until [[ -e $1/$ficTemoin ]]
    18    do
    19        sleep 3
    20    done
    21    # Vérification que le fichier attendu est bien présent
    22    if [[ ! -e $1/$2 ]]
    23    then
    24        echo "Le fichier témoin existe mais le fichier attendu est absent"
    25        exit 3
    26    fi
    27    # Sauvegarde du fichier attendu dans le HOME de l'utilisateur avec horodatage et suppression du fichier témoin
    28    date=$(date '+%Y%m%d_%H%M')
    29    newFichier=$2.$date
    30    mv $1/$2 $HOME/$newFichier
    31    rm $1/$ficTemoin
    32    # Envoi d'un mail à l'utilisateur
    33    mail $LOGNAME <<FIN
    34    Le fichier $HOME/$newFichier est bien arrivé.
    35    FIN
    36    echo "$0 : Vous avez reçu un message !!! "
    37    exit 0
$ ./boucleUntil02.sh /tmp monFichier &     # Lancement du script en arrière plan grâce à la commande &
[1] 2298
$ touch /tmp/monFichier                                   # Création du fichier attendu
$ touch /tmp/temoin                                           # Création du fichier témoin
$ ./boucleUntil02.sh : Vous avez reçu un message !!!     # Message généré par la ligne 36
 
[1]+  Done                    ./boucleUntil02.sh /tmp monFichier
$

il ne reste plus qu'à consulter sa boite mail pour lire le message envoyé par le script.

Etiquettes: 

break et continue

Les commandes break et continue peuvent s'utiliser à l'intérieur des boucles for, while, until et select.
La commande break permet de sortir d'une boucle.
La commande continue permet de remonter à la condition d'une boucle.

Syntaxe :

Quitter la boucle de premier niveau
break

Quitter la boucle de niveau n
break n

Remonter à la condition de la boucle de premier niveau
continue

Remonter à la condition de la boucle de niveau n
continue n

Exemple :

$ nl boucleWhile06.sh
     1    #!/bin/bash
     2    somme=0
     3    while true
     4    do
     5        echo "Saisir un nombre, ^d pour afficher la somme"
     6        if read nombre
     7        then
     8            if [[ $nombre != +([0-9]) ]]
     9            then
    10                echo "$nombre n'est pas un nombre"
    11                continue
    12            fi
    13            ((somme+=nombre))
    14        else
    15            break
    16        fi
    17    done
    18    echo "La somme est de : $somme"
    19    exit 0
$ ./boucleWhile06.sh
Saisir un nombre, ^d pour afficher la somme
23
Saisir un nombre, ^d pour afficher la somme
56
Saisir un nombre, ^d pour afficher la somme
54
Saisir un nombre, ^d pour afficher la somme
89
Saisir un nombre, ^d pour afficher la somme
La somme est de : 222
$

Etiquettes: 

Aspects avancés de la programmation shell

Comparatif des variables $* et $@

Utilisation de $* et de $@

Les variables $* et $@ contiennent la liste des arguments d'un script shell.
Lorsqu'elles ne sont pas entourées par des guillemets, elles sont équivalentes.

Exemple :

$ nl scr01.sh
     1  #!/bin/bash
     2  cpt=1
     3  echo "Utilisation de la variable \$*"
     4  for arg in $*
     5  do
     6          echo "Argument $cpt : $arg"
     7          ((cpt+=1))
     8  done
     9  cpt=1
    10  echo "Utilisation de la variable \$@"
    11  for arg in $@
    12  do
    13          echo "Argument $cpt : $arg"
    14          ((cpt+=1))
    15  done
    16  exit 0
$ ./scr01.sh a b "c d e" f
Utilisation de la variable $*
Argument 1 : a
Argument 2 : b
Argument 3 : c
Argument 4 : d
Argument 5 : e
Argument 6 : f
Utilisation de la variable $@
Argument 1 : a
Argument 2 : b
Argument 3 : c
Argument 4 : d
Argument 5 : e
Argument 6 : f
$

Interprétation :

  1. $* et $@ contiennent exactement la même liste d'arguments.
  2. Les guillemets protégeant les arguments ne sont pas pris en compte
  3. Ce sont les espaces qui délimitent les arguments

Utilisation de "$*"

Les guillemets autour de $* supprime la signification des espaces contenus dans $*.

Exemple :

$ nl scr02.sh
     1  #!/bin/bash
     2  cpt=1
     3  echo "Utilisation de la variable \"\$*\""
     4  for arg in "$*"
     5  do
     6          echo "Argument $cpt : $arg"
     7          ((cpt+=1))
     8  done
     9  exit 0
$ ./scr02.sh a b c "d e f" g
Utilisation de la variable "$*"
Argument 1 : a b c d e f g
$

Interprétation :

  1. Les guillemets entourant les arguments ne sont pas pris en compte.
  2. Tous les arguments sont considérés comme étant un seul argument.

Utilisation de "$@"

La variable $@ placée entre guillemets permet de conserver la protection des arguments par les guillemets.

Exemple :

$ nl scr03.sh
     1  #!/bin/bash
     2  cpt=1
     3  echo "Utilisation de la variable \"\$@\""
     4  for arg in "$@"
     5  do
     6          echo "Argument $cpt : $arg"
     7          ((cpt+=1))
     8  done
     9  exit 0
$ ./scr03.sh a b c "d e f" g
Utilisation de la variable "$@"
Argument 1 : a
Argument 2 : b
Argument 3 : c
Argument 4 : d e f
Argument 5 : g
$

Interprétation :

  1. Les espaces délimitent la liste des arguments.
  2. Les arguments placés entre guillemets sont considérés comme étant un seul argument

Substitution de variables

Les shells BASH et KSH offrent des fonctionnalités supplémentaires au niveau des substitution de variables.

Connaitre la longueur d'une chaine d'une variable :

Syntaxe :

${#variable}

Exemple :

$ variable="Une chaine de texte"
$ echo $variable
Une chaine de texte
$ echo "La chaine contient ${#variable} caractères"
La chaine contient 19 caractères
$

Manipuler des chaines de caractères :

Exemple avec la variable suivante :

$ variable="col1|col2|col3|col4|col5"
$ echo $variable
col1|col2|col3|col4|col5
$

Cette variable est constituée de 5 segments délimités par le caractère |

Le contenu de la variable n'est jamais modifié

Exclure le premier segment

Syntaxe :

${variable#modele}

modele est une chaine de caractère incluant les caractères spéciaux *, ?, [ ], ?(expression), +(expression), *(expression), @(expression), !(expression)

Rappel des expressions pour la substitution des noms de fichiers

Le caractère # signifie "Chaine la plus courte possible en partant de la gauche"

Exemple :

$ echo ${variable#*|}
col2|col3|col4|col5
$

Conserver le dernier segment

Syntaxe :

${variable##modele}

Les caractères ## signifient "Chaine la plus longue possible en partant de la gauche"

Exemple :

$ echo ${variable##*|}
col5
$

Exclure le dernier segment

Syntaxe :

${variable%modele}

Le caractère % signifie "Chaine la plus courte possible en partant de la droite"

Exemple :

$ echo ${variable%|*}
col1|col2|col3|col4
$

Conserver le premier segment

Syntaxe :

${variable%%modele}

Les caractères %% signifient "Chaine la plus longue possible en partant de la droite"

Exemple :

$ echo ${variable%%|*}
col1
$

En bash, ne pas oublier d'activer l'option extglob avec la commande shopt -s extglob afin de permettre au shell d'interpréter les modèles utilisant les expressions complexes.

Tableaux

Avec les shells récents, il est possible d'utiliser des tableaux à 1 dimension.
Les éléments du tableau commencent toujours à l'indice 0.

Ajouter un élément à un tableau :

Syntaxe :

tableau[indice]=valeur

Exemple :

$ tableau[0]=15
$ tableau[3]=20
$

Les indices non initialisés sont vides.

Afficher un élément d'un tableau :

Syntaxe :

${tableau[indice]}

Exemple :

$ echo ${tableau[3]}
20
$ echo ${tableau[0]}
15
$ echo ${tableau[2]}
 
$

L'indice 2 du tableau est vide.

Initialiser un tableau avec plusieurs valeurs :

Syntaxe :

tableau=(valeur1 valeur2 valeur3 ..... valeurn)

Exemple :

$ tableau=(02 40 35 68 98 45 52 03)
$ echo ${tableau[0]}
02
$ echo ${tableau[2]}
35
$

Toutes les précédentes valeurs contenues dans le tableaux sont effacées.

Afficher tous les éléments d'un tableau :

Syntaxe :

${tableau[*]}

Exemple :

$ echo ${tableau[*]}
02 40 35 68 98 45 52 03
$

Afficher le nombre d'éléments d'un tableau :

Syntaxe :

${#tableau[*]}

Exemple :

$ echo ${#tableau[*]}
8
$

Obtenir la longueur d'un élément d'un tableau :

Syntaxe :

${#tableau[indice]}

Exemple :

$ echo ${#tableau[0]}
2
$

Parcourir tous les éléments d'un tableau :

$ tab=("un" "deux" "trois")
$ for m in ${tab[@]}; do echo $m; done
un
deux
trois
$

Initialisation des paramètres positionnels avec set

La commande set utilisée sans option mais suivie d'arguments affecte ces derniers aux paramètres positionnels $1, $2, $3 ....., $*, $@ et $#.

Cela permet de manipuler plus facilement le résultat de diverses substitutions.

Exemple :

$ ls
fichier_0  fichier_2  fichier_4  fichier_6  fichier_8
fichier_1  fichier_3  fichier_5  fichier_7  fichier_9
$ set `ls`
$

La liste des fichiers obtenue avec la commande ls est maintenant affecté aux paramètres positionnels.

$ echo $#
10
$ echo $*
fichier_0 fichier_1 fichier_2 fichier_3 fichier_4 fichier_5 fichier_6 fichier_7 fichier_8 fichier_9
$ echo $1
fichier_0
$ echo $3
fichier_2
$ echo $9
fichier_8
$ echo ${10}
fichier_9
$

Les fonctions

Les fonctions permettent de regrouper et d'exécuter des commandes à différents endroits d'un script.

Cela permet de créer des fonctions personnalisées réutilisables.

Définition d'une fonction

Une fonction doit être définie au début d'un script, avant sa première utilisation.

Il existe 2 syntaxes permettant de définir une fonction.

Première syntaxe :

Ce sont les doubles parenthèses ( ) qui indique au shell la définition d'une fonction

Définition d'une fonction

maFonction () {
     commande1
     commande2
     .....
}

Appel d'une fonction

maFonction

Seconde syntaxe :

Le mot clé function remplace les doubles parenthèses ( )

Définition d'une fonction

function maFonction {
     commande1
     commande2
     .....
}

Appel d'une fonction

maFonction

Une fonction peut être appelée aussi bien à partir du programme principal qu'à partir d'une autre fonction.

Exemple :

$ nl fonction01.sh
     1  #!/bin/bash
 
     2  fctn01 () {
     3          echo "Fonction fctn01"
     4  }
 
     5  function fctn02 {
     6          echo "Fonction fctn02"
     7  }
 
     8  echo "Début du programme principal"
     9  echo "Appel de la fonction fctn01"
    10  fctn01
    11  echo "Appel de la fonction fctn02"
    12  fctn02
    13  echo "Fin du programme principal"
    14  exit 0
$ ./fonction01.sh
Début du programme principal
Appel de la fonction fctn01
Fonction fctn01
Appel de la fonction fctn02
Fonction fctn02
Fin du programme principal
$

Dès qu'une fonction est définie, celle-ci est considérée par le shell comme étant une commande interne.

Code de retour d'une fonction

Comme toutes commandes Linux, une fonction retourne également un code d'erreur.
Si le code erreur n'est pas spécifié, celui retourné par défaut correspond au code erreur de la dernière commande exécutée dans la fonction.
La commande return permet de retourner le code erreur de la fonction concernée. Ce code doit obligatoirement correspondre à un nombre compris entre 0 et 255.
Le code erreur retourné par la fonction est récupérable grâce à la variable $?.

Exemple :

Le script suivant test si l'utilisateur saisi existe sur le système.

$ nl fonction02.sh
     1  #!/bin/bash
 
     2  function pause {
     3          echo "Appuyer sur Entrée pour continuer"
     4          read x
     5  }
 
     6  function existUser {
     7          echo -e "Saisir le nom d'un utilisateur : \c"
     8          read user
     9          if grep -q "^$user:" /etc/passwd ; then
    10                  return 0
    11          fi
    12          return 1
    13  }
 
    14  while true
    15  do
    16          clear
    17          echo "- 1 - Savoir si un utilisateur existe"
    18          echo "- 2 - Connaitre l'UID d'un utilisateur"
    19          echo "- 3 - Fin"
    20          echo -e "Votre choix : \c"
    21          read choix
    22          case $choix in
    23                  1)      if existUser
    24                          then
    25                                  echo "L'utilisateur $user existe"
    26                          else
    27                                  echo "l'utilisateur $user n'existe pas"
    28                          fi
    29                          ;;
 
    30                  2)      echo "Option non disponible"
    31                          ;;
 
    32                  3)      exit 0
    33                          ;;
    34          esac
    35          pause
    36  done
$

Portée des variables

Dans un script shell, sans définition particulière, toutes les variables utilisées sont globales à tout le script.

Qu'une variable soit définie au niveau du programme principal ou d'une fonction, elle est accessible n'importe où dans le script.

Par exemple, dans le script fonction02.sh, la variable $user est initialisée dans la fonction existUser (ligne 8) et utilisée également au niveau du programme principal (ligne 25 et 27).

$ nl fonction02.sh
     1  #!/bin/bash
 
     2  function pause {
     3          echo "Appuyer sur Entrée pour continuer"
     4          read x
     5  }
 
     6  function existUser {
     7          echo -e "Saisir le nom d'un utilisateur : \c"
     8          read user
     9          if grep -q "^$user:" /etc/passwd ; then
    10                  return 0
    11          fi
    12          return 1
    13  }
 
    14  while true
    15  do
    16          clear
    17          echo "- 1 - Savoir si un utilisateur existe"
    18          echo "- 2 - Connaitre l'UID d'un utilisateur"
    19          echo "- 3 - Fin"
    20          echo -e "Votre choix : \c"
    21          read choix
    22          case $choix in
    23                  1)      if existUser
    24                          then
    25                                  echo "L'utilisateur $user existe"
    26                          else
    27                                  echo "l'utilisateur $user n'existe pas"
    28                          fi
    29                          ;;
 
    30                  2)      echo "Option non disponible"
    31                          ;;
 
    32                  3)      exit 0
    33                          ;;
    34          esac
    35          pause
    36  done
$

Définition de variables locales

La commande typeset permet de définir des variables locales à une fonction.

Syntaxe :

typeset variable
typeset variable=valeur

Exemple :

$ nl fonction03.sh
     1  #!/bin/bash
     2  function f1 {
     3          # var1 est une variable locale
     4          typeset var1
     5          echo "Dans la fonction f1 => var1 avant : $var1"
     6          var1=100
     7          echo "Dans la fonction f1 => var1 après : $var1"
     8          echo "Dans la fonction f1 => var2 avant : $var2"
     9          var2=200
    10          echo "Dans la fonction f1 => var2 après : $var2"
    11  }
    12  # var1 et var2 sont des variables globales
    13  var1=1
    14  var2=2
    15  echo "Dans le programme principal => var1 avant appel f1 : $var1"
    16  echo "Dans le programme principal => var2 avant appel f1 : $var2"
    17  f1
    18  echo "Dans le programme principal => var1 après appel f1 : $var1"
    19  echo "Dans le programme principal => var2 après appel f1 : $var2"
    20  exit 0
$ ./fonction03.sh
Dans le programme principal => var1 avant appel f1 : 1
Dans le programme principal => var2 avant appel f1 : 2
Dans la fonction f1 => var1 avant :
Dans la fonction f1 => var1 après : 100
Dans la fonction f1 => var2 avant : 2
Dans la fonction f1 => var2 après : 200
Dans le programme principal => var1 après appel f1 : 1
Dans le programme principal => var2 après appel f1 : 200
$

2 variables globales var1 et var2 sont définies et initialisées ligne 13 et 14.
1 variable locale var1 est définie dans la fonction ligne 4 et initialisée à 100 ligne 6.
Après exécution de la fonction f1, la variable globale var1 a conservée sa valeur (1) alors que la variable globale var2 a été modifiée.

Passage d'arguments

Dans un script shell, il est tout à fait possible de passer des arguments à une fonction étant donné qu'une fonction est reconnue par le shell comme étant une commande à part entière.

Ces arguments sont récupérables dans les fonctions grâce aux variables spéciales $1, $2, $3, ....., ${10} ......$*, $@ et $#. Ces variables sont aussi locales aux fonctions.

Par contre, la variable $0 contient toujours le nom du script.

Exemple :

$ nl fonction04.sh
     1  #!/bin/bash
 
     2  function f1 {
     3          echo "Arguments de la fonction f1 :"
     4          echo "\$0 => $0"
     5          echo "\$1 => $1"
     6          echo "\$2 => $2"
     7          echo "\$3 => $3"
     8          echo "\$* => $*"
     9          echo "\$# => $#"
    10  }
 
    11  function f2 {
    12          echo "Arguments de la fonction f2 :"
    13          echo "\$0 => $0"
    14          echo "\$1 => $1"
    15          echo "\$2 => $2"
    16          echo "\$3 => $3"
    17          echo "\$* => $*"
    18          echo "\$# => $#"
    19  }
 
    20  function f3 {
    21          echo "Arguments de la fonction f3 :"
    22          echo "\$0 => $0"
    23          echo "\$1 => $1"
    24          echo "\$2 => $2"
    25          echo "\$3 => $3"
    26          echo "\$* => $*"
    27          echo "\$# => $#"
    28  }
 
    29  echo "Arguments du programme principal :"
    30  echo "\$0 => $0"
    31  echo "\$1 => $1"
    32  echo "\$2 => $2"
    33  echo "\$3 => $3"
    34  echo "\$* => $*"
    35  echo "\$# => $#"
 
    36  # Appel de la fonction f1 avec 3 arguments
    37  f1 a b c
 
    38  # Appel de la fonction f2 avec 3 arguments
    39  f2 file.c 2000 500
 
    40  # Appel de la fonction f3 avec 2 arguments provenant du programme principal
    41  f3 $2 $3
 
    42  exit 0
$

Appel du script fonction04.sh avec 3 arguments :

$ ./fonction04.sh arg1 arg2 arg3
Arguments du programme principal :
$0 => ./fonction04.sh
$1 => arg1
$2 => arg2
$3 => arg3
$* => arg1 arg2 arg3
$# => 3
Arguments de la fonction f1 :
$0 => ./fonction04.sh
$1 => a
$2 => b
$3 => c
$* => a b c
$# => 3
Arguments de la fonction f2 :
$0 => ./fonction04.sh
$1 => file.c
$2 => 2000
$3 => 500
$* => file.c 2000 500
$# => 3
Arguments de la fonction f3 :
$0 => ./fonction04.sh
$1 => arg2
$2 => arg3
$3 =>
$* => arg2 arg3
$# => 2
$

Exploiter l'affichage d'une fonction

Comme n'importe quelle commande renvoyant un résultat, une fonction peut également être placée à l'intérieur de caractères de substitution de commande `` ou $( ).

Exemple :

$ nl fonction05.sh
     1  #!/bin/bash
 
     2  function getUid {
     3          grep "^$1:" /etc/passwd | cut -d':' -f3
     4  }
 
     5  # Initialisation de la variable globale uid
     6  uid=""
 
     7  # Appel de la fonction getUid avec l'argument du programme principal
     8  # Juste pour l'affichage
     9  getUid $1
 
    10  # Affectation du résultat de la fonction getUid à la variable uid
    11  uid=$(getUid $1)
 
    12  if [[ $uid != "" ]]
    13  then
    14          echo "L'utilisateur $1 a pour UID : $uid"
    15  else
    16          echo "L'utilisateur $1 n'existe pas"
    17  fi
 
    18  exit 0
$ ./fonction05.sh root
0
L'utilisateur root a pour UID : 0
$

Exemple complet

Exemple d'un script reprenant toutes les commandes et fonctions vues précédement

$ nl fonction06.sh
     1  #!/bin/bash
 
     2  # Pour faire une pause
     3  function pause {
     4          echo "Appuyer sur Entrée pour continuer"
     5          read x
     6  }
 
     7  # Pour savoir si un utilisateur existe
     8  function existUser {
     9          grep -qi "^$1:" /etc/passwd && return 0
    10          return 1
    11  }
 
    12  # Pour connaitre l'uid de l'utilisateur
    13  function getUid {
    14          grep -i "^$1:" /etc/passwd | cut -d':' -f3
    15  }
 
    16  # Initialisation des variables globales
    17  uid=""
    18  user=""
    19  choix=""
 
    20  while true
    21  do
    22          clear
    23          echo "- 1 - Savoir si un utilisateur existe"
    24          echo "- 2 - Connaitre l'UID d'un utilisateur"
    25          echo "- 3 - Fin"
    26          echo -e "Votre choix : \c"
    27          read choix
 
    28          if [[ $choix = @(1|2) ]] ; then
    29                  echo -e "Saisir le nom d'un utilisateur : \c"
    30                  read user
    31          fi
 
    32          case $choix in
 
    33                  1)      if existUser $user ; then
    34                                  echo "L'utilisateur $user existe"
    35                          else
    36                                  echo "l'utilisateur $user n'existe pas"
    37                          fi
    38                          ;;
 
    39                  2)      if existUser $user ; then
    40                                  uid=$(getUid $user)
    41                                  echo "l'UID de l'utilisateur $user est : $uid"
    42                          else
    43                                  echo "L'utilisateur $user n'existe pas"
    44                          fi
    45                          ;;
 
    46                  3)      exit 0
    47                          ;;
    48          esac
    49          pause
    50  done
$

Commandes d'affichage

La commande print

En KSH, la commande print apporte des fonctionnalités supplémentaires à la commande echo.

Exemple :

Utilisation simple

$ print coucou
coucou
$

Supprimer le saut de ligne

$ print -n coucou
coucou$

Afficher des arguments commançant par le caractère "-"

$ print - "-i : Option invalide"
-i : Option invalide
$

Ecrire sur un descripteur particulier

$ print -u2 "Message d'erreur"
Message d'erreur
$

Comparaison avec la commande echo

$ echo "Message d'erreur" 1>&2
Message d'erreur
$

 

La commande printf

En BASH, cette commande est identique à celle du langage C.
Elle permet de formater les affichages.

Syntaxe :

printf chaine expr1 expr2 ..... exprn

chaine représente la chaîne qui sera affichée à l'écran.
Elle peut contenir des formats qui seront substitués par la valeur des expressions citées à sa suite.
Il doit y avoir autant de formats que d'expressions.

Exemple de formats utilisés.

%20s Affichage d'une chaine (string) sur 20 positions avec cadrage à droite
%-20s Affichage d'une chaine (string) sur 20 positions avec cadrage à gauche
%3d Affichage d'un entier (décimal) sur 3 positions avec cadrage à droite
%03d Affichage d'un entier (décimal) sur 3 positions avec cadrage à droite et complété avec des 0 à gauche
%-3d Affichage d'un entier (décimal) sur 3 positions avec cadrage à gauche
%+3d Affichage d'un entier (décimal) sur 3 positions avec cadrage à droite et affichage systématique du signe (un nombre négatif est toujours affiché avec son signe)
%10.2f Affichage d'un nombre flottant sur 10 positions dont 2 décimales
%+010.2f Affichage d'un nombre flottant sur 10 positions dont 2 décimales, complété par des 0 à gauche, avec cadrage à droite et affichage systématique du signe

Exemple :

$ article="Livres"
$ quantite=3
$ prix=3,5
$ printf "%-20s***%03d***%+10.2f\n" $article $quantite $prix
Livres              ***003***     +3,50
$

En utilisant un tableau

$ liste=(livre 10 3,5 cd 5 10,65 dvd 7 19,70 bd 80 5,25)
$ printf "%-20s***%03d***%+10.2f\n" ${liste[*]}
livre               ***010***     +3,50
cd                  ***005***    +10,65
dvd                 ***007***    +19,70
bd                  ***080***     +5,25
$

Gestion des entrées / sorties d'un script

Redirection des entrées/sorties standard

La commande interne exec permet de manipuler les descripteurs de fichier du shell courant.
Utilisée à l'intérieur d'un script, elle permet de rediriger de manière globale les entrées/sorties de celui-ci.

Rediriger l'entrée standard d'un script :

exec 0< fichier1

Toutes les commandes du script placées après cette directive et qui lisent leur entrée standard vont extraire leurs données à partir de fichier1.
Il n'y aura donc pas d'interaction avec le clavier.

Rediriger la sortie standard et la sortie d'erreur standard d'un script :

exec 1> fichier1 2> fichier2

Toutes les commandes du script placées après cette directive et qui écrivent sur leur sortie standard enverront leurs résultat dans fichier1.
Celles qui écrivent sur leur sortie d'erreur standard enverront leurs erreurs dans fichier2.

Rediriger la sortie standard et la sortie d'erreur standard d'un script dans un même fichier :

exec 1> fichier1 2>&1

Toutes les commandes du script placées après cette directive enverront leurs résultats et leurs erreurs dans fichier1

Premier exemple :

Redirection de la sortie standard vers /tmp/fichier1.log et redirection de la sortie d'erreur standard vers /tmp/fichier2.log.

$ nl test.sh
     1  #!/bin/bash
     2  exec 1> /tmp/fichier1.log 2> /tmp/fichier2.log
     3  echo "Début du traitement : $(date)"
     4  ls
     5  cp *.zzz /tmp
     6  rm *.zzz
     7  sleep 5
     8  echo "Fin du traitement : $(date)"
     9  exit 0
$

Exécution du script.

$ ./test.sh
$

Affichage du fichier /tmp/fichier1.log

$ nl /tmp/fichier1.log
     1  Début du traitement : lundi 12 décembre 2011, 08:23:33 (UTC+0100)
     2  1coucou
     3  24902
     4  25013
     5  25031
     6  25043
     7  boucleFor01.sh
     8  boucleFor02.sh
     9  boucleUntil01.sh
     .....
    87  Fin du traitement : lundi 12 décembre 2011, 08:23:38 (UTC+0100)
$

Affichage du fichier /tmp/fichier2.log (les fichiers *.zzz n'exsistant pas, 2 erreurs sont générées)

$ nl /tmp/fichier2.log
     1  cp: impossible d'évaluer «*.zzz»: Aucun fichier ou dossier de ce type
     2  rm: impossible de supprimer «*.zzz»: Aucun fichier ou dossier de ce type
$

Deuxième exemple :

Redirection de la sortie standard et de la sortie d'erreur standard vers le fichier /tmp/fichier3.log

$ nl ./test2.sh
     1  #!/bin/bash
     2  exec 1> /tmp/fichier3.log 2>&1
     3  echo "Début du traitement : $(date)"
     4  ls
     5  cp *.zzz /tmp
     6  rm *.zzz
     7  sleep 5
     8  echo "Fin du traitement : $(date)"
     9  exit 0
$

Exécution du script

$ ./test2.sh
$

Affichage du fichier /tmp/fichier3.log

$ nl /tmp/fichier3.log
     1  Début du traitement : lundi 12 décembre 2011, 12:56:35 (UTC+0100)
     2  1coucou
     3  24902
     4  25013
     5  25031
     6  25043
     .....
    88  cp: impossible d'évaluer «*.zzz»: Aucun fichier ou dossier de ce type
    89  rm: impossible de supprimer «*.zzz»: Aucun fichier ou dossier de ce type
    90  Fin du traitement : lundi 12 décembre 2011, 12:56:40 (UTC+0100)
$

Troisième exemple :

Redirection de l'entrée standard

$ nl test3.sh
     1  #!/bin/bash
     2  exec 0< /etc/passwd
     3  cpt=1
     4  while read ligne
     5  do
     6          echo "Lecture de la ligne $cpt"
     7          echo $ligne
     8          ((cpt+=1))
     9  done
    10  exit 0
$

Exécution du script

$ ./test3.sh
Lecture de la ligne 1
root:x:0:0:root:/root:/bin/bash
Lecture de la ligne 2
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
Lecture de la ligne 3
bin:x:2:2:bin:/bin:/bin/sh
Lecture de la ligne 4
sys:x:3:3:sys:/dev:/bin/sh
Lecture de la ligne 5
sync:x:4:65534:sync:/bin:/bin/sync
Lecture de la ligne 6
games:x:5:60:games:/usr/games:/bin/sh
Lecture de la ligne 7
man:x:6:12:man:/var/cache/man:/bin/sh
.....
$

Gestion de fichiers

Les shells récents apportent une fonctionnalité supplémentaire.
La possibilité d'ouvrir et de manipuler des fichiers en utilisant des descripteurs compris entre 3 et 9 (en supplément de 0, 1 et 2).
L'avantage est de pouvoir manipuler des fichiers tout en conservant les descripteurs 0, 1 et 2 connectés sur le terminal.

Ouverture de fichier :

En lecture

exec  desc <fichier

En écriture

exec  desc >fichier

Lecture à partir d'un fichier :

read variable1 variable2 ..... variablen <&desc

ou

read -udesc variable1 variable2 ..... variablen

Ecriture dans un fichier :

echo variable1 variable2 ..... variablen >&desc

ou

print -udesc variable1 variable2 ..... variablen

Fermeture d'un fichier :

Syntaxe :

Ouvert en lecture

exec  desc <&-

Ouvert en écriture

exec  desc >&-

Exemple :

$ nl test4.sh
     1  #!/bin/bash
     2  # Ouverture du fichier /etc/passwd en lecture sous le descripteur 3
     3  # et du fichier /tmp/resultat.log en écriture sous le descripteur 4
     4  exec 3</etc/passwd 4>/tmp/resultat.log
     5  cpt=1
     6  # Lecture ligne par ligne du fichier /etc/passwd
     7  # correspondant au descripteur 3
     8  while read -u3 ligne
     9  do
    10          # Ecriture des données dans le fichier /tmp/resultat.log
    11          # correspondant au descripteur 4
    12          echo "Ecriture de la ligne $cpt" >&4
    13          echo $ligne >&4
    14          ((cpt+=1))
    15  done
    16  # Fermeture du fichier /etc/passwd correspondant au descripteur 3
    17  exec 3<&-
    18  # Fermeture du fichier /tmp/resultat.log correspondant au descripteur 4
    19  exec 4>&-
    20  exit 0
$

Résultat :

$ ./test4.sh
$ nl /tmp/resultat.log
     1  Ecriture de la ligne 1
     2  root:x:0:0:root:/root:/bin/bash
     3  Ecriture de la ligne 2
     4  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     5  Ecriture de la ligne 3
     6  bin:x:2:2:bin:/bin:/bin/sh
     7  Ecriture de la ligne 4
     8  sys:x:3:3:sys:/dev:/bin/sh
     9  Ecriture de la ligne 5
    10  sync:x:4:65534:sync:/bin:/bin/sync
    11  Ecriture de la ligne 6
    12  games:x:5:60:games:/usr/games:/bin/sh
    13  Ecriture de la ligne 7
    14  man:x:6:12:man:/var/cache/man:/bin/sh
    .....
$

Traitement d'un fichier

Les diffétrentes façons d'exploiter un fichier

Rediriger l'exécution du script

Les redirections peuvent également être faites au moment de l'exécution du script.

Exemple :

Avec le script suivant

$ nl test5.sh
     1  #!/bin/bash
     2  cpt=1
     3  # Lecture ligne par ligne du fichier passé en paramètre
     4  # ou lecture de la saisie clavier si pas de fichier en paramètre
     5  while read ligne
     6  do
     7          # Ecriture des données dans le fichier passé en paramètre
     8          # ou affichage à l'écran si pas de fichier en paramètre
     9          echo "Ecriture de la ligne $cpt"
    10         echo $ligne
    11         ((cpt+=1))
    12  done
    13  exit 0
$

Exécution du script sans paramètre

$ ./test5.sh
saisie 1     # saisie
Ecriture de la ligne 1
saisie 1
saisie 2     # saisie
Ecriture de la ligne 2
saisie 2
saisie 3     # saisie
Ecriture de la ligne 3
saisie 3
^d
$

Exécution du script avec paramètre en entrée

$ ./test5.sh < /etc/passwd
Ecriture de la ligne 1
root:x:0:0:root:/root:/bin/bash
Ecriture de la ligne 2
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
Ecriture de la ligne 3
bin:x:2:2:bin:/bin:/bin/sh
Ecriture de la ligne 4
sys:x:3:3:sys:/dev:/bin/sh
Ecriture de la ligne 5
sync:x:4:65534:sync:/bin:/bin/sync
Ecriture de la ligne 6
games:x:5:60:games:/usr/games:/bin/sh
Ecriture de la ligne 7
man:x:6:12:man:/var/cache/man:/bin/sh
.....
$

Exécution du script avec paramètres en entrée et en sortie

$ ./test5.sh < /etc/passwd > /tmp/resultat.log
$ nl /tmp/resultat.log
     1  Ecriture de la ligne 1
     2  root:x:0:0:root:/root:/bin/bash
     3  Ecriture de la ligne 2
     4  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     5  Ecriture de la ligne 3
     6  bin:x:2:2:bin:/bin:/bin/sh
     7  Ecriture de la ligne 4
     8  sys:x:3:3:sys:/dev:/bin/sh
     9  Ecriture de la ligne 5
    10  sync:x:4:65534:sync:/bin:/bin/sync
    11  Ecriture de la ligne 6
    12  games:x:5:60:games:/usr/games:/bin/sh
    13  Ecriture de la ligne 7
    14  man:x:6:12:man:/var/cache/man:/bin/sh
     .....
$

Redirections internes au script

Les redirections d'entrée (0) et de sortie (1) standards peuvent également être faite à l'intérieur du script.

Exemple :

Avec le script suivant

$ nl test6.sh
     1  #!/bin/bash
     2  exec </etc/passwd >/tmp/resultat.log
     3  cpt=1
     4  while read ligne
     5  do
     6          echo "Ecriture de la ligne $cpt"
     7          echo $ligne
     8          ((cpt+=1))
     9  done
    10  exit 0
$

Exécution du script et affichage du résultat

$ ./test6.sh
$ nl /tmp/resultat.log
     1  Ecriture de la ligne 1
     2  root:x:0:0:root:/root:/bin/bash
     3  Ecriture de la ligne 2
     4  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     5  Ecriture de la ligne 3
     6  bin:x:2:2:bin:/bin:/bin/sh
     7  Ecriture de la ligne 4
     8  sys:x:3:3:sys:/dev:/bin/sh
     9  Ecriture de la ligne 5
    10  sync:x:4:65534:sync:/bin:/bin/sync
    11  Ecriture de la ligne 6
    12  games:x:5:60:games:/usr/games:/bin/sh
    13  Ecriture de la ligne 7
    14  man:x:6:12:man:/var/cache/man:/bin/sh
     .....
$

Redirection d'un bloc

Il est également possible de rediriger uniquement les commandes situées à l'intérieur d'une structure de contrôle.
Les redirections doivent être écrites derrière le mot clé qui ferme la structure de contrôle.
A l'exécution, elles sont mises en place avant le traitement de la structure de contrôle.*

Exemple :

Dans le script suivant, seules les commandes situées à l'intérieur de la boucle while seront redirigées.

$ nl test7.sh
     1  #!/bin/bash
     2  echo "Lancement du script"
     3  cpt=1
     4  while read ligne
     5  do
     6          echo "Ecriture de la ligne $cpt"
     7          echo $ligne
     8          ((cpt+=1))
     9  done </etc/passwd >/tmp/resultat.log
    10  exit 0
$ ./test7.sh
Lancement du script
$ nl /tmp/resultat.log
     1  Ecriture de la ligne 1
     2  root:x:0:0:root:/root:/bin/bash
     3  Ecriture de la ligne 2
     4  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     5  Ecriture de la ligne 3
     6  bin:x:2:2:bin:/bin:/bin/sh
$

Rediriger un bloc avec des fichiers ouverts en amont

$ nl test8.sh
     1  #!/bin/bash
     2  exec 3</etc/passwd 4>/tmp/resultat.log
     3  echo "Lancement du script"
     4  cpt=1
     5  while read ligne
     6  do
     7          # Ecriture des données dans le fichier /tmp/resultat.log
     8          # correspondant au descripteur 4
     9          echo "Ecriture de la ligne $cpt"
    10          echo $ligne
    11          ((cpt+=1))
    12  done <&3 >&4
    13  echo "Fin du script"
    14  exec 3<&-
    15  exec 4>&-
    16  exit 0
$ ./test8.sh
Lancement du script
Fin du script
$ nl /tmp/resultat.log
     1  Ecriture de la ligne 1
     2  root:x:0:0:root:/root:/bin/bash
     3  Ecriture de la ligne 2
     4  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     5  Ecriture de la ligne 3
     6  bin:x:2:2:bin:/bin:/bin/sh
$

Découper une ligne en champs

Si les lignes du fichier à traiter sont structurées en champs, il est très facile de récupérer chacun de ceux ci dans une variable.
Pour cela, il faut modifier la valeur de la variable IFS.

Exemple :

Le script suivant génère, à partir du fichier /etc/passwd, un affichage à l'écran du username suivi de son uid.
La variable IFS est sauvegardée (ligne 13) afin d'être restaurée (ligne 19) et son contenu est remplacé par ":" (ligne 14).
":" étant le caractère séparant les champs du fichier /etc/passwd.
La commande read reçoit 7 variables en argument (ligne 15). Autant de variables que de champs dans le fichier /etc/passwd.
Elle découpe donc la ligne lue (ligne 18) en champs en utilisant le caractère ":" comme séparateur.
La ligne est donc automatiquement découpée et les valeurs récupérables via les variables indiquées.

$ nl test9.sh
     1  #!/bin/bash
     2  if (( $# != 1 ))
     3  then
     4          echo "Mauvais nombre d'arguments"
     5          echo "Usage : $0 fichier"
     6          exit 1
     7  fi
     8  if [[ ( ! -f "$1" ) || ( ! -r "$1" ) ]]
     9  then
    10          echo "$1 n'est pas un fichier ordinaire ou n'est pas accessible en lecture"
    11          exit 2
    12  fi
    13  oldIFS="$IFS"
    14  IFS=":"
    15  while read username password uid gid nomComplet home shell
    16  do
    17          echo "$username ==> $uid"
    18  done < $1
    19  IFS="$oldIFS"
    20  exit 0
$ ./test9.sh /etc/passwd
root ==> 0
daemon ==> 1
bin ==> 2
sys ==> 3
$

Ce script peut très bien être dirigé dans un tube et exploité avec la commande grep

$ ./test9.sh /etc/passwd | grep "root"
root ==> 0
$

La commande eval

Syntaxe :

eval expr1 expr2 ..... exprn

La commande eval permet de faire subir à une ligne de commande une double évaluation.

Exemple :

Initialisation de la variable nom

$ nom=toto

Initialisation de la variable var avec le nom de la variable définie ci dessus

$ var=nom

Affichage des valeurs des variables

$ echo $nom
toto
$ echo $var
nom
$ echo \$$var
$nom
$

Pour obtenir $var=toto, il faut obligatoirement utiliser la commande eval de cette manière

$ eval echo \$$var
toto
$

La commande eval évalue en premier la variable $var, le premier $ étant protégé par un anti-slash

==> eval echo \$nom

puis évalue le résultat de la première évaluation, c'est à dire $nom (suppression de l'anti-slash)

==> eval echo $nom

Initialisation de la variable var2

$ var2=\$$var
$ echo $var2
$nom
$ eval var2=\$$var
$ echo $var2
toto
$

Gestion des signaux

Il est possible de modifier le comportement des signaux envoyés au shell en utilisant la commande trap.

Principaux signaux

Liste des signaux :

$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
$

Détail des signaux :

  1. HUP    hangup : envoie un signal de réinitialisation
  2. INT    Interruption
  3. QUIT    Core dump
  4. ILL    Le programme tente d'exécuter du code malformé, inconnu ou avec de mauvais privilèges
  5. TRAP    Le programme envoie un signal au débugger (message capturé)
  6. IOT    idem SIGABRT : interruption du programme
  7. EMT    emulator trap : un programme émulé ou virtualisé a posé problème
  8. FPE    floating-point exception : le programme a réalisé une opération arithmétique erronée
  9. KILL    arrête le programme immédiatement
  10. BUS    Le programme a causé une erreur de bus
  11. SEGV    Segmentation fault : le programme fait référence à un mauvais emplacement de mémoire
  12. SYS    Un mauvais argument est passé en paramètre
  13. PIPE    Un programme tente d'écrire dans un pipe sans processus connecté à l'autre bout
  14. ALRM    La limite de temps est dépassée
  15. TERM    Envoie un signal au programme pour le terminer
  16. USR1/USR2    Envoie un signal dans des conditions définies par un utilisateur
  17. CHLD/CLD    child : signal envoyé par un programme lorsqu'un processus fils est achevé
  18. PWR    power : le système subit un problème d'alimentation
  19. VTALRM    virtual alarm : signal envoyé lorsque le temps limite a été dépassé
  20. PROF    profiler : signal envoyé lorsqu'un timer a expiré
  21. POLL    polling : un problème est survenu lors d'un événement I/O asynchrone
  22. WINCH    window [size] change : signal envoyé au programme lorsque la fenêtre de contrôle change de taille
  23. STOP    signal demandant au programme de se suspendre
  24. TSTP    tty stop : signal envoyé au programme lorsqu'un terminal suspend ses requêtes
  25. CONT    Redémarre un programme suspendu par STOP
  26. TTIN    Le programme tente de lire tty alors qu'il est en arrière-plan
  27. TTOU    Le programme tente d'écrire sur tty alors qu'il est en arrière-plan
  28. URG    Un socket a une donnée urgente à lire
  29. LOST    Le verrou d'un fichier a été perdu
  30. XCPU    Le programme a utilisé le CPU prend trop longtemps
  31. XFSZ    Le fichier a dépassé la taille maximale autorisée
  32. RTMIN/RTMIN+n    real-time minimum : signaux définis par l'application
  33. RTMAX/RTMAX-n    real-time maximum : signaux définis par l'application

Dans les commandes, les signaux peuvent être exprimés sous forme numérique ou symbolique.
Les signaux HUP, INT, TERM et KILL possèdent la même valeur numérique sur toutes les plates-formes Unix, ce qui n'est pas le cas de tous les signaux.
Il est donc préférable d'utiliser la forme symbolique.

Le signal INT est généré à partir du clavier.
Il est utilisé pour tuer le processus qui tourne en avant plan.
Pour connaitre la saisie clavier correspondant au signal INT, voir le paramètre intr de la commande stty -a.

$ stty -a
speed 38400 baud; rows 36; columns 134; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S;
susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
$

Ignorer un signal

Syntaxe :

trap ' ' sig1 sig2

Exemple :

Le shell courant correspond au PID 30819

$ echo $$
30819

Modification des signaux HUP et TERM

$ trap '' HUP TERM

Envoi des signaux HUP et TERM

$ kill -HUP 30819
$ kill -TERM 30819

Les signaux sont ignorés et le processus est toujours actif

$ echo $$
30819
$

Modifier le traitement associé à un signal

Syntaxe :

trap 'cmd1 ; cmd2 ; cmd3 ; ..... ; cmdn' sig1 sig2

Exemple :

Le shell courant correspond au PID 23217

$ ps
  PID TTY          TIME CMD
22109 pts/0    00:00:00 bash
23217 pts/0    00:00:00 bash
23465 pts/0    00:00:00 ps
$ echo $$
23217

Modification du traitement associé au signal ^C (ctrl+c)
On demande au shell d'afficher le message "Signal INT reçu" après avoir appuyer sur ^C (ctrl+c)

$ trap 'echo "Signal INT reçu" ; exit 1' INT
$ ^C     # Saisie
$ Signal INT reçu

Le shell courant correspondant au PID 23217 n'existe plus

$ ps
  PID TTY          TIME CMD
22109 pts/0    00:00:00 bash
24229 pts/0    00:00:00 ps
$ echo $$
22109
$

Repositionner le traitement par défaut du shell vis-à-vis d'un signal

Syntaxe :

trap - sig1 sig2 ..... sign

Exemple :

Création du fichier /tmp/fichier

$ > /tmp/fichier
$ ls /tmp/fichier
/tmp/fichier

Modification du traitement associé au signal INT et TERM
On demande au shell de supprimer le fichier "/tmp/fichier" après avoir appuyer sur ^C (ctrl+c)

$ trap 'rm -f /tmp/fichier' INT TERM

Le shell courant correspond au PID 22109

$ echo $$
22109

Envoi du signal INT "^C" (ctrl+c)

$ ^C

Le fichier a bien été suprimé

$ ls /tmp/fichier
ls: impossible d'accéder à /tmp/fichier: Aucun fichier ou dossier de ce type

Création d'un nouveau fichier /tmp/fichier

$ > /tmp/fichier
$ ls /tmp/fichier
/tmp/fichier

Traitement associé au signal INT et TERM remis par défaut

$ trap - INT TERM

Le shell courant correspond au PID 22109

$ echo $$
22109

Envoi du signal INT "^C" (ctrl+c)

$ ^C

Le fichier n'a pas été supprimé

$ ls /tmp/fichier
/tmp/fichier
$

Utiliser trap à partir d'un script shell

L'utilisation de trap dans un script shell va permettre de gérer des actions en fonctions de différents signaux reçus.

Exemple :

Dans le script suivant, à la réception d'un signal HUP INT ou TERM, la fonction "fin" est appelée et le fichier $fileTmp est supprimé.

$ nl signaux.sh
     1  #!/bin/bash
     2
     3  # Nom du fichier temporaire
     4  fileTmp=/tmp/fileTemp
     5
     6  # Fonction appelée lors de la réception d'un signal HUP INT TERM
     7  function fin {
     8          echo -e "\nSuppression du fichier $fileTmp"
     9          echo "Fin du script"
    10          rm -f $fileTmp
    11          ls $fileTmp
    12          exit 1
    13  }
    14
    15  # Paramétrage de la fonction "fin" à la réception d'un signal HUP INT TERM
    16  trap fin HUP INT TERM
    17
    18  # Création du fichier temporaire
    19  > $fileTmp
    20
    21  echo "Lancement du script"
    22  # Vérification de la création du fichier temporaire
    23  ls $fileTmp
    24  sleep 100
    25  echo "Arrêt du script"
    26  exit 0
$

Exécution du script :

$ ./signaux.sh
Lancement du script
/tmp/fileTemp
^C     # Envoi du signal
Suppression du fichier /tmp/fileTemp
Fin du script
ls: impossible d'accéder à /tmp/fileTemp: Aucun fichier ou dossier de ce type
$

Gestion de menu avec select

Syntaxe :

select var in item1 item2 item3 ..... itemn
do
     commandes
done

La commande interne select est une structure de contrôle de type boucle qui permet d'afficher de manière cyclique un menu.
La liste des items sera affichée à l'écran à chaque tour de boucle.
Les items sont indicés automatiquement.
La variable var sera initialisée avec l'item correspondant au choix de l'utilisateur.

Cette commande utilise également deux variables réservées :

  • La variable PS3 représente le prompt utilisé pour la saisiedu choix de l'utilisateur.
    Sa valeur par défaut est #?. Elle peut être modifiée si on le souhaite.
  • La variable REPLY qui contient l'indice de l'item sélectionné.

La variable var contient le libellé du choix et REPLY l'indice de ce dernier.

Exemple :

$ nl select.sh
     1  #!/bin/bash
     2
     3  function sauve {
     4          echo "Lancement de la sauvegarde"
     5  }
     6
     7  function restaure {
     8          echo "Lancement de la restauration"
     9  }
    10
    11  PS3="Votre choix : "
    12
    13  select item in "- Sauvegarde -" "- Restauration -" "- Fin -"
    14  do
    15          echo "Vous avez choisi l'item $REPLY : $item"
    16          case $REPLY in
    17                  1)
    18                          # Appel de la fonction sauve
    19                          sauve
    20                          ;;
    21                  2)
    22                          # Appel de la fonction restaure
    23                          restaure
    24                          ;;
    25                  3)
    26                          echo "Fin du script"
    27                          exit 0
    28                          ;;
    29                  *)
    30                          echo "Choix incorrect"
    31                          ;;
    32          esac
    33  done
$

Exécution :

$ ./select.sh
1) - Sauvegarde -
2) - Restauration -
3) - Fin -
Votre choix : 2
Vous avez choisi l'item 2 : - Restauration -
Lancement de la restauration

La saisie de la touche [Entrée] permet de réafficher le menu :

$ .
Votre choix :     # Appui sur la touche [Entrée]
1) - Sauvegarde -
2) - Restauration -
3) - Fin -
Votre choix : 3
Vous avez choisi l'item 3 : - Fin -
Fin du script
$

Analyse des options d'un script avec getopts

$ type getopts
getopts est une primitive du shell
$

Syntaxe :

getopts listeOptionsAttendues option

La commande interne getopts permet à un script d'anayser les options passées en argument.
Chaque appel à la commande getopts analyse l'option suivante de la ligne de commande.
Pour vérifier la validité de chacune des options, il faut appeler getopts à partir d'une boucle.

Définition d'une option

Pour getopts, une option est composée d'un caractère précédé du signe "+" ou "-".

Premier exemple :

$ ls -l *.sh

Par exemple, pour la commande ls, "-l" est une option et "*.sh" est un argument.
Une option peut fonctionner seule ou être associée à un argument.

Second exemple :

Le script suivant détail la manière d'utiliser la commande getopts.

$ nl test_getopts_1.sh
     1  #!/bin/bash
     2
     3  while getopts "abcd:e:" option
     4  do
     5          echo "getopts a trouvé l'option $option"
     6          case $option in
     7                  a)
     8                          echo "Exécution des commandes de l'option a"
     9                          echo "Indice de la prochaine option à traiter : $OPTIND"
    10                          ;;
    11                  b)
    12                          echo "Exécution des commandes de l'option b"
    13                          echo "Indice de la prochaine option à traiter : $OPTIND"
    14                          ;;
    15                  c)
    16                          echo "Exécution des commandes de l'option c"
    17                          echo "Indice de la prochaine option à traiter : $OPTIND"
    18                          ;;
    19                  d)
    20                          echo "Exécution des commandes de l'option d"
    21                          echo "Liste des arguments à traiter : $OPTARG"
    22                          echo "Indice de la prochaine option à traiter : $OPTIND"
    23                          ;;
    24                  e)
    25                          echo "Exécution des commandes de l'option e"
    26                          echo "Liste des arguments à traiter : $OPTARG"
    27                          echo "Indice de la prochaine option à traiter : $OPTIND"
    28                          ;;
    29          esac
    30  done
    31  echo "Analyse des options terminée"
    32  exit 0
$

L'appel à la commande getopts récupère l'option suivante et retourne un code vrai tant qu'il reste des options à analyser.
La liste des options utilisables avec ce script sont définies à la ligne 3 (getopts "abcd:e:" option). Il s'agit des options -a, -b, -c, -d et -e.
Le caractère ":" inscrit après les options "d" et "e" (getopts "abcd:e:" option) indique que ces options doivent être suivies obligatoirement d'un argument.
La variable "option" (getopts "abcd:e:" option) permet de récupérer la valeur de l'option en cours de traitement par la boucle while.
La variable réservée "$OPTIND" contient l'indice de la prochaine option à traiter.
La variable réservée "$OPTARG" contient l'argument associé à l'option.

Exécution du script avec des options valides :

$ ./test_getopts_1.sh -a -b -c -d toto -e tata,tutu
getopts a trouvé l'option a
Exécution des commandes de l'option a
Indice de la prochaine option à traiter : 2
getopts a trouvé l'option b
Exécution des commandes de l'option b
Indice de la prochaine option à traiter : 3
getopts a trouvé l'option c
Exécution des commandes de l'option c
Indice de la prochaine option à traiter : 4
getopts a trouvé l'option d
Exécution des commandes de l'option d
Liste des arguments à traiter : toto
Indice de la prochaine option à traiter : 6
getopts a trouvé l'option e
Exécution des commandes de l'option e
Liste des arguments à traiter : tata,tutu
Indice de la prochaine option à traiter : 8
Analyse des options terminée
$

Option invalide

Lorsque la commande getopts détecte une option invalide, la variable option est initialisée avec la caractère "?" et un message d'erreur est affiché à l'écran.
Les options suivantes sont analysées.

Exemple :

L'option -z ne fait pas partie de la liste des options attendues.

$ ./test_getopts_1.sh -a -z -b -c -d toto -e tata,tutu
getopts a trouvé l'option a
Exécution des commandes de l'option a
Indice de la prochaine option à traiter : 2
./test_getopts_1.sh : option non permise -- z
getopts a trouvé l'option ?

getopts a trouvé l'option b
Exécution des commandes de l'option b
Indice de la prochaine option à traiter : 4
getopts a trouvé l'option c
Exécution des commandes de l'option c
Indice de la prochaine option à traiter : 5
getopts a trouvé l'option d
Exécution des commandes de l'option d
Liste des arguments à traiter : toto
Indice de la prochaine option à traiter : 7
getopts a trouvé l'option e
Exécution des commandes de l'option e
Liste des arguments à traiter : tata,tutu
Indice de la prochaine option à traiter : 9
Analyse des options terminée
$

Gestion des erreurs

Si le caractère ":" est placé en première position dans la liste des options à traiter (ligne 3), les erreurs sont gérées différemment.

En cas d'option invalide :
- getopts n'affichera pas de message d'erreur.
- la variable OPTARG sera initialisée avec la valeur de l'option incorrecte (ligne 29).

Exemple :

$ nl test_getopts_1.sh
     1  #!/bin/bash
     2
     3  while getopts ":abcd:e:" option
     4  do
     5          echo "getopts a trouvé l'option $option"
     6          case $option in
     7                  a)
     8                          echo "Exécution des commandes de l'option a"
     9                          echo "Indice de la prochaine option à traiter : $OPTIND"
    10                          ;;
    11                  b)
    12                          echo "Exécution des commandes de l'option b"
    13                          echo "Indice de la prochaine option à traiter : $OPTIND"
    14                          ;;
    15                  c)
    16                          echo "Exécution des commandes de l'option c"
    17                          echo "Indice de la prochaine option à traiter : $OPTIND"
    18                          ;;
    19                  d)
    20                          echo "Exécution des commandes de l'option d"
    21                          echo "Liste des arguments à traiter : $OPTARG"
    22                          echo "Indice de la prochaine option à traiter : $OPTIND"
    23                          ;;
    24                  e)
    25                          echo "Exécution des commandes de l'option e"
    26                          echo "Liste des arguments à traiter : $OPTARG"
    27                          echo "Indice de la prochaine option à traiter : $OPTIND"
    28                          ;;
    29                  \?)
    30                          echo "$OPTARG : option invalide"
    31                          exit 1
    32                          ;;
    33          esac
    34  done
    35  echo "Analyse des options terminée"
    36  exit 0
$

Le message d'erreur généré automatiquement par getopts n'apparait plus et la variable $OPTARG a été substituée par la valeur de l'option incorrecte.

Ligne 29, le "?" doit être protégé par un anti-slash pour ne pas être interprété par le shell.

$ ./test_getopts_1.sh -a -z -b -c -d toto -e tata,tutu
getopts a trouvé l'option a
Exécution des commandes de l'option a
Indice de la prochaine option à traiter : 2
getopts a trouvé l'option ?
z : option invalide
$

Option valide avec argument manquant

Lorsque l'argument d'une option est absent, la variable option est initialisée avec le caractère ":" et OPTARG contient la valeur de l'option concernée (ligne 29).

Exemple :

$ nl test_getopts_1.sh
     1  #!/bin/bash
     2
     3  while getopts ":abcd:e:" option
     4  do
     5          echo "getopts a trouvé l'option $option"
     6          case $option in
     7                  a)
     8                          echo "Exécution des commandes de l'option a"
     9                          echo "Indice de la prochaine option à traiter : $OPTIND"
    10                          ;;
    11                  b)
    12                          echo "Exécution des commandes de l'option b"
    13                          echo "Indice de la prochaine option à traiter : $OPTIND"
    14                          ;;
    15                  c)
    16                          echo "Exécution des commandes de l'option c"
    17                          echo "Indice de la prochaine option à traiter : $OPTIND"
    18                          ;;
    19                  d)
    20                          echo "Exécution des commandes de l'option d"
    21                          echo "Liste des arguments à traiter : $OPTARG"
    22                          echo "Indice de la prochaine option à traiter : $OPTIND"
    23                          ;;
    24                  e)
    25                          echo "Exécution des commandes de l'option e"
    26                          echo "Liste des arguments à traiter : $OPTARG"
    27                          echo "Indice de la prochaine option à traiter : $OPTIND"
    28                          ;;
    29                  :)
    30                          echo "L'option $OPTARG requiert un argument"
    31                          exit 1
    32                          ;;
    33                  \?)
    34                          echo "$OPTARG : option invalide"
    35                          exit 1
    36                          ;;
    37          esac
    38  done
    39  echo "Analyse des options terminée"
    40  exit 0
$

Exécution du script en oubliant l'argument de l'option -e

$ ./test_getopts_1.sh -a -b -c -d toto -e
getopts a trouvé l'option a
Exécution des commandes de l'option a
Indice de la prochaine option à traiter : 2
getopts a trouvé l'option b
Exécution des commandes de l'option b
Indice de la prochaine option à traiter : 3
getopts a trouvé l'option c
Exécution des commandes de l'option c
Indice de la prochaine option à traiter : 4
getopts a trouvé l'option d
Exécution des commandes de l'option d
Liste des arguments à traiter : toto
Indice de la prochaine option à traiter : 6
getopts a trouvé l'option :
L'option e requiert un argument
$

Gestion d'arguments supplémentaires

Les options sont stockées dans les paramètres positionnels $1, $2 ..... $n.
Une fois que celles ci sont analysées, il est possible de s'en débarasser avec la commande shift.
Ceci est intéressant s'il reste des arguments à traiter derrière les options.

Exemple :

$ nl test_getopts_1.sh | tail
    37          esac
    38  done
    39  echo "Analyse des options terminée"
    40  echo "Avant shift : "
    41  echo "Liste des arguments : $*"
    42  echo "Indice de la prochaine option à traiter : $OPTIND"
    43  shift $((OPTIND-1))
    44  echo "Après shift : "
    45  echo "Liste des arguments : $*"
    46  exit 0
$

L'instruction à la ligne 43 permet de retirer les options de la liste des arguments.
L'expression OPTIND-1 représente le nombre d'options analysées, donc la valeur du décalage à réaliser.

Exécution du script avec des arguments supplémentaires :

$ ./test_getopts_1.sh -a -b -c -d toto -e tata,tutu arg1 arg2 arg3 arg4
getopts a trouvé l'option a
Exécution des commandes de l'option a
Indice de la prochaine option à traiter : 2
getopts a trouvé l'option b
Exécution des commandes de l'option b
Indice de la prochaine option à traiter : 3
getopts a trouvé l'option c
Exécution des commandes de l'option c
Indice de la prochaine option à traiter : 4
getopts a trouvé l'option d
Exécution des commandes de l'option d
Liste des arguments à traiter : toto
Indice de la prochaine option à traiter : 6
getopts a trouvé l'option e
Exécution des commandes de l'option e
Liste des arguments à traiter : tata,tutu
Indice de la prochaine option à traiter : 8
Analyse des options terminée
Avant shift :
Liste des arguments : -a -b -c -d toto -e tata,tutu arg1 arg2 arg3 arg4
Indice de la prochaine option à traiter : 8
Après shift :
Liste des arguments : arg1 arg2 arg3 arg4

$

Gestion d'un processus en arrière plan

La commande wait permet au shell d'attendre la terminaison d'un processus lancé en arrière-plan.

Syntaxes :

Attendre la terminaison du processus dont le PID est donné en argument :

wait pid1

Attendre la terminaison de tous les processus lancés en arrière-plan à partir du shell courant :

wait

Attendre la terminaison du processus dont le numéro de job est donné en argument :

wait %job

Exemples :

La commande find est lançée en arrière-plan et a pour PID 2878 :

$ find / -name /etc/passwd 1>/tmp/resu 2>/dev/null &
[1] 2878
$ jobs
[1]+  Running                 find / -name /etc/passwd > /tmp/resu 2> /dev/null &
$

Le shell s'endort en attendant la terminaison du processus 2878 :

$ wait 2878     # Ou wait %1

Le shell est réveillé lorsque le processus 2878 est terminé :

$ .
[1]+  Exit 1                  find / -name /etc/passwd > /tmp/resu 2> /dev/null
$

Le PID de la dernière commande lançée en arrière-plan est contenu dans la variable spéciale $!

Le script suivant lance en arrière-plan une recherche du fichier /etc/passwd (ligne 3).
D'autres actions peuvent être exécutées en attendant (ligne 5 à 8) puis le shell attend la fin de la recherche (ligne 9) avant d'afficher à l'écran le contenu du fichier /tmp/resu (ligne 12).

$ nl test_wait_1.sh
     1  #!/bin/bash
     2
     3  find / -name /etc/passwd 1>/tmp/resu 2>&1 &
     4  echo "Le PID du script lancé en arrière-plan est le : $!"
     5  echo "Début des autres commandes"
     6
     7  echo "Fin des autres commandes"
     8  echo "Recherche en cours - Attente de la fin de la recherche"
     9  wait $!
    10  echo "La recherche est terminée"
    11  echo "Affichage du résultat"
    12  most /tmp/resu
    13  exit 0
$
$ ./test_wait_1.sh
Le PID du script lancé en arrière-plan est le : 11697
Début des autres commandes
Fin des autres commandes
Recherche en cours - Attente de la fin de la recherche
.....      # Le shell s'endort
.....
La recherche est terminée
Affichage du résultat
$

Script d'archivage incrémental et transfert sftp automatique

Le script suivant utilise toutes les techniques vues précédemment

CPIO

Pré-requis pour que le script suivant fonctionne :

1 - Paramétrer sur le poste à sauvegarder un accès ssh au serveur de sauvegarde via un système d'authentification par clé privée / publique.

Tout est expliqué ici : http://quennec.fr/linux/utilisation/connexion-a-une-machine-distante-sans-saisir-le-mot-de-passe

2 - Le programme bzip2 doit être installé sur le poste à sauvegarder.

$ apt-get install bzip2

3 - Rendre les scripts exécutables

$ chmod +x uploadBackup.sh && chmod +x fonctions.inc.sh

Script d'archivage incrémental et transfert sftp automatique

Ce script permet d'effectuer une sauvegarde incrémentale d'un répertoire vers un serveur de sauvegarde.
Il utilise un système de niveau correspondant au jour du mois - 1.
Tous les 1er de chaque mois, une sauvegarde totale (niveau 0) est effectuée.
Une sauvegarde incrémentale est effectuée les jours suivants.

Le script est composé de 2 fichiers.
Un fichier comportant le script principal (uploadBackup.sh) et un fichier comportant les fonctions personnalisées utiles au script principal (fonctions.inc.sh).

Les fichiers à sauvegarder (variable DATADIR) sont enregistrés dans une archive CPIO compressée avec BZIP2 et stockée dans un répertoire local sur le poste à sauvegarder (variable ARCHIVEDIR).
Avant chaque sauvegarde, un fichier de niveau est créé dans le répertoire local.
Toutes les logs sont enregistrées dans un fichier stocké également dans le répertoire local.
La sauvegarde incrémentale utilise le fichier niveau précédent afin de sauvegarder tous les fichiers modifiés depuis la sauvegarde précédente grâce à la commande find et l'option -newer.
Enfin, l'archive CPIO est envoyée sur le serveur de sauvegarde (variable serveur_sauvegarde) via SFTP et stockée dans des sous-répertoires correspondant à l'année et au mois de la sauvegarde dans le dossier de sauvegarde (variable dossier_distant) .

Détail du fichier uploadBackup.sh

$ nl uploadBackup.sh
     1  #!/bin/bash
     2  # set -x
     3  # Répertoire des scripts shell
     4  SCRIPTDIR="/root/script_archivage_incremental"
     5  # Répertoire des fichiers à sauvegarder
     6  DATADIR="/root/dossier_a_sauvegarder"
     7  # Répertoire local des archives
     8  ARCHIVEDIR="/root/local_backup"
     9  # Inclure les fonctions
    10  . $SCRIPTDIR/fonctions.inc.sh
    11  # Fichier de log
    12  LOG=$ARCHIVEDIR/`getDate`.log
    13  # Redirection de toutes les sorties du script vers le fichier de log
    14  exec 1>$LOG 2>&1
    15  # Déterminer le niveau de sauvegarde
    16  # Le 1er du mois => niveau 0
    17  jourDuMois=`getDayForCalcul`
    18  ((niveau=$jourDuMois-1))
    19  case $niveau in
    20          0) # Sauvegarde totale
    21                  # Nettoyage du répertoire d'archive
    22                  rm -i $ARCHIVEDIR/*.bz2 $ARCHIVEDIR/niveau*
    23                  # Création du fichier de niveau (niveau 0)
    24                  touch $ARCHIVEDIR/niveau0
    25                  archive="$ARCHIVEDIR/`getDate`_niveau0.cpio"
    26                  find $DATADIR | cpio -ocv | bzip2 -c > $archive.bz2
    27                  ;;
    28          *)
    29                  # Creation du fichier de niveau
    30                  touch $ARCHIVEDIR/niveau$niveau
    31                  archive="$ARCHIVEDIR/`getDate`_niveau${niveau}.cpio"
    32                  # Determination du niveau precedent
    33                  ((niveauPrec=$niveau-1))
    34                  # Test si le fichier de niveau precedent existe
    35                  if [[ ! -f $ARCHIVEDIR/niveau$niveauPrec ]]
    36                  then
    37                          # Si il n'existe pas, sauvegarde integrale du repertoire
    38                          echo "Fichier de niveau $niveauPrec inexistant"
    39                          echo "Execution d'une sauvegarde integrale en cours de mois"
    40                          find $DATADIR | cpio -ocv | bzip2 -c > $archive.bz2
    41                  else
    42                          # Sauvegarde incrementale
    43                          find $DATADIR -newer $ARCHIVEDIR/niveau$niveauPrec | cpio -ocv | bzip2 -c > $archive.bz2
    44                  fi
    45                  ;;
    46  esac
    47  # Vérification de la validité de l'archive
    48  if isArchiveInvalide $archive.bz2 ; then
    49          echo "Archive $archive.bz2 INVALIDE - Fichier non transfere"
    50          exit 1
    51  fi
    52  # Transfert du fichier vers le serveur de sauvegarde
    53  if transfert ${archive}.bz2 ; then
    54          echo "Transfert realise avec succes"
    55          exit 0
    56  fi
    57  # Si le transfert a echoue
    58  echo "Echec de transfert"
    59  exit 1
$

Détail du fichier fonctions.inc.sh

$ nl fonctions.inc.sh
     1  #!/bin/bash
     2  function transfert {
     3          typeset mois
     4          typeset annee
     5          # Recuperation de la valeur du premier argument passe a la fonction
     6          # Recuperation du nom de l'archive a envoyer au serveur de sauvegarde
     7          typeset ficATransferer=$1
     8          # Adresse du serveur de sauvegarde
     9          typeset serveur_sauvegarde="192.168.1.113"
    10          # Compte utilise pour se connecter au serveur de sauvegarde
    11          typeset user="root"
    12          # Chemin absolu du dossier ou sera stocke la sauvegarde
    13          typeset dossier_distant="/root/sauvegarde_dossier"
    14          mois=`getMonth`
    15          annee=`getYear`
    16          # Test si le dossier de sauvegarde existe sur le serveur de sauvegarde
    17          # Connexion au serveur avec le user root
    18          ssh $user@$serveur_sauvegarde test -d $dossier_distant/$annee/$mois
    19          # Test sur le code retour de la commande precedente
    20          case $? in
    21                  0)
    22                          ;;
    23                  255)
    24                          echo "Echec de la commande SSH"
    25                          return 1
    26                          ;;
    27                  *)
    28                          # Si code retour different de 0 et 255
    29                          # Creation du repertoire de la sauvegarde
    30                          ssh $user@$serveur_sauvegarde mkdir -p $dossier_distant/$annee/$mois
    31                          ;;
    32          esac
    33          # Connexion au serveur de sauvegarde en FTP sécurisé
    34          # Ne pas oublier les doubles chevrons << avant le mot cle FIN
    35          # Ne pas mettre d'espace entre << et FIN
    36          sftp -b - $user@$serveur_sauvegarde <<FIN
    37          # Positionnement dans le repertoire de la sauvegarde
    38          cd $dossier_distant/$annee/$mois
    39          pwd
    40          # Envoi de la sauvegarde
    41          put $ficATransferer
    42  FIN
    43  # Ne pas mettre de tabulation ou d'espace devant le mot clé FIN
    44  # Sinon celui-ci n'est pas reconnu
    45          # Test sur le code retour de la commande SFTP
    46          # Si le code retour est different de 0
    47          # Une anomalie a ete rencontree
    48          (( $? != 0 )) && return 1
    49          # Tester si archive valide sur serveur de sauvegarde
    50          ficSurMachineCible=$(basename $ficATransferer)
    51          ssh $user@$serveur_sauvegarde bzip2 -t $dossier_distant/$annee/$mois/$ficSurMachineCible
    52          case $? in
    53                  0)
    54                          ;;
    55                  255)
    56                          echo "Echec de la commande SSH"
    57                          return 1
    58                          ;;
    59                  *)
    60                          # Si code retour different de 0 et 255
    61                          # Alors l'archive est invalide
    62                          echo "Archive $dossier_distant/$annee/$mois/$ficSurMachineCible INVALIDE"
    63                          return 1
    64                          ;;
    65          esac
    66          return 0
    67  }
    68  function isArchiveInvalide {
    69          typeset archive=$1
    70          bzip2 -t $archive 2>/dev/null && return 1
    71          return 0
    72  }
    73  function getDate {
    74          date '+%Y_%m_%d'
    75  }
    76  function getYear {
    77          date '+%Y'
    78  }
    79  function getMonth {
    80          date '+%m'
    81  }
    82  function getDayForCalcul {
    83          date '+%e' | sed 's/ //'
    84  }
$

Les expressions régulières

Les expressions régulières sont utilisées par un certain nombre de commandes UNIX.

Les expressions régulières sont composées de caractères ordinaires et de caractères spéciaux qui ont une signification particulière.

Il existe 2 types d'expressions régulières :

  • Les expressions régulières basiques (ERb)
  • Les expressions régulières étendues (ERe)

Les ERb sont utilisées par les commandes suivantes :

  • vi
  • grep
  • expr
  • sed

Les ERe sont utilisées par les commandes suivantes :

  • grep avec l'option -e (egrep)
  • awk

 

Caractères communs aux ERb et ERe

Le tableau suivant liste les caractères spéciaux communs aux ERb et aux ERe

Caractère spécial Signification
^ Début de ligne
$ Fin  de ligne
. (point) Un caractère quelconque
[liste_de_caractères] Un caractère cité dans la liste
[^liste_de_caractères] Un caractère qui n'est pas cité dans la liste
* 0 à n fois le caractère ou regroupement précédent
\<expression Début d'un mot. Caractères pouvant faire partie d'un mot : [A-Za-z0-9_]
expression\> Fin d'un mot
\<expression\> Mot complet
\c Protection du caractère spécial "c"

Exemples :

Les expressions "space" et "tab" représentent les touches [espace] et [tabulation].
La tabulation est représentée par les caractères "\t".

Expression régulière Signification
soleil Chaine contenant "soleil".
Exemple de correspondance :
Aujourd'hui il y a du soleil, mais demain il pleut !
soleil toujours.
Passer ses vacances au soleil.
^soleil Chaine commençant par "soleil".
Exemple de correspondance :
soleil toujours.
soleil$ Chaine se terminant par "soleil".
Exemple de correspondance :
Passer ses vacances au soleil.
^[A-Z][5-9].$ Chaine composée de 3 caractères.
Le premier est une majuscule, le second est un chiffre entre 5 et 9 et le dernier un caractère quelconque.
Exemple de correspondance :
B6z
Z5*
^$ Chaine vide (aucun caractère entre ^ et $)
^[space tab]*$ Chaine contenant entre 0 et n caractères espace ou tabulation
7space[0-79]A* Chaine contenant le chiffre 7, suivi d'un espace, suivi de n'importe quel chiffre sauf 8, suivi de la lettre A 0 à n fois.
Exemple de correspondance :
x7 6
abc7 9Axy
7 1AAAAAAAAAAAAAAAAbfgddghjgdhj
[0-9][^A-Z_]$ Chaine dont l'avant dernier caractère est un chiffre et le dernier n'est ni une lettre majuscule ni un caractère souligné.
Exemple de correspondance :
AZER1a
3*
440008b
\<tout Chaine contenant un mot commençant par "tout".
Exemple de correspondance :
bonjour tout le monde
il faut dire toutefois
Exemple de non correspondance :
partout dans le monde
\<tout\>

Chaine contenant le mot "tout".
Exemple de correspondance :
bonjour tout le monde
Exemple de non correspondance :
partout dans le monde
il faut dire toutefois

[0-9][0-9]\.[0-9] Chaine contenant 2 chiffres suivi du caractère . (point) suivi d'un chiffre.
Exemple de correspondance :
4576.45

Les marqueurs ^ et $ perdent leur signification s'ils ne sont pas placés respectivement en début et en fin de l'expression régulière.

Etiquettes: 

Caractères spécifiques aux ERb

Caractère spécial Signification
\{n\} n fois le caractère précédent
\{n,\} Au moins n fois le caractère précédent
\{n,x\} Entre n et x fois le caractère précédent
\(ERb\) Mémorisation d'une ERb
\1, \2, ... Rappel de mémorisation

Dans les ERB, le caractère "\" donne une signification spéciale aux parenthèses et accolades.

Exemple :

Expression régulière Signification
\-[A-Z]\{2\}\- Chaine contenant un mot de 2 lettres majuscules entouré par des tirets (-)
Exemples de correspondance :
BOUCHES-DU-RHONE
EURE-ET-LOIR
VAL-DE-MARNE

Avec la commande grep (ou autre), le tiret (-) doit être protégé par un antislash (\) pour éviter d'être interprété comme une option.

Caractères spécifiques aux ERe

Caractère spécial Signification
? 0 ou 1 fois le caractère ou regroupement précédent
+ 1 à n fois le caractère ou regroupement précédent
{n} n fois le caractère ou regroupement précédent
{n,} Au moins n fois le caractère ou regroupement précédent
{n,x} Entre n et x fois le caractère ou regroupement précédent
(er1) Regroupement
er1 | er2 | er3 Alternatives

Dans les ERe, les accolades et parenthèses perdent leur signification spéciale si elles sont précédées d'un antislash.

Exemples :

Expression régulière Signification
^[+-]?[0-9]+$

Chaine représentant un nombre entier d'au moins 1 chiffre précédé éventuellement du signe + ou -
Exemples de correspondance :
2
-56
+235
789654

\([a-zA-Z]{3}\) Chaine contenant 3 lettres minuscules et/ou majuscules et entourés de parenthèses.
Exemple de correspondance :
HERBIERS        (Les)   Herbiers
\([-a-zA-Z _]{4,}\) Chaine contenant au moins 4 caractères composées de lettres minuscules et/ou majuscules, de tiret (-), d'espace et de tiret bas (_) et le tout entouré de parenthèses.
Exemples de correspondance :
1er (Ouest)
CASTILLON (CANTON DE LEMBEYE)
2e  (Nord-Ouest)
^41|yotte$ Chaine commençant par "41" ou finissant par "yotte"
Exemples de correspondance :
41      57      57463   3       MOSELLE Moselle
41      88      88160   4       VOSGES  Vosges
06      976     97608   0       MAYOTTE Mayotte
(in|re)+

Chaine composée de 1 à n occurences de "in" ou de "re" (en minuscule)
Exemples de correspondance :
Finistère
Indre-et-Loire
Martinique

 

Exploitation des expressions régulières par les commandes

La commande grep

La commande grep permet l'utilisation d'expressions régulières normalisées par POSIX.

Par défaut, la commande grep utilise les ERb.

Associée à l'option -E, elle utilise les ERe.

Exemples :

Avec le fichier depts2012.txt (téléchargeable sur le site de l'INSEE à cette adresse : http://www.insee.fr/fr/methodes/nomenclatures/cog/telechargement.asp)

$ head depts2012.txt && tail depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
82      07      07186   5       ARDECHE Ardèche
21      08      08105   4       ARDENNES        Ardennes
73      09      09122   5       ARIEGE  Ariège
11      91      91228   5       ESSONNE Essonne
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
..............
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Rechercher la chaine "loire-atlantique" sans tenir compte de la casse :

$ cat depts2012.txt | grep -i 'loire-atlantique'
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
$

Rechercher les lignes commançant par 21 :

$ cat depts2012.txt | grep '^21'
21      08      08105   4       ARDENNES        Ardennes
21      10      10387   5       AUBE    Aube
21      51      51108   3       MARNE   Marne
21      52      52121   3       HAUTE-MARNE     Haute-Marne
$

Rechercher les lignes se terminant par "ique" :

$ cat depts2012.txt | grep -i 'ique$'
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
02      972     97209   3       MARTINIQUE      Martinique
$

Rechercher les lignes ayant 2 occurences de la lettre "s" :

$ cat depts2012.txt | grep -i 'ss'
11      91      91228   5       ESSONNE Essonne
$

Idem mais avec une ERb :

$ cat depts2012.txt | grep -i 's\{2\}'
11      91      91228   5       ESSONNE Essonne
$

Idem mais avec une ERe :

$ cat depts2012.txt | grep -iE 's{2}'
11      91      91228   5       ESSONNE Essonne
$

Rechercher les lignes ayant un tiret (-) :

$ cat depts2012.txt | grep '\-'
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
93      13      13055   4       BOUCHES-DU-RHONE        Bouches-du-Rhone
$

Rechercher les lignes contenant les chaines "loir" ou "tique" (peu importe la casse) :

$ cat depts2012.txt | grep -iE 'loir|tique'
24      28      28085   1       EURE-ET-LOIR    Eure-et-Loir
24      37      37261   1       INDRE-ET-LOIRE  Indre-et-Loire
24      41      41018   0       LOIR-ET-CHER    Loir-et-Cher
82      42      42218   3       LOIRE   Loire
83      43      43157   3       HAUTE-LOIRE     Haute-Loire
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
24      45      45234   2       LOIRET  Loiret
52      49      49007   0       MAINE-ET-LOIRE  Maine-et-Loire
72      64      64445   4       PYRENEES-ATLANTIQUES    Pyrenees-Atlantiques
26      71      71270   0       SAONE-ET-LOIRE  Saone-et-Loire
$

Etiquettes: 

La commande expr

Syntaxe :

expr chaine-de-caracteres : expression-reguliere-basique

La commande expr propose l'opérateur ":" qui permet de mettre en correspondance une chaine de caractères avec une expression régulière.

Fonctionnement de l'opérateur ":" :

  • Le nombre de caractères de chaine-de-caracteres correspondant à l'ERb expression-reguliere-basique est affiché à l'écran.
  • Si chaine-de-caracteres correspond à expression-reguliere, la commande retourne un code vrai ($? = 0). Elle retourne un code faux dans le cas contraire ($? = 1).
  • L'expression régulière est comparée par rapport au début de la variable (le "^" est implicite dans l'ERb).
  • Si une partie de l'expression régulière est mémorisée avec \( \), la commande affiche sur le terminal la portion de la chaine correspondante.

Exemples :

Vérifier que la chaine saisie est un nombre.

$ read nb1
34657
$ read nb2
456G43
$

La variable nb1 contient uniquement des chiffres.

$ expr "$nb1" : '[0-9]*$'                   # Equivaut à '^[0-9]*$'
5
$ echo $?
0
$

La variable nb2 contient une lettre.

$ expr "$nb2" : '[0-9]*$'
0
$ echo $?
1
$

Compter le nombre de caractères contenus dans une variable.

$ chaine="Ceci est une ligne de texte"
$ expr "$chaine" : '.*'
27
$

Afficher la partie de la chaine correspondant à la mémorisation (ici, le chiffre entouré de pipe "|")

$ chaine="un|deux|trois|4|cinq|six|sept"
$ expr "$chaine" : '.*|\([0-9]\{1\}\)'
4
$

Le caractère * recherche toujours la chaine la plus longue. Le "|" le plus à droite.

$ expr "$chaine" : '\(.*\)|'
un|deux|trois|4|cinq|six
$

Solution permettant de s'arrêter au premier "|".

$ expr "$chaine" : '\([^|]*\)|'
un
$

Le script suivant test si la saisie correspond à un nombre (positif ou négatif) et effectue la somme des nombres saisis.

$ nl ./sumnb.sh
     1  #!/bin/bash
     2  somme=0
     3  while :
     4  do
     5          echo -e "Saisir un nombre entier : \c"
     6          if read nombre
     7          then
     8                  if ( expr "$nombre" : '[0-9]*$' || expr "$nombre" : '-[0-9]*$' ) > /dev/null
     9                  then
    10                          somme=`expr $somme + $nombre`
    11                  else
    12                          echo "Saisie incorrecte"
    13                  fi
    14          else
    15                  break
    16          fi
    17  done
    18  echo -e "\nResultat : $somme"
    19  exit 0
$

Exécution du script.

$ ./sumnb.sh
Saisir un nombre entier : 45
Saisir un nombre entier : 69
Saisir un nombre entier : er
Saisie incorrecte
Saisir un nombre entier : 9y
Saisie incorrecte
Saisir un nombre entier : 2
Saisir un nombre entier : -89
Saisir un nombre entier : ^d                                # Saisie clavier ctrl+d
Resultat : 27
$

Etiquettes: 

La commande sed

sed

La commande sed (stream editor) est un éditeur de texte non intéractif. Elle permet d'automatiser le traitement de fichiers texte.

Etiquettes: 

Utilisation de la commande sed

Syntaxe de base :

sed [-n] action [fichier1 fichier2 ... fichierx]
sed [-n] -e action1 [-e action2 ...] [fichier1 fichier2 ... fichierx]
sed -f script-sed [fichier1 fichier2 ... fichierx]

Les actions spécifiées sont exécutées sur chaque ligne du ou des fichiers. Le résultat du traitement est affiché sur la sortie standard. Si plusieurs actions sont spécifiées sur la ligne de commande, chacune doit être précédée de l'option -e.

La commande sed ne modifie pas le fichier d'origine sauf si elle est exécutée avec l'option -i.

Syntaxe d'une action :

[adresse[,adresse]] commande [arguments]

Une action est syntaxiquement composée de :

  • Une partie adresse qui permet de spécifier sur quelles lignes doit s'effectuer la commande.
  • La commande à exécuter.
  • Les arguments de la commande.

Syntaxe d'une adresse :

Type d'adresse Lignes traitées
Aucune adresse Toutes les lignes
Adresses de type 1
n Ligne n.
$ Dernière ligne.
/ERb/ Lignes correspondant à l'expression régulière
Adresses de type 2
n1, n2 Ligne n1 jusqu'à ligne n2.
/ERb1/,/ERb2/ La première ligne traitée sera la première trouvée correspondant à ERb1. Le traitement se poursuivra sur toutes les lignes jusqu'à ce que sed rencontre une ligne correspondant à ERb2. Cette dernière sera également traitée.

Syntaxe de la commande :

Commande Argument Type d'adresse supportée (maximum) Signification
d Aucun 2 Ne pas afficher les lignes spécifiées (delete).
p Aucun 2 Afficher les lignes spécifiées (print).
s /ERb/remplacement/[g] 2 Effectuer une substitution sur les lignes spécifiées (substitute). Le caractère optionnel "g" indique si la substitution doit être globale sur les lignes.
w fichier 2 Ecrire les lignes spécifiées dans un fichier (write).
= Aucun 1 Afficher le numéro de la ligne spécifiée.
Dans les commandes ci-dessous, l'antislash rend le caractère "saut de ligne" invisible, ce qui permet de pouvoir spécifier plusieurs lignes de texte. Le dernier saut de ligne n'est pas masqué et représente la fin de la commande.
a\

texte\[entrée]
texte\[entrée]
texte [entrée]

1 Ajouter les lignes de texte après chaque ligne spécifiée (add).
i\ texte\[entrée]
texte\[entrée]
texte [entrée]
1 Insérer les lignes de texte avant chaque ligne spécifiée (insert).
c\ texte\[entrée]
texte\[entrée]
texte [entrée]
2 Remplacer les ligne spécifiées par les lignes de texte (change).
Négation de la commande
!commande La commande s'exécutera sur toutes les lignes sauf celles spécifiées dans l'adresse.

 

Exemples

Utilisation de sed en ligne de commande

Exemples avec le fichier depts2012.txt (téléchargeable sur le site de l'INSEE à cette adresse : http://isbeta.fr/f8c21)

$ cat depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
82      07      07186   5       ARDECHE Ardèche
...
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

La commande d (delete)

La commande d permet de ne pas afficher à l'écran les lignes sélectionnées par la partie adresse.

Exemple :

Ne pas afficher les lignes contenant les caractères de a à h (en minuscule)

$ sed '/[a-h]/d' depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
73      46      46042   2       LOT     Lot
$

La commande p (print)

La commande p permet d'afficher à l'écran les lignes sélectionnées par la partie adresse. Par défauf, sed affiche également tout le contenu du fichier. Pour modifier ce comportement, il faut utiliser l'option -n.

Exemple :

Afficher les lignes 1 à 4. Par défaut, sed affiche en plus tout le contenu du fichier. Les lignes demandées apparaissent donc en double.

$ sed '1,4p' depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
....
$

L'option -n permet de ne pas afficher le reste du fichier.

$ sed -n '1,4p' depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
$

La commande w (write)

La commande w permet d'écrire dans un fichier les lignes sélectionnées par la partie adresse. Comme pour la commande p, sed affiche également tout le contenu du fichier à l'écran. Pour modifier ce comportement, il faut utiliser l'option -n.

Exemple :

Stocker dans un fichier "depts3x" toutes les villes dont le code postal commence par 3 et dans le fichier "depts6x" toutes les villes dont le code postal commence par 6.

$ sed -n -e '/3[0-9]\{4\}/w depts3x' -e '/6[0-9]\{4\}/w depts6x' depts2012.txt
$ cat depts3x
91      30      30189   2       GARD    Gard
73      31      31555   3       HAUTE-GARONNE   Haute-Garonne
73      32      32013   2       GERS    Gers
72      33      33063   3       GIRONDE Gironde
91      34      34172   5       HERAULT Hérault
53      35      35238   1       ILLE-ET-VILAINE Ille-et-Vilaine
24      36      36044   5       INDRE   Indre
24      37      37261   1       INDRE-ET-LOIRE  Indre-et-Loire
82      38      38185   5       ISERE   Isère
43      39      39300   2       JURA    Jura
$ cat depts6x
22      60      60057   5       OISE    Oise
25      61      61001   5       ORNE    Orne
31      62      62041   2       PAS-DE-CALAIS   Pas-de-Calais
83      63      63113   2       PUY-DE-DOME     Puy-de-Dôme
72      64      64445   4       PYRENEES-ATLANTIQUES    Pyrénées-Atlantiques
73      65      65440   4       HAUTES-PYRENEES Hautes-Pyrénées
91      66      66136   4       PYRENEES-ORIENTALES     Pyrénées-Orientales
42      67      67482   2       BAS-RHIN        Bas-Rhin
42      68      68066   2       HAUT-RHIN       Haut-Rhin
82      69      69123   2       RHONE   Rhône
$

Négation d'une commande (!)

Le caractère ! placé devant une commande permet d'exécuter cette dernière sur toutes les lignes sauf sur celles correspondant à la partie adresse.

Exemple :

Ne pas afficher les lignes contenant les caractères de a à h (en minuscule)

$ sed -n '/[a-h]/!p' depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
73      46      46042   2       LOT     Lot
$

La commande s (substitution)

La commande s permet de substituer une chaine de caractères par une autre sur les lignes sélectionnées par la partie adresse.

Pour substituer des noms de fichiers avec la commande sed, utiliser le pipe '|' comme séparateur à cause du slash '/' dans les noms de fichiers.

$ sed -i "s|/fichier1|/fichier2|g" maListeDeFichiers

Premier exemple :

Remplacer toutes les chaines contenant '-et-' ou '-ET-' par ' & '

$ sed -n '/-et-/p' depts2012.txt
24      28      28085   1       EURE-ET-LOIR    Eure-et-Loir
53      35      35238   1       ILLE-ET-VILAINE Ille-et-Vilaine
24      37      37261   1       INDRE-ET-LOIRE  Indre-et-Loire
24      41      41018   0       LOIR-ET-CHER    Loir-et-Cher
72      47      47001   0       LOT-ET-GARONNE  Lot-et-Garonne
52      49      49007   0       MAINE-ET-LOIRE  Maine-et-Loire
41      54      54395   0       MEURTHE-ET-MOSELLE      Meurthe-et-Moselle
26      71      71270   0       SAONE-ET-LOIRE  Saône-et-Loire
11      77      77288   0       SEINE-ET-MARNE  Seine-et-Marne
73      82      82121   0       TARN-ET-GARONNE Tarn-et-Garonne
$

$ sed -e 's/-et-/ \& /g' -e 's/-ET-/ \& /g' depts2012.txt | sed -n '/ \& /p'
24      28      28085   1       EURE & LOIR     Eure & Loir
53      35      35238   1       ILLE & VILAINE  Ille & Vilaine
24      37      37261   1       INDRE & LOIRE   Indre & Loire
24      41      41018   0       LOIR & CHER     Loir & Cher
72      47      47001   0       LOT & GARONNE   Lot & Garonne
52      49      49007   0       MAINE & LOIRE   Maine & Loire
41      54      54395   0       MEURTHE & MOSELLE       Meurthe & Moselle
26      71      71270   0       SAONE & LOIRE   Saône & Loire
11      77      77288   0       SEINE & MARNE   Seine & Marne
73      82      82121   0       TARN & GARONNE  Tarn & Garonne
$

Second exemple :

Travailler sur le contenu d'une variable.

$ arg=fic1,fic2,fic3
$ echo $arg
fic1,fic2,fic3
$ echo $arg | sed 's/,/ /g'        # On remplace les virgules par des espaces
fic1 fic2 fic3
$ liste_arg=$(echo $arg | sed 's/,/ /g')
$ echo $liste_arg
fic1 fic2 fic3
$ for argt in $liste_arg
> do
> echo $argt
> done
fic1
fic2
fic3
$

Troisième exemple :

Le caractère "&" utilisée dans la partie remplacement représente la chaine correspondant à l'expression régulière.

$ tail depts2012.txt
11      91      91228   5       ESSONNE Essonne
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$ tail depts2012.txt | sed 's/.*/|&|/'
|11     91      91228   5       ESSONNE Essonne|
|11     92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine|
|11     93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis|
|11     94      94028   2       VAL-DE-MARNE    Val-de-Marne|
|11     95      95500   2       VAL-D'OISE      Val-d'Oise|
|01     971     97105   3       GUADELOUPE      Guadeloupe|
|02     972     97209   3       MARTINIQUE      Martinique|
|03     973     97302   3       GUYANE  Guyane|
|04     974     97411   0       LA REUNION      La Réunion|
|06     976     97608   0       MAYOTTE Mayotte|
$

Toutes les chaines ont été encadrées par des pipes "|"

Quatrième exemple :

Récupérer les codes postaux et les noms des départements (en majuscule) afin d'effectuer une mise en forme particulière.
Utilisation de la mémorisation grâce aux caractères \( \) afin de les réafficher avec \1 et \2.

$ tail depts2012.txt
11      91      91228   5       ESSONNE Essonne
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte

$ tail depts2012.txt | sed "s/^[0-9]\{2\}[ \t]*[0-9]\{2,3\}[ \t]*\([0-9]\{5\}\)[ \t]*[0-9][ \t]*\([-A-Z_\. ']\{1,\}\)[ \t]*.\{1,\}$/Code postal : \1\tDepartement : \2/"
Code postal : 91228     Departement : ESSONNE
Code postal : 92050     Departement : HAUTS-DE-SEINE
Code postal : 93008     Departement : SEINE-SAINT-DENIS
Code postal : 94028     Departement : VAL-DE-MARNE
Code postal : 95500     Departement : VAL-D'OISE
Code postal : 97105     Departement : GUADELOUPE
Code postal : 97209     Departement : MARTINIQUE
Code postal : 97302     Departement : GUYANE
Code postal : 97411     Departement : LA REUNION
Code postal : 97608     Departement : MAYOTTE
$

Cinquième exemple :

Exemples détaillés de l'option g dans la commande de substitution s.

$ arg=val1,val2,val3,val4
$ echo $arg
val1,val2,val3,val4

Remplacement de la globalité des virgules par des espaces grâce à l'option g.

$ echo $arg | sed 's/,/ /g'
val1 val2 val3 val4

Sans l'option g, seule la première virgule rencontrée est remplacée par un espace.

$ echo $arg | sed 's/,/ /'
val1 val2,val3,val4

Le chiffre 1 permet le remplacement de la première virgule rencontrée. Identique à la commande précédente.

$ echo $arg | sed 's/,/ /1'
val1 val2,val3,val4

Le chiffre 3 permet le remplacement de la troisième virgule rencontrée.

$ echo $arg | sed 's/,/ /3'
val1,val2,val3 val4

 

 

Script sed

Un script sed est un fichier texte contenant une suite d'actions sed qui seront exécutées sur les lignes d'un ou plusieurs fichiers passés en paramètre ou sur l'entrée standard de la commande sed.

Toutes les actions sont évaluées et appliquées sur toutes les lignes du ou des fichiers.

Exemple :

$ nl script.sed
     1  1d
     2  2i\
     3  ------------------------\
     4  Liste des departements :\
     5  ------------------------
     6  s/^[0-9]\{2\}[ \t]*[0-9]\{2,3\}[ \t]*\([0-9]\{5\}\)[ \t]*[0-9][ \t]*\([-A-Z_\. ']\{1,\}\)[ \t]*.\{1,\}$/Code postal : \1\tDepartement : \2/
     7  s/^[0-9]\{2\}[ \t]*[0-9][A-B][ \t]*\([0-9][A-B][0-9]\{3\}\)[ \t]*[0-9][ \t]*\([-A-Z_\. ']\{1,\}\)[ \t]*.\{1,\}$/Code postal : \1\tDepartement : \2/
     8  $a\
     9  ---------------\
    10  Fin de la liste\
    11  ---------------
$

  • Ligne 1 : Suppression de la première ligne du fichier.
  • Ligne 2 : Affichage du contenu des lignes 3 à 5 du script avant la ligne 2 du fichier.
  • Ligne 6 : Mise en forme des données du fichier
  • Ligne 7 : Identique à la ligne 6 mais avec la prise en compte des départements de La Corse
  • Ligne 8 : Affichage du contenu des lignes 9 à 11 du script après la dernière ligne ($) du fichier.

$ sed -f script.sed depts2012.txt
------------------------
Liste des departements :
------------------------
Code postal : 01053     Departement : AIN
Code postal : 02408     Departement : AISNE
Code postal : 03190     Departement : ALLIER
Code postal : 04070     Departement : ALPES-DE-HAUTE-PROVENCE
Code postal : 05061     Departement : HAUTES-ALPES
Code postal : 06088     Departement : ALPES-MARITIMES
Code postal : 07186     Departement : ARDECHE
Code postal : 08105     Departement : ARDENNES
Code postal : 09122     Departement : ARIEGE
Code postal : 10387     Departement : AUBE
Code postal : 11069     Departement : AUDE
Code postal : 12202     Departement : AVEYRON
Code postal : 13055     Departement : BOUCHES-DU-RHONE
Code postal : 14118     Departement : CALVADOS
Code postal : 15014     Departement : CANTAL
Code postal : 16015     Departement : CHARENTE
Code postal : 17300     Departement : CHARENTE-MARITIME
Code postal : 18033     Departement : CHER
Code postal : 19272     Departement : CORREZE
Code postal : 2A004     Departement : CORSE-DU-SUD
Code postal : 2B033     Departement : HAUTE-CORSE
Code postal : 21231     Departement : COTE-D'OR
Code postal : 22278     Departement : COTES-D'ARMOR
Code postal : 23096     Departement : CREUSE
Code postal : 24322     Departement : DORDOGNE
Code postal : 25056     Departement : DOUBS
Code postal : 26362     Departement : DROME
Code postal : 27229     Departement : EURE
Code postal : 28085     Departement : EURE-ET-LOIR
Code postal : 29232     Departement : FINISTERE
Code postal : 30189     Departement : GARD
Code postal : 31555     Departement : HAUTE-GARONNE
Code postal : 32013     Departement : GERS
Code postal : 33063     Departement : GIRONDE
Code postal : 34172     Departement : HERAULT
Code postal : 35238     Departement : ILLE-ET-VILAINE
Code postal : 36044     Departement : INDRE
Code postal : 37261     Departement : INDRE-ET-LOIRE
Code postal : 38185     Departement : ISERE
Code postal : 39300     Departement : JURA
Code postal : 40192     Departement : LANDES
Code postal : 41018     Departement : LOIR-ET-CHER
Code postal : 42218     Departement : LOIRE
Code postal : 43157     Departement : HAUTE-LOIRE
Code postal : 44109     Departement : LOIRE-ATLANTIQUE
Code postal : 45234     Departement : LOIRET
Code postal : 46042     Departement : LOT
Code postal : 47001     Departement : LOT-ET-GARONNE
Code postal : 48095     Departement : LOZERE
Code postal : 49007     Departement : MAINE-ET-LOIRE
Code postal : 50502     Departement : MANCHE
Code postal : 51108     Departement : MARNE
Code postal : 52121     Departement : HAUTE-MARNE
Code postal : 53130     Departement : MAYENNE
Code postal : 54395     Departement : MEURTHE-ET-MOSELLE
Code postal : 55029     Departement : MEUSE
Code postal : 56260     Departement : MORBIHAN
Code postal : 57463     Departement : MOSELLE
Code postal : 58194     Departement : NIEVRE
Code postal : 59350     Departement : NORD
Code postal : 60057     Departement : OISE
Code postal : 61001     Departement : ORNE
Code postal : 62041     Departement : PAS-DE-CALAIS
Code postal : 63113     Departement : PUY-DE-DOME
Code postal : 64445     Departement : PYRENEES-ATLANTIQUES
Code postal : 65440     Departement : HAUTES-PYRENEES
Code postal : 66136     Departement : PYRENEES-ORIENTALES
Code postal : 67482     Departement : BAS-RHIN
Code postal : 68066     Departement : HAUT-RHIN
Code postal : 69123     Departement : RHONE
Code postal : 70550     Departement : HAUTE-SAONE
Code postal : 71270     Departement : SAONE-ET-LOIRE
Code postal : 72181     Departement : SARTHE
Code postal : 73065     Departement : SAVOIE
Code postal : 74010     Departement : HAUTE-SAVOIE
Code postal : 75056     Departement : PARIS
Code postal : 76540     Departement : SEINE-MARITIME
Code postal : 77288     Departement : SEINE-ET-MARNE
Code postal : 78646     Departement : YVELINES
Code postal : 79191     Departement : DEUX-SEVRES
Code postal : 80021     Departement : SOMME
Code postal : 81004     Departement : TARN
Code postal : 82121     Departement : TARN-ET-GARONNE
Code postal : 83137     Departement : VAR
Code postal : 84007     Departement : VAUCLUSE
Code postal : 85191     Departement : VENDEE
Code postal : 86194     Departement : VIENNE
Code postal : 87085     Departement : HAUTE-VIENNE
Code postal : 88160     Departement : VOSGES
Code postal : 89024     Departement : YONNE
Code postal : 90010     Departement : TERRITOIRE DE BELFORT
Code postal : 91228     Departement : ESSONNE
Code postal : 92050     Departement : HAUTS-DE-SEINE
Code postal : 93008     Departement : SEINE-SAINT-DENIS
Code postal : 94028     Departement : VAL-DE-MARNE
Code postal : 95500     Departement : VAL-D'OISE
Code postal : 97105     Departement : GUADELOUPE
Code postal : 97209     Departement : MARTINIQUE
Code postal : 97302     Departement : GUYANE
Code postal : 97411     Departement : LA REUNION
Code postal : 97608     Departement : MAYOTTE
---------------
Fin de la liste
---------------
$

Etiquettes: 

Le langage de programmation awk

Etiquettes: 

Principe

 Extrait de Wikipédia :

Awk est le plus souvent utilisé pour la production de fichiers plats aux spécifications particulières (échanges entre différents systèmes d'informations hétérogènes). Il est aussi utilisé comme "parser" de fichiers XML ou de fichiers textes pour générer des commandes SQL à partir des données extraites. Il peut être utilisé aussi pour des opérations de calculs complexes et mise en forme de données brutes pour faire des tableaux statistiques.

On distingue awk, la commande originale, du new awk (nawk), arrivée un peu plus tard sur le marché. Les implémentations GNU de awk, sont en fait des new awk. On trouve en général la commande awk dans /usr/bin sous Unix. Certains systèmes GNU/Linux le mettent dans /bin. En général, elle est dans la variable d'environnement PATH. Cependant, on peut faire des scripts en awk et le shebang (#!/usr/bin/awk -f) devient faux. Le script est donc inutilisable si le binaire n’est pas là où on l’attend.

Il agit comme un filtre programmable prenant une série de lignes en entrée (sous forme de fichiers ou directement via l'entrée standard) et écrivant sur la sortie standard, qui peut être redirigée vers un autre fichier ou programme. Un programme Awk est composé de trois blocs distincts utilisables ou non pour le traitement d'un fichier (prétraitement, traitement, posttraitement). Awk lit sur l'entrée ligne par ligne, puis sélectionne (ou non) les lignes à traiter par des expressions rationnelles (et éventuellement des numéros de lignes). Une fois la ligne sélectionnée, elle est découpée en champs selon un séparateur d'entrée indiqué dans le programme awk par le symbole FS (qui par défaut correspond au caractère espace ou tabulation). Puis les différents champs sont disponibles dans des variables : $1 (premier champ), $2 (deuxième champ), $3 (troisième champ), ..., $NF (dernier champ).

« awk » est aussi l'extension de nom de fichier utilisée pour les scripts écrits dans ce langage.

Etiquettes: 

Syntaxe

awk [-F] '{action-awk}' [ fichier1 fichier2 ..... fichiern ]

awk [-F] -f script-awk [ fichier1 fichier2 ..... fichiern ]

La commande awk prend en argument la liste des fichiers à traiter. En l'absence de noms de fichiers sur la ligne de commande, awk travaille sur les données arrivant sur son entrée standard. Cette commande peut donc être placée derrière un tube de communication.

L'option "-F" permet d'initialiser, si besoin, la variable "FS" (Field Separator) correspondant au caractère séparateur de champ.

Etiquettes: 

Variables spéciales

Variables prédéfinies au lancement de awk

Le tableau suivant présente les principales variables internes du langage awk présentes en mémoire dès le lancement de la commande. La valeur de ces variables peut éventuellement être modifiée en fonction de la structure des données à traiter.

Nom de la variable Valeur par défaut Rôle de la variable
RS Newline (\n) Record Separator : Caractère séparateur d'enregistrement (lignes).
FS Suite d'espaces et/ou de tabulations Field Separator : Caractères séparateurs de champs.
OFS Espace Output Field Separator : Séparateur de champ utilisé pour l'affichage.
ORS Newline (\n) Output Record Separator : Caractère séparateur d'enregistrement en sortie.
ARGV - Tableau initialisé avec les arguments de la ligne de commande (options et nom du script awk exclus).
ARGC - Nombre d'éléments contenus dans le tableau ARGV.
ENVIRON Variables d'environnement exportées par le shell. Tableau contenant les variables d'environnement exportées par le shell.
CONVFMT %.6g Format de conversion des nombres en String.
OFMT %.6g Format de sortie des nombres.
SUBSEP \034 Caractère de séparation pour les routines internes des tableaux.

Par défaut, un enregistrement correspond donc à une ligne (suite de caractères terminée par "\n").

Lorsque la variable FS est initialisée avec un minimum de 2 caractères, cette valeur est interprétée comme une expression régulière.

Variables initialisées lors du traitement d'une ligne

Les enregistrements sont traités successivement. L'enregistrement courant est automatiquement découpé en champs et un certain nombre de variables internes awk sont initialisées. Le tableau suivant donne la liste des principales variables.

Nom de la variable Valeur de la variable
$0 Valeur de l'enregistrement courant
NF Number of Field : Nombre de champs de l'enregistrement courant.
$1, $2, ... $NF $1 contient la valeur du 1er champ, $2 la valeur du 2ème champ etc etc ... et $NF la valeur du dernier champ (NF est remplacé par sa valeur).
NR Number : Indice de l'enregistrement courant (NR vaut 1 quand la 1ère ligne est en cours de traitement, puis s'incrémente dès que awk change d'enregistrement).
FNR File Number : Indice de l'enregistrement courant relatif au fichier en cours de traitement.
FILENAME Nom du fichier en cours de traitement.
RLENGTH Longueur du string trouvé par la fonction match()
RSTART Première position du string trouvé par la fonction match()

Contrairement aux variables du shell, le symbole "$" des variables awk $1, $2 etc etc... fait partie du nom des variables.

Exemples simples

Premier exemple :

Ici, awk travaille sur le résultat de la commande ps -ef. La partie en rouge représente l'action que awk doit exécuter sur chaque ligne. Les simples quotes sont indispensables pour empêcher le shell d'interpréter les caractères destinés à la commande awk. Les instructions doivent être placées entre accolades. La fonction intégrée print va afficher à l'écran les champs 1 et 8 de chaque ligne.

$ ps -ef | awk '{print $1,$8}'
UID CMD
root init
...
www-data /usr/sbin/apache2
www-data /usr/sbin/apache2
root vzctl:
root -bash
postfix pickup
root ps
root awk
root /usr/lib/postfix/master
postfix qmgr
$

Deuxième exemple :

La fonction print peut également recevoir des chaines de caractères en argument.

$ ps -ef | awk '{print "User : " , $1, "\tCommande : " , $8}'
User :  UID     Commande :  CMD
User :  root    Commande :  init
User :  root    Commande :  [kthreadd/113]
User :  root    Commande :  [khelper/113]
User :  root    Commande :  [init-logger]
...
User :  root    Commande :  vzctl:
User :  root    Commande :  -bash
User :  root    Commande :  ps
User :  root    Commande :  awk
User :  root    Commande :  /usr/lib/postfix/master
$

\t représente le caractère tabulation.

Troisième exemple :

Modification du séparateur de champ "FS" grâce à l'option "-F"

$ cat /etc/passwd | awk -F : '{print $1,$7}'
root /bin/bash
...
nobody /bin/sh
libuuid /bin/sh
postfix /bin/false
sshd /usr/sbin/nologin
mysql /bin/false
$

Quatrième exemple :

$ cat /etc/passwd | awk -F : '{print "User : " , $1 , "\tShell : " , $7}'
User :  root    Shell :  /bin/bash
User :  daemon  Shell :  /bin/sh
User :  bin     Shell :  /bin/sh
User :  sys     Shell :  /bin/sh
User :  sync    Shell :  /bin/sync
User :  games   Shell :  /bin/sh
User :  man     Shell :  /bin/sh
User :  lp      Shell :  /bin/sh
User :  mail    Shell :  /bin/sh
User :  news    Shell :  /bin/sh
User :  uucp    Shell :  /bin/sh
User :  proxy   Shell :  /bin/sh
User :  www-data        Shell :  /bin/sh
User :  backup  Shell :  /bin/sh
User :  list    Shell :  /bin/sh
User :  irc     Shell :  /bin/sh
User :  gnats   Shell :  /bin/sh
User :  nobody  Shell :  /bin/sh
User :  libuuid         Shell :  /bin/sh
User :  postfix         Shell :  /bin/false
User :  sshd    Shell :  /usr/sbin/nologin
User :  mysql   Shell :  /bin/false
$

Si la fonction print ne reçoit pas d'argument, elle affiche $0.

$ cat /etc/passwd | awk -F : '{print}'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

Etiquettes: 

Critères de sélection

Il est possible de sélectionner les enregistrements sur lesquels l'action doit être exécutée.

Syntaxe :

awk [-F] 'critère {action-awk}' [fichier1 fichier2 ... fichiern]

Le critère de sélection peut s'exprimer de différentes manières.

Expressions régulières

Les enregistrements à traiter peuvent être sélectionnés en utilisant les expressions régulières étendues (ERe).

Attention, pour utiliser les quantificateurs {x,y} d'une ERe, il faut utiliser awk avec l'option --posix

Premier exemple :

Afficher les lignes du fichier /etc/passwd contenant /bin/false

$ awk -F':' '/\/bin\/false/ {print $0}' /etc/passwd
postfix:x:101:104::/var/spool/postfix:/bin/false
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

Par défaut, le critère est mis en correspondance avec $0.

Il est possible de mettre un champ particulier en correspondance avec une expression régulière. Dans ce cas, il faut utiliser l'opérateur de concordance "~" ou de non-concordance "!~".

Deuxième exemple :

Afficher les lignes du fichier /etc/passwd dont le 6ème champ commence par /usr.

$ awk -F':' '$6 ~ /^\/usr/ {print $0}' /etc/passwd
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
$

Et à l'inverse

$ awk -F':' '$6 !~ /^\/usr/ {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:2:2:bin:/bin:/bin/sh
...
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

Tests logiques

Le critère peut être une expression d'opérateurs et renvoyant la valeur de vérité vrai ou faux.

Opérateurs de tests courants

Opérateur Signification
< Inférieur
> Supérieur
<= Inférieur ou égal
>= Supérieur ou égal
== Test d'égalité
!= Test d'inégalité
~ Correspondance avec une expression régulière
!~ Non-correspondance avec une expression régulière
! Négation
&& Et logique
|| Ou logique
(expression) Regroupement

Exemple :

Afficher les champs 1 et 7 de la ligne 4 et 8 du fichier /etc/passwd

$ awk -F':' 'NR == 4 || NR == 8 {print $1,"==>",$7}' /etc/passwd
sys ==> /bin/sh
lp ==> /bin/sh
$

Le chiffre 0 et la chaîne vide sont des valeurs fausses. Toute autre valeur est vraie.

Afficher uniquement les lignes impaires du fichier /etc/passwd.

Le résultat de l'opération NR%2 représentant le reste de la division par 2 du numéro de ligne en cours de traitement, cela permet d'afficher uniquement les lignes impaires du fichier car seulement les nombres impairs renvoient un reste de division différent de 0 (donc vrai). Equivalent à NR%2!=0

$ nl /etc/passwd
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     3  bin:x:2:2:bin:/bin:/bin/sh
     4  sys:x:3:3:sys:/dev:/bin/sh
     5  sync:x:4:65534:sync:/bin:/bin/sync
     6  games:x:5:60:games:/usr/games:/bin/sh
     7  man:x:6:12:man:/var/cache/man:/bin/sh
     8  lp:x:7:7:lp:/var/spool/lpd:/bin/sh
     9  mail:x:8:8:mail:/var/mail:/bin/sh
    10  news:x:9:9:news:/var/spool/news:/bin/sh
    11  uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
    12  proxy:x:13:13:proxy:/bin:/bin/sh
    13  www-data:x:33:33:www-data:/var/www:/bin/sh
    14  backup:x:34:34:backup:/var/backups:/bin/sh
    15  list:x:38:38:Mailing List Manager:/var/list:/bin/sh
    16  irc:x:39:39:ircd:/var/run/ircd:/bin/sh
    17  gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
    18  nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
    19  libuuid:x:100:101::/var/lib/libuuid:/bin/sh
    20  postfix:x:101:104::/var/spool/postfix:/bin/false
    21  sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
    22  mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

$ awk -F':' 'NR%2 {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:2:2:bin:/bin:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
$

A l'inverse, afficher uniquement les lignes paires.

$ awk -F':' 'NR%2==0 {print $0}' /etc/passwd
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

Afficher les champs 1 et 7 de la ligne 4 à 8 du fichier /etc/passwd

$ awk -F':' 'NR == 4 , NR == 8 {print $1,"==>",$7}' /etc/passwd
sys ==> /bin/sh
sync ==> /bin/sync
games ==> /bin/sh
man ==> /bin/sh
lp ==> /bin/sh
$

Etiquettes: 

Structure d'un script awk

Lorsqu'il y a un certain nombre d'actions à réaliser sur les données, il est plus confortable d'écrire un script awk. Un script awk peut contenir une section BEGIN, une section END, et 0 à x sections intermédiaires. Toute section est facultative.

BEGIN

La section BEGIN est exécutée avant le traitement du premier enregistrement des données. Elle est utilisée essentiellement pour initialiser le contexte d'exécution.

Sections intermédiaires

Il peut y avoir plusieurs sections intermédiaires qui seront exécutées sur chaque enregistrement.

END

La section END est exécutée après le traitement du dernier enregistrement des données. Elle est utilisée pour exploiter les résultats issus du traitement des données.

Commentaires

Un commentaire commence par le caractère "#" et se termine au caractère "\n" (fin de la ligne).

Variables

Des variables personnelles peuvent être créées. Une variable est définie dès qu'elle est initialisée et n'a pas besoin d'être typée. L'utilisation d'une variable qui n'a jamais été définie a pour valeur 0 dans un contexte numérique et chaine vide dans un contexte de chaine.

Exemple :

$ nl script1.awk
     1  # Section BEGIN
     2  BEGIN {
     3          print "Section BEGIN"
     4          nb_0=0
     5          nb_1=0
     6          nb_2=0
     7          nb_3=0
     8          nb_4=0
     9          nb_5=0
    10          nb_6=0
    11          nb_7=0
    12          nb_8=0
    13          nb_9=0
    14  }
    15  # Section intermediaire
    16  # Traitement des departements commancant par 0
    17  $2 ~ /^0/ {
    18          print "Departement commancant par 0 ==> CP : " , $3 , "DEPT : " , $5
    19          nb_0+=1
    20  }
    21  # Section intermediaire
    22  # Traitement des departements commancant par 1
    23  $2 ~ /^1/ {
    24          print "Departement commancant par 1 ==> CP : " , $3 , "DEPT : " , $5
    25          nb_1+=1
    26  }
    27  # Section intermediaire
    28  # Traitement des departements commancant par 2
    29  $2 ~ /^2/ {
    30          print "Departement commancant par 2 ==> CP : " , $3 , "DEPT : " , $5
    31          nb_2+=1
    32  }
    33  # Section intermediaire
    34  # Traitement des departements commancant par 3
    35  $2 ~ /^3/ {
    36          print "Departement commancant par 3 ==> CP : " , $3 , "DEPT : " , $5
    37          nb_3+=1
    38  }
    39  # Section intermediaire
    40  # Traitement des departements commancant par 4
    41  $2 ~ /^4/ {
    42          print "Departement commancant par 4 ==> CP : " , $3 , "DEPT : " , $5
    43          nb_4+=1
    44  }
    45  # Section intermediaire
    46  # Traitement des departements commancant par 5
    47  $2 ~ /^5/ {
    48          print "Departement commancant par 5 ==> CP : " , $3 , "DEPT : " , $5
    49          nb_5+=1
    50  }
    51  # Section intermediaire
    52  # Traitement des departements commancant par 6
    53  $2 ~ /^6/ {
    54          print "Departement commancant par 6 ==> CP : " , $3 , "DEPT : " , $5
    55          nb_6+=1
    56  }
    57  # Section intermediaire
    58  # Traitement des departements commancant par 7
    59  $2 ~ /^7/ {
    60          print "Departement commancant par 7 ==> CP : " , $3 , "DEPT : " , $5
    61          nb_7+=1
    62  }
    63  # Section intermediaire
    64  # Traitement des departements commancant par 8
    65  $2 ~ /^8/ {
    66          print "Departement commancant par 8 ==> CP : " , $3 , "DEPT : " , $5
    67          nb_8+=1
    68  }
    69  # Section intermediaire
    70  # Traitement des departements commancant par 9
    71  $2 ~ /^9/ {
    72          print "Departement commancant par 9 ==> CP : " , $3 , "DEPT : " , $5
    73          nb_9+=1
    74  }
    75  # Section END
    76  END {
    77          print "Section END"
    78          print "Nombre total de lignes : " , NR
    79          print "Nombre de departements commencant par 0 : " , nb_0
    80          print "Nombre de departements commencant par 1 : " , nb_1
    81          print "Nombre de departements commencant par 2 : " , nb_2
    82          print "Nombre de departements commencant par 3 : " , nb_3
    83          print "Nombre de departements commencant par 4 : " , nb_4
    84          print "Nombre de departements commencant par 5 : " , nb_5
    85          print "Nombre de departements commencant par 6 : " , nb_6
    86          print "Nombre de departements commencant par 7 : " , nb_7
    87          print "Nombre de departements commencant par 8 : " , nb_8
    88          print "Nombre de departements commencant par 9 : " , nb_9
    89  }
$

Section BEGIN

Initialisation des variables personnelles servant de compteur.

Sections intermédiaires

Exécution des traitements spécifiques en fonction du début du numéro des départements.

Section END

Affichage du nombre total de lignes traitées et du nombre de départements regroupés par dizaine du numéro de départements.

Exécution du script

$ awk -f script1.awk depts2012.txt
Section BEGIN
Departement commancant par 0 ==> CP :  01053 DEPT :  AIN
Departement commancant par 0 ==> CP :  02408 DEPT :  AISNE
Departement commancant par 0 ==> CP :  03190 DEPT :  ALLIER
Departement commancant par 0 ==> CP :  04070 DEPT :  ALPES-DE-HAUTE-PROVENCE
Departement commancant par 0 ==> CP :  05061 DEPT :  HAUTES-ALPES
Departement commancant par 0 ==> CP :  06088 DEPT :  ALPES-MARITIMES
Departement commancant par 0 ==> CP :  07186 DEPT :  ARDECHE
Departement commancant par 0 ==> CP :  08105 DEPT :  ARDENNES
Departement commancant par 0 ==> CP :  09122 DEPT :  ARIEGE
Departement commancant par 1 ==> CP :  10387 DEPT :  AUBE
Departement commancant par 1 ==> CP :  11069 DEPT :  AUDE
Departement commancant par 1 ==> CP :  12202 DEPT :  AVEYRON
Departement commancant par 1 ==> CP :  13055 DEPT :  BOUCHES-DU-RHONE
Departement commancant par 1 ==> CP :  14118 DEPT :  CALVADOS
Departement commancant par 1 ==> CP :  15014 DEPT :  CANTAL
Departement commancant par 1 ==> CP :  16015 DEPT :  CHARENTE
Departement commancant par 1 ==> CP :  17300 DEPT :  CHARENTE-MARITIME
Departement commancant par 1 ==> CP :  18033 DEPT :  CHER
Departement commancant par 1 ==> CP :  19272 DEPT :  CORREZE
Departement commancant par 2 ==> CP :  2A004 DEPT :  CORSE-DU-SUD
Departement commancant par 2 ==> CP :  2B033 DEPT :  HAUTE-CORSE
Departement commancant par 2 ==> CP :  21231 DEPT :  COTE-D'OR
Departement commancant par 2 ==> CP :  22278 DEPT :  COTES-D'ARMOR
Departement commancant par 2 ==> CP :  23096 DEPT :  CREUSE
Departement commancant par 2 ==> CP :  24322 DEPT :  DORDOGNE
Departement commancant par 2 ==> CP :  25056 DEPT :  DOUBS
Departement commancant par 2 ==> CP :  26362 DEPT :  DROME
Departement commancant par 2 ==> CP :  27229 DEPT :  EURE
Departement commancant par 2 ==> CP :  28085 DEPT :  EURE-ET-LOIR
Departement commancant par 2 ==> CP :  29232 DEPT :  FINISTERE
Departement commancant par 3 ==> CP :  30189 DEPT :  GARD
Departement commancant par 3 ==> CP :  31555 DEPT :  HAUTE-GARONNE
Departement commancant par 3 ==> CP :  32013 DEPT :  GERS
Departement commancant par 3 ==> CP :  33063 DEPT :  GIRONDE
Departement commancant par 3 ==> CP :  34172 DEPT :  HERAULT
Departement commancant par 3 ==> CP :  35238 DEPT :  ILLE-ET-VILAINE
Departement commancant par 3 ==> CP :  36044 DEPT :  INDRE
Departement commancant par 3 ==> CP :  37261 DEPT :  INDRE-ET-LOIRE
Departement commancant par 3 ==> CP :  38185 DEPT :  ISERE
Departement commancant par 3 ==> CP :  39300 DEPT :  JURA
Departement commancant par 4 ==> CP :  40192 DEPT :  LANDES
Departement commancant par 4 ==> CP :  41018 DEPT :  LOIR-ET-CHER
Departement commancant par 4 ==> CP :  42218 DEPT :  LOIRE
Departement commancant par 4 ==> CP :  43157 DEPT :  HAUTE-LOIRE
Departement commancant par 4 ==> CP :  44109 DEPT :  LOIRE-ATLANTIQUE
Departement commancant par 4 ==> CP :  45234 DEPT :  LOIRET
Departement commancant par 4 ==> CP :  46042 DEPT :  LOT
Departement commancant par 4 ==> CP :  47001 DEPT :  LOT-ET-GARONNE
Departement commancant par 4 ==> CP :  48095 DEPT :  LOZERE
Departement commancant par 4 ==> CP :  49007 DEPT :  MAINE-ET-LOIRE
Departement commancant par 5 ==> CP :  50502 DEPT :  MANCHE
Departement commancant par 5 ==> CP :  51108 DEPT :  MARNE
Departement commancant par 5 ==> CP :  52121 DEPT :  HAUTE-MARNE
Departement commancant par 5 ==> CP :  53130 DEPT :  MAYENNE
Departement commancant par 5 ==> CP :  54395 DEPT :  MEURTHE-ET-MOSELLE
Departement commancant par 5 ==> CP :  55029 DEPT :  MEUSE
Departement commancant par 5 ==> CP :  56260 DEPT :  MORBIHAN
Departement commancant par 5 ==> CP :  57463 DEPT :  MOSELLE
Departement commancant par 5 ==> CP :  58194 DEPT :  NIEVRE
Departement commancant par 5 ==> CP :  59350 DEPT :  NORD
Departement commancant par 6 ==> CP :  60057 DEPT :  OISE
Departement commancant par 6 ==> CP :  61001 DEPT :  ORNE
Departement commancant par 6 ==> CP :  62041 DEPT :  PAS-DE-CALAIS
Departement commancant par 6 ==> CP :  63113 DEPT :  PUY-DE-DOME
Departement commancant par 6 ==> CP :  64445 DEPT :  PYRENEES-ATLANTIQUES
Departement commancant par 6 ==> CP :  65440 DEPT :  HAUTES-PYRENEES
Departement commancant par 6 ==> CP :  66136 DEPT :  PYRENEES-ORIENTALES
Departement commancant par 6 ==> CP :  67482 DEPT :  BAS-RHIN
Departement commancant par 6 ==> CP :  68066 DEPT :  HAUT-RHIN
Departement commancant par 6 ==> CP :  69123 DEPT :  RHONE
Departement commancant par 7 ==> CP :  70550 DEPT :  HAUTE-SAONE
Departement commancant par 7 ==> CP :  71270 DEPT :  SAONE-ET-LOIRE
Departement commancant par 7 ==> CP :  72181 DEPT :  SARTHE
Departement commancant par 7 ==> CP :  73065 DEPT :  SAVOIE
Departement commancant par 7 ==> CP :  74010 DEPT :  HAUTE-SAVOIE
Departement commancant par 7 ==> CP :  75056 DEPT :  PARIS
Departement commancant par 7 ==> CP :  76540 DEPT :  SEINE-MARITIME
Departement commancant par 7 ==> CP :  77288 DEPT :  SEINE-ET-MARNE
Departement commancant par 7 ==> CP :  78646 DEPT :  YVELINES
Departement commancant par 7 ==> CP :  79191 DEPT :  DEUX-SEVRES
Departement commancant par 8 ==> CP :  80021 DEPT :  SOMME
Departement commancant par 8 ==> CP :  81004 DEPT :  TARN
Departement commancant par 8 ==> CP :  82121 DEPT :  TARN-ET-GARONNE
Departement commancant par 8 ==> CP :  83137 DEPT :  VAR
Departement commancant par 8 ==> CP :  84007 DEPT :  VAUCLUSE
Departement commancant par 8 ==> CP :  85191 DEPT :  VENDEE
Departement commancant par 8 ==> CP :  86194 DEPT :  VIENNE
Departement commancant par 8 ==> CP :  87085 DEPT :  HAUTE-VIENNE
Departement commancant par 8 ==> CP :  88160 DEPT :  VOSGES
Departement commancant par 8 ==> CP :  89024 DEPT :  YONNE
Departement commancant par 9 ==> CP :  90010 DEPT :  TERRITOIRE
Departement commancant par 9 ==> CP :  91228 DEPT :  ESSONNE
Departement commancant par 9 ==> CP :  92050 DEPT :  HAUTS-DE-SEINE
Departement commancant par 9 ==> CP :  93008 DEPT :  SEINE-SAINT-DENIS
Departement commancant par 9 ==> CP :  94028 DEPT :  VAL-DE-MARNE
Departement commancant par 9 ==> CP :  95500 DEPT :  VAL-D'OISE
Departement commancant par 9 ==> CP :  97105 DEPT :  GUADELOUPE
Departement commancant par 9 ==> CP :  97209 DEPT :  MARTINIQUE
Departement commancant par 9 ==> CP :  97302 DEPT :  GUYANE
Departement commancant par 9 ==> CP :  97411 DEPT :  LA
Departement commancant par 9 ==> CP :  97608 DEPT :  MAYOTTE
Section END
Nombre total de lignes :  102
Nombre de departements commencant par 0 :  9
Nombre de departements commencant par 1 :  10
Nombre de departements commencant par 2 :  11
Nombre de departements commencant par 3 :  10
Nombre de departements commencant par 4 :  10
Nombre de departements commencant par 5 :  10
Nombre de departements commencant par 6 :  10
Nombre de departements commencant par 7 :  10
Nombre de departements commencant par 8 :  10
Nombre de departements commencant par 9 :  11
$

Etiquettes: 

Opérateurs

Liste des opérateurs disponible dans awk.

Opérateurs arithmétiques

Opérateur Arité Signification
+ Binaire Addition
- Binaire Soustraction
* Binaire Multiplication
/ Binaire Division
% Binaire Modulo
^ Binaire Exponentiation
++ Unaire Incrémentation d'une variable d'une unité
-- Unaire Décrémentation d'une variable d'une unité
+= Binaire a+=b equivalent à a=a+b
-= Binaire a-=b équivalent à a=a-b
*= Binaire a*=b équivalent à a=a*b
/= Binaire a/=b équivalent à a=a/b
%= Binaire a%=b équivalent à a=a%b
^= Binaire a^=b équivalent à a=a^b

Opérateurs de tests

Opérateur Arité Signification
< Binaire Inférieur
> Binaire Supérieur
<= Binaire Inférieur ou égal
>= Binaire Supérieur ou égal
== Binaire Test d'égalité
!= Binaire Test d'inégalité
~ Binaire Correspondance avec une ERe
!~ Binaire Non correspondance avec une ERe

Opérateurs logiques

Opérateur Arité Signification
! Binaire Négation
&& Binaire ET logique
|| Binaire OU logique

Divers

Opérateur Arité Signification
= Binaire Affectation
e1 ? e2 : e3 Ternaire Le résultat est égal à e2 si e1 est vrai, égal à e3 si e1 est faux
e1 e2 Binaire Concaténation de e1 et e2

 

Etiquettes: 

La fonction printf

awk propose la fonction intégrée printf similaire à celle du langage C. Elle permet de formater les affichages.

printf ("chaine",field1,field2,field3...fieldx)

chaine représente la chaine affichée à l'écran. Elle peut contenir des formats qui seront substitués par la valeur des expressions citées à la suite. Il doit y avoir autant de formats que d'expressions.

Formats souvent utilisés

Format Signification
%20s Affichage d'une chaine (string) sur 20 caractères (cadrage à droite par défaut)
%-20s Affichage d'une chaine (string) sur 20 caractères avec cadrage à gauche
%3d Affichage d'un entier/décimal sur 3 chiffres (cadrage à droite)
%03d Affichage d'un entier/décimal sur 3 chiffres (cadrage à droite) complété par des 0 à gauche
%-3d Affichage d'un entier/décimal sur 3 chiffres (cadrage à gauche)
%+3d Affichage d'un entier/décimal sur 3 chiffres (cadrage à droite) avec affichage systématique du signe (un nombre négatif est toujours affiché avec son signe)
%10.2f Affichage d'un nombre flottant sur 10 chiffres dont 2 décimales. (cadrage à droite)
%+010.2f Affichage d'un nombre flottant sur 10 chiffres dont 2 décimales, cadrage à droite, affichage systématique du signe, complétion par des 0 à gauche.

Exemple :

$ date | awk '{printf "%10s\n%-10s\n%d\n%15d\n%015d\n%-10d\n%+10d\n%10.2f\n%+010.2f\n" , $1 , $1 , $4 , $4 , $4 , $4 , $4 , 5.2 , 5.2}'
      mer.       # %10s
mer.       # %-10s
2012       # %d
           2012       # %15d
000000000002012       # %015d
2012       # %-10d
     +2012       # %+10d
      5.20       # %10.2f
+000005.20       # %+010.2f
$

Etiquettes: 

Redirections

Il est possible de rediriger les sorties du script vers un fichier ou vers une commande du système.

Syntaxe

instruction > "fichier" Au premier appel, le fichier est ouvert en mode "écrasement", puis écriture. Les écritures suivantes se font en mode "ajout"
instruction >> "fichier" Au premier appel, le fichier est ouvert en mode "ajout", puis écriture. Les écritures suivantes se font également en mode "ajout"
print[f] "..." | "commande" Le résultat de la fonction print/printf est transmise sur l'entrée standard de la commande système par l'intermédiaire d'un tube (pipe)

Exemple 1

Ouverture en mode "écrasement"

$ nl script2.awk
     1  BEGIN {
     2          fichier = "/root/fichier1.txt"
     3          print "Ligne 1" > fichier
     4          print "Ligne 2" > fichier
     5          print "Ligne 3" > fichier
     6          close(fichier)
     7  }
$ uptime > /root/fichier1.txt
$ cat /root/fichier1.txt
 13:47:18 up 7 days,  5:28,  0 users,  load average: 0.00, 0.00, 0.00
$ awk -f script2.awk
$ cat /root/fichier1.txt
Ligne 1
Ligne 2
Ligne 3

$

Exemple 2

Ouverture en mode "ajout"

$ nl script3.awk
     1  BEGIN {
     2          fichier = "/root/fichier1.txt"
     3          print "Ligne 1" >> fichier
     4          print "Ligne 2" > fichier
     5          print "Ligne 3" > fichier
     6          close(fichier)
     7  }
$ uptime > /root/fichier1.txt
$ cat /root/fichier1.txt
 13:50:40 up 7 days,  5:31,  0 users,  load average: 0.00, 0.00, 0.00
$ awk -f script3.awk
$ cat /root/fichier1.txt
 13:50:40 up 7 days,  5:31,  0 users,  load average: 0.00, 0.00, 0.00
Ligne 1
Ligne 2
Ligne 3

$

Exemple 3

Ecriture dans un tube. Trier les lignes par users croissants.
La commande sort est exécutée une seule fois puis fermée dans la section END.

$ nl /etc/passwd
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     3  bin:x:2:2:bin:/bin:/bin/sh
     4  sys:x:3:3:sys:/dev:/bin/sh
     5  sync:x:4:65534:sync:/bin:/bin/sync
     6  games:x:5:60:games:/usr/games:/bin/sh
     7  man:x:6:12:man:/var/cache/man:/bin/sh
     8  lp:x:7:7:lp:/var/spool/lpd:/bin/sh
     9  mail:x:8:8:mail:/var/mail:/bin/sh
    10  news:x:9:9:news:/var/spool/news:/bin/sh
    11  uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
    12  proxy:x:13:13:proxy:/bin:/bin/sh
    13  www-data:x:33:33:www-data:/var/www:/bin/sh
    14  backup:x:34:34:backup:/var/backups:/bin/sh
    15  list:x:38:38:Mailing List Manager:/var/list:/bin/sh
    16  irc:x:39:39:ircd:/var/run/ircd:/bin/sh
    17  gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
    18  nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
    19  libuuid:x:100:101::/var/lib/libuuid:/bin/sh
    20  postfix:x:101:104::/var/spool/postfix:/bin/false
    21  sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
    22  mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$ awk -F':' '{print $0 | "sort"} END {close("sort")}' /etc/passwd
backup:x:34:34:backup:/var/backups:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
news:x:9:9:news:/var/spool/news:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
proxy:x:13:13:proxy:/bin:/bin/sh
root:x:0:0:root:/root:/bin/bash
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
sys:x:3:3:sys:/dev:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
$

Etiquettes: 

Lecture de la ligne suivante : next

L'instruction next interrompt le traitement de la ligne courante et déclenche la lecture de la ligne suivante.

Exemple

Dans le script suivant, les instruction next permettent d'accélérer l'exécution en évitant le traitement de la ligne courante dans les sections suivantes celle où la ligne a été traitée.

$ nl script4.awk
     1  # Section BEGIN
     2  BEGIN {
     3          nb_0=0
     4          nb_1=0
     5          nb_2=0
     6          nb_3=0
     7          nb_4=0
     8          nb_5=0
     9          nb_6=0
    10          nb_7=0
    11          nb_8=0
    12          nb_9=0
    13  }
    14  # Section intermediaire
    15  # Traitement des departements commancant par 0
    16  $2 ~ /^0/ {
    17          print "Departement commancant par 0 ==> CP : " , $3 , "DEPT : " , $5
    18          nb_0+=1
    19          next
    20  }
    21  # Section intermediaire
    22  # Traitement des departements commancant par 1
    23  $2 ~ /^1/ {
    24          print "Departement commancant par 1 ==> CP : " , $3 , "DEPT : " , $5
    25          nb_1+=1
    26          next
    27  }
    28  # Section intermediaire
    29  # Traitement des departements commancant par 2
    30  $2 ~ /^2/ {
    31          print "Departement commancant par 2 ==> CP : " , $3 , "DEPT : " , $5
    32          nb_2+=1
    33          next
    34  }
    35  # Section intermediaire
    36  # Traitement des departements commancant par 3
    37  $2 ~ /^3/ {
    38          print "Departement commancant par 3 ==> CP : " , $3 , "DEPT : " , $5
    39          nb_3+=1
    40          next
    41  }
    42  # Section intermediaire
    43  # Traitement des departements commancant par 4
    44  $2 ~ /^4/ {
    45          print "Departement commancant par 4 ==> CP : " , $3 , "DEPT : " , $5
    46          nb_4+=1
    47          next
    48  }
    49  # Section intermediaire
    50  # Traitement des departements commancant par 5
    51  $2 ~ /^5/ {
    52          print "Departement commancant par 5 ==> CP : " , $3 , "DEPT : " , $5
    53          nb_5+=1
    54          next
    55  }
    56  # Section intermediaire
    57  # Traitement des departements commancant par 6
    58  $2 ~ /^6/ {
    59          print "Departement commancant par 6 ==> CP : " , $3 , "DEPT : " , $5
    60          nb_6+=1
    61          next
    62  }
    63  # Section intermediaire
    64  # Traitement des departements commancant par 7
    65  $2 ~ /^7/ {
    66          print "Departement commancant par 7 ==> CP : " , $3 , "DEPT : " , $5
    67          nb_7+=1
    68          next
    69  }
    70  # Section intermediaire
    71  # Traitement des departements commancant par 8
    72  $2 ~ /^8/ {
    73          print "Departement commancant par 8 ==> CP : " , $3 , "DEPT : " , $5
    74          nb_8+=1
    75          next
    76  }
    77  # Section intermediaire
    78  # Traitement des departements commancant par 9
    79  $2 ~ /^9/ {
    80          print "Departement commancant par 9 ==> CP : " , $3 , "DEPT : " , $5
    81          nb_9+=1
    82          next
    83  }
    84  # Section END
    85  END {
    86          print "Nombre total de lignes : " , NR
    87          print "Nombre de departements commencant par 0 : " , nb_0
    88          print "Nombre de departements commencant par 1 : " , nb_1
    89          print "Nombre de departements commencant par 2 : " , nb_2
    90          print "Nombre de departements commencant par 3 : " , nb_3
    91          print "Nombre de departements commencant par 4 : " , nb_4
    92          print "Nombre de departements commencant par 5 : " , nb_5
    93          print "Nombre de departements commencant par 6 : " , nb_6
    94          print "Nombre de departements commencant par 7 : " , nb_7
    95          print "Nombre de departements commencant par 8 : " , nb_8
    96          print "Nombre de departements commencant par 9 : " , nb_9
    97  }
$

$ awk -f script4.awk depts2012.txt
Departement commancant par 0 ==> CP :  01053 DEPT :  AIN
Departement commancant par 0 ==> CP :  02408 DEPT :  AISNE
Departement commancant par 0 ==> CP :  03190 DEPT :  ALLIER
Departement commancant par 0 ==> CP :  04070 DEPT :  ALPES-DE-HAUTE-PROVENCE
Departement commancant par 0 ==> CP :  05061 DEPT :  HAUTES-ALPES
Departement commancant par 0 ==> CP :  06088 DEPT :  ALPES-MARITIMES
Departement commancant par 0 ==> CP :  07186 DEPT :  ARDECHE
Departement commancant par 0 ==> CP :  08105 DEPT :  ARDENNES
Departement commancant par 0 ==> CP :  09122 DEPT :  ARIEGE
Departement commancant par 1 ==> CP :  10387 DEPT :  AUBE
Departement commancant par 1 ==> CP :  11069 DEPT :  AUDE
Departement commancant par 1 ==> CP :  12202 DEPT :  AVEYRON
Departement commancant par 1 ==> CP :  13055 DEPT :  BOUCHES-DU-RHONE
Departement commancant par 1 ==> CP :  14118 DEPT :  CALVADOS
Departement commancant par 1 ==> CP :  15014 DEPT :  CANTAL
Departement commancant par 1 ==> CP :  16015 DEPT :  CHARENTE
Departement commancant par 1 ==> CP :  17300 DEPT :  CHARENTE-MARITIME
Departement commancant par 1 ==> CP :  18033 DEPT :  CHER
Departement commancant par 1 ==> CP :  19272 DEPT :  CORREZE
Departement commancant par 2 ==> CP :  2A004 DEPT :  CORSE-DU-SUD
Departement commancant par 2 ==> CP :  2B033 DEPT :  HAUTE-CORSE
Departement commancant par 2 ==> CP :  21231 DEPT :  COTE-D'OR
Departement commancant par 2 ==> CP :  22278 DEPT :  COTES-D'ARMOR
Departement commancant par 2 ==> CP :  23096 DEPT :  CREUSE
Departement commancant par 2 ==> CP :  24322 DEPT :  DORDOGNE
Departement commancant par 2 ==> CP :  25056 DEPT :  DOUBS
Departement commancant par 2 ==> CP :  26362 DEPT :  DROME
Departement commancant par 2 ==> CP :  27229 DEPT :  EURE
Departement commancant par 2 ==> CP :  28085 DEPT :  EURE-ET-LOIR
Departement commancant par 2 ==> CP :  29232 DEPT :  FINISTERE
Departement commancant par 3 ==> CP :  30189 DEPT :  GARD
Departement commancant par 3 ==> CP :  31555 DEPT :  HAUTE-GARONNE
Departement commancant par 3 ==> CP :  32013 DEPT :  GERS
Departement commancant par 3 ==> CP :  33063 DEPT :  GIRONDE
Departement commancant par 3 ==> CP :  34172 DEPT :  HERAULT
Departement commancant par 3 ==> CP :  35238 DEPT :  ILLE-ET-VILAINE
Departement commancant par 3 ==> CP :  36044 DEPT :  INDRE
Departement commancant par 3 ==> CP :  37261 DEPT :  INDRE-ET-LOIRE
Departement commancant par 3 ==> CP :  38185 DEPT :  ISERE
Departement commancant par 3 ==> CP :  39300 DEPT :  JURA
Departement commancant par 4 ==> CP :  40192 DEPT :  LANDES
Departement commancant par 4 ==> CP :  41018 DEPT :  LOIR-ET-CHER
Departement commancant par 4 ==> CP :  42218 DEPT :  LOIRE
Departement commancant par 4 ==> CP :  43157 DEPT :  HAUTE-LOIRE
Departement commancant par 4 ==> CP :  44109 DEPT :  LOIRE-ATLANTIQUE
Departement commancant par 4 ==> CP :  45234 DEPT :  LOIRET
Departement commancant par 4 ==> CP :  46042 DEPT :  LOT
Departement commancant par 4 ==> CP :  47001 DEPT :  LOT-ET-GARONNE
Departement commancant par 4 ==> CP :  48095 DEPT :  LOZERE
Departement commancant par 4 ==> CP :  49007 DEPT :  MAINE-ET-LOIRE
Departement commancant par 5 ==> CP :  50502 DEPT :  MANCHE
Departement commancant par 5 ==> CP :  51108 DEPT :  MARNE
Departement commancant par 5 ==> CP :  52121 DEPT :  HAUTE-MARNE
Departement commancant par 5 ==> CP :  53130 DEPT :  MAYENNE
Departement commancant par 5 ==> CP :  54395 DEPT :  MEURTHE-ET-MOSELLE
Departement commancant par 5 ==> CP :  55029 DEPT :  MEUSE
Departement commancant par 5 ==> CP :  56260 DEPT :  MORBIHAN
Departement commancant par 5 ==> CP :  57463 DEPT :  MOSELLE
Departement commancant par 5 ==> CP :  58194 DEPT :  NIEVRE
Departement commancant par 5 ==> CP :  59350 DEPT :  NORD
Departement commancant par 6 ==> CP :  60057 DEPT :  OISE
Departement commancant par 6 ==> CP :  61001 DEPT :  ORNE
Departement commancant par 6 ==> CP :  62041 DEPT :  PAS-DE-CALAIS
Departement commancant par 6 ==> CP :  63113 DEPT :  PUY-DE-DOME
Departement commancant par 6 ==> CP :  64445 DEPT :  PYRENEES-ATLANTIQUES
Departement commancant par 6 ==> CP :  65440 DEPT :  HAUTES-PYRENEES
Departement commancant par 6 ==> CP :  66136 DEPT :  PYRENEES-ORIENTALES
Departement commancant par 6 ==> CP :  67482 DEPT :  BAS-RHIN
Departement commancant par 6 ==> CP :  68066 DEPT :  HAUT-RHIN
Departement commancant par 6 ==> CP :  69123 DEPT :  RHONE
Departement commancant par 7 ==> CP :  70550 DEPT :  HAUTE-SAONE
Departement commancant par 7 ==> CP :  71270 DEPT :  SAONE-ET-LOIRE
Departement commancant par 7 ==> CP :  72181 DEPT :  SARTHE
Departement commancant par 7 ==> CP :  73065 DEPT :  SAVOIE
Departement commancant par 7 ==> CP :  74010 DEPT :  HAUTE-SAVOIE
Departement commancant par 7 ==> CP :  75056 DEPT :  PARIS
Departement commancant par 7 ==> CP :  76540 DEPT :  SEINE-MARITIME
Departement commancant par 7 ==> CP :  77288 DEPT :  SEINE-ET-MARNE
Departement commancant par 7 ==> CP :  78646 DEPT :  YVELINES
Departement commancant par 7 ==> CP :  79191 DEPT :  DEUX-SEVRES
Departement commancant par 8 ==> CP :  80021 DEPT :  SOMME
Departement commancant par 8 ==> CP :  81004 DEPT :  TARN
Departement commancant par 8 ==> CP :  82121 DEPT :  TARN-ET-GARONNE
Departement commancant par 8 ==> CP :  83137 DEPT :  VAR
Departement commancant par 8 ==> CP :  84007 DEPT :  VAUCLUSE
Departement commancant par 8 ==> CP :  85191 DEPT :  VENDEE
Departement commancant par 8 ==> CP :  86194 DEPT :  VIENNE
Departement commancant par 8 ==> CP :  87085 DEPT :  HAUTE-VIENNE
Departement commancant par 8 ==> CP :  88160 DEPT :  VOSGES
Departement commancant par 8 ==> CP :  89024 DEPT :  YONNE
Departement commancant par 9 ==> CP :  90010 DEPT :  TERRITOIRE
Departement commancant par 9 ==> CP :  91228 DEPT :  ESSONNE
Departement commancant par 9 ==> CP :  92050 DEPT :  HAUTS-DE-SEINE
Departement commancant par 9 ==> CP :  93008 DEPT :  SEINE-SAINT-DENIS
Departement commancant par 9 ==> CP :  94028 DEPT :  VAL-DE-MARNE
Departement commancant par 9 ==> CP :  95500 DEPT :  VAL-D'OISE
Departement commancant par 9 ==> CP :  97105 DEPT :  GUADELOUPE
Departement commancant par 9 ==> CP :  97209 DEPT :  MARTINIQUE
Departement commancant par 9 ==> CP :  97302 DEPT :  GUYANE
Departement commancant par 9 ==> CP :  97411 DEPT :  LA
Departement commancant par 9 ==> CP :  97608 DEPT :  MAYOTTE
Nombre total de lignes :  102
Nombre de departements commencant par 0 :  9
Nombre de departements commencant par 1 :  10
Nombre de departements commencant par 2 :  11
Nombre de departements commencant par 3 :  10
Nombre de departements commencant par 4 :  10
Nombre de departements commencant par 5 :  10
Nombre de departements commencant par 6 :  10
Nombre de departements commencant par 7 :  10
Nombre de departements commencant par 8 :  10
Nombre de departements commencant par 9 :  11
$

Etiquettes: 

Structures de controle

Awk propose des structures de controle que l'on retrouve dans la plupart des langages de programmation. La syntaxe est héritée du langage C.

1 - if

La partie else est facultative.

Syntaxe

if (condition) {
     instruction
     ...
}
else {
     instruction
     ...
}

2 - for

Première syntaxe

for (initialisation ; condition ; incrementation) {
     instruction
     ...
}

Deuxième syntaxe

for (cle in tableau) {
     print cle , tableau[cle]
}

3 - while

Syntaxe

while (condition) {
     instruction
     ...
}

4 - do-while

Syntaxe

do {
     instruction
     ...
} while (condition)

5 - break

Le mot clé break permet d'interrompre une boucle.

Principe

while (1) {
     if (condition) break ;
     instruction ;
}

6 - continue

Le mot clé continue permet de remonter immédiatement à la condition, sans exécuter la suite de la boucle.

Principe

while (1) {
     if (condition) continue ;
     instruction ;
}

Etiquettes: 

Terminer un script

L'instruction exit permet de terminer un script à tout moment en retournant un statut au système.

Exemple

{
     if ( NF < 3 ) exit 1 ;
     print $1, $2, $3
}
 
END {
     exit 0
}

Etiquettes: 

Tableaux

Dans le langage awk, il existe 2 types de tableaux

Etiquettes: 

Tableaux indicés par un entier

L'indice de départ est au choix.

Exemple

Traitement du fichier /etc/passwd.
Ligne 2, initialisation de la variable FS.
Ligne 5, le contenu de la variable $1 correspondant au user est stocké dans le tableau user[] indicé à partir de 1.
Ligne 8 à 10, on parcourt le tableau et on affiche le contenu en formatant l'affichage avec printf.

$ nl script5.awk
     1  BEGIN {
     2          FS=":"
     3  }
     4  {
     5          user[NR]=$1
     6  }
     7  END {
     8          for (indice = 1 ; indice <= NR ; indice++ ) {
     9                  printf ("User num %2d : %-20s\n" , indice , user[indice]) ;
    10          }
    11  }

Exécution

$ awk -f script5.awk /etc/passwd
User num  1 : root
User num  2 : daemon
User num  3 : bin
User num  4 : sys
User num  5 : sync
User num  6 : games
User num  7 : man
User num  8 : lp
User num  9 : mail
User num 10 : news
User num 11 : uucp
User num 12 : proxy
User num 13 : www-data
User num 14 : backup
User num 15 : list
User num 16 : irc
User num 17 : gnats
User num 18 : nobody
User num 19 : libuuid
User num 20 : postfix
User num 21 : sshd
User num 22 : mysql
$

Etiquettes: 

Tableaux associatifs

Initialiser un tableau

Les tableaux associatifs ont leurs éléments indicés par une chaine de caractères. Cet indice alphanumérique est considéré comme la clé et l'élément correspondant est nommé valeur.

Exemple

Le fichier depts2012.txt liste tous les départements par numéro de région.

$ cat depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
82      07      07186   5       ARDECHE Ardèche
21      08      08105   4       ARDENNES        Ardennes
...
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Le script suivant doit compter le nombre de départements qu'il y a par numéro de région.
La section BEGIN est vide car il n'y a pas besoin d'initialiser des variables.
Ligne 3, une condition est renseignée afin de ne pas traiter la première ligne du fichier qui correspond aux en-têtes.
Ligne 4, mise à jour du tableau nbdepts[] ayant comme clé la valeur du premier champ correspondant au numéro de la région et incrémenté de 1 à chaque fois que le champ 1 de la ligne traitée correspond à la clé.
Ligne 7 à 9, on parcourt le tableau et on affiche les résultats.

$ nl script6.awk
     1  BEGIN {
     2  }
     3  NR != 1{
     4          nbdepts[$1]+=1
     5  }
     6  END {
     7          for ( region in nbdepts) {
     8                  printf("Region num : %02d ==> %3d departement(s)\n" , region , nbdepts[region])
     9          }
    10  }
$

Exécution

$ awk -f script6.awk depts2012.txt
Region num : 01 ==>   1 departement(s)
Region num : 02 ==>   1 departement(s)
Region num : 03 ==>   1 departement(s)
Region num : 04 ==>   1 departement(s)
Region num : 11 ==>   8 departement(s)
Region num : 06 ==>   1 departement(s)
Region num : 21 ==>   4 departement(s)
Region num : 22 ==>   3 departement(s)
Region num : 23 ==>   2 departement(s)
Region num : 31 ==>   2 departement(s)
Region num : 24 ==>   6 departement(s)
Region num : 25 ==>   3 departement(s)
Region num : 26 ==>   4 departement(s)
Region num : 41 ==>   4 departement(s)
Region num : 42 ==>   2 departement(s)
Region num : 43 ==>   4 departement(s)
Region num : 52 ==>   5 departement(s)
Region num : 53 ==>   4 departement(s)
Region num : 54 ==>   4 departement(s)
Region num : 72 ==>   5 departement(s)
Region num : 73 ==>   8 departement(s)
Region num : 74 ==>   3 departement(s)
Region num : 82 ==>   8 departement(s)
Region num : 83 ==>   4 departement(s)
Region num : 91 ==>   5 departement(s)
Region num : 93 ==>   6 departement(s)
Region num : 94 ==>   2 departement(s)
$

Tester l'existence d'un élément

Le mot clé in permet de tester l'existence d'une clé dans un tableau associatif. Cette expression retourne vrai si la clé est présente, faux dans le cas contraire.

cle in tableau

Cette expression peut donc être utilisée comme condition d'une structure de contrôle.

if ( cle in tableau ) {
     ...
     ...
}

Supprimer un élément

Il est possible de supprimer un élément d'un tableau associatif en utilisant la syntaxe suivante.

delete tableau[cle]

La paire clé-valeur est supprimée.

Etiquettes: 

Les arguments de la ligne de commande

awk fourni un mécanisme qui permet de passer des arguments à un script au moment de son appel. Les variables ARGC et ARGV sont initialisées par awk et permettent de traiter les valeurs passées sur la ligne de commandes. La syntaxe doit être obligatroirement du genre var=value et placé avant le ou les fichiers à traiter.

Exemple

Avec le fichier depts2012.txt. Ecrire un script awk permettant de rechercher dans le fichier des enregistrements bien précis en lui passant comme argument un code région (champ 1), un code département (champ 2), un code postal (champ3) ou un département (champ5).

$ cat depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
82      07      07186   5       ARDECHE Ardèche
21      08      08105   4       ARDENNES        Ardennes
...
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA_REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Le script suivant traitera au maximum 4 arguments. La variable $1 devra être strictement égale à la valeur de l'argument coderegion, la variable $2 devra être strictement égale à la valeur de l'argument codedept, la variable $3 devra être strictement égale à la valeur de l'argument codepostal et enfin la variable $5 devra être strictement égale à la valeur de l'argument dept. Tous ces arguments sont bien sûr facultatifs. Les instructions next permettent de ne pas afficher plusieurs fois une même ligne correspondante à plusieurs sections intermédiaires.

$ nl script9.awk
     1  BEGIN {
     2          print "ARGC = " , ARGC
     3          for (i=0 ; i<ARGC ; i++) {
     4                  printf ("ARGV[%d] = %s\n", i , ARGV[i])
     5          }
     6  }
     7  $1 == coderegion {
     8          print $0
     9          next
    10  }
    11  $2 == codedept {
    12          print $0
    13          next
    14  }
    15  $3 == codepostal {
    16          print $0
    17          next
    18  }
    19  $5 == dept {
    20          print $0
    21          next
    22  }
$

Exécution du script ayant comme argument de recherche coderegion=93

$ awk -f script9.awk coderegion=93 depts2012.txt
ARGC =  3
ARGV[0] = awk
ARGV[1] = coderegion=93
ARGV[2] = depts2012.txt
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
93      13      13055   4       BOUCHES-DU-RHONE        Bouches-du-Rhône
93      83      83137   2       VAR     Var
93      84      84007   0       VAUCLUSE        Vaucluse
$

Exécution du script ayant comme argument de recherche codedept=44

$ awk -f script9.awk codedept=44 depts2012.txt
ARGC =  3
ARGV[0] = awk
ARGV[1] = codedept=44
ARGV[2] = depts2012.txt
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
$

Exécution du script ayant comme argument de recherche codepostal=85191

$ awk -f script9.awk codepostal=85191 depts2012.txt
ARGC =  3
ARGV[0] = awk
ARGV[1] = codepostal=85191
ARGV[2] = depts2012.txt
52      85      85191   3       VENDEE  Vendée
$

Exécution du script ayant comme argument de recherche dept=VAR

$ awk -f script9.awk dept=VAR depts2012.txt
ARGC =  3
ARGV[0] = awk
ARGV[1] = dept=VAR
ARGV[2] = depts2012.txt
93      83      83137   2       VAR     Var
$

Exécution du script avec tous les arguments renseignés

$ awk -f script9.awk coderegion=93 codedept=44 codepostal=75056 dept=HAUTE-SAVOIE depts2012.txt
ARGC =  6
ARGV[0] = awk
ARGV[1] = coderegion=93
ARGV[2] = codedept=44
ARGV[3] = codepostal=75056
ARGV[4] = dept=HAUTE-SAVOIE
ARGV[5] = depts2012.txt
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
93      13      13055   4       BOUCHES-DU-RHONE        Bouches-du-Rhône
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
82      74      74010   3       HAUTE-SAVOIE    Haute-Savoie
11      75      75056   0       PARIS   Paris
93      83      83137   2       VAR     Var
93      84      84007   0       VAUCLUSE        Vaucluse
$

Etiquettes: 

Fonctions intégrées

Etiquettes: 

Les fonctions sur les chaines de caractères

Les chaines de caractères

En plus de fonctions de base, awk dispose également de fonctions dédiées aux traitements des chaines de caractères, facilitant ce genre d'opérations. La liste de ces fonctions est la suivante :

Fonction de string Description
gsub(exp,sub,str) Substitue globalement par la chaine sub chaque expression régulière exp trouvée dans la chaine str et retourne le nombre de substitutions. Si str n'est pas indiquée, par défaut $0 est utilisé.
index(str,st)

Retourne la position du string st dans la chaine str, ou 0 si non trouvé.

length(str) Retourne la longueur de la chaine str. Si str n'est pas indiquée, par défaut $0 est utilisé.
match(str,exp) Retourne la position de l'expression régulière exp dans la chaine str, ou 0 si non trouvé. Affecte les valeurs aux variables RSTART et RLENGTH.
split(str,tab,sep) Sépare la chaine str en éléments dans un tableau tab et en utilisant le séparateur sep. Si sep n'est pas renseigné, FS est utilisé par défaut.
sprintf("format",exp) Retourne une chaine au lieu de l'affichage vers la sortie standard, contrairement à printf().
sub(exp,sub,str) Comme gsub(), mais ne substitue par sub que la première expression exp trouvée dans str.
substr(str,pos,long) Retourne une partie du string str commançant à la position pos et de longueur long. Si long n'est pas indiqué, substr() utilise tout le reste de str.
tolower(str) Met en minuscules toute la chaine str et retourne la nouvelle chaine.
toupper(str) Met en majuscules toute la chaine str et retourne la nouvelle chaine.

Exemple :

gsub : remplacer par un @ toutes les lettres a et A du fichier depts2012.txt

$ head depts2012.txt | awk '{gsub(/a|A/,"@") ; print}'
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       @IN     @in
22      02      02408   5       @ISNE   @isne
83      03      03190   5       @LLIER  @llier
93      04      04070   4       @LPES-DE-H@UTE-PROVENCE @lpes-de-H@ute-Provence
93      05      05061   4       H@UTES-@LPES    H@utes-@lpes
93      06      06088   4       @LPES-M@RITIMES @lpes-M@ritimes
82      07      07186   5       @RDECHE @rdèche
21      08      08105   4       @RDENNES        @rdennes
73      09      09122   5       @RIEGE  @riège
$

index : connaitre la position d'un caractère dans une chaine

$ head depts2012.txt | awk '{pos=index($5,"-") ; print "Position du tiret : " , pos , "\tdans la chaine : " , $5}'
Position du tiret :  0  dans la chaine :  NCC
Position du tiret :  0  dans la chaine :  AIN
Position du tiret :  0  dans la chaine :  AISNE
Position du tiret :  0  dans la chaine :  ALLIER
Position du tiret :  6  dans la chaine :  ALPES-DE-HAUTE-PROVENCE
Position du tiret :  7  dans la chaine :  HAUTES-ALPES
Position du tiret :  6  dans la chaine :  ALPES-MARITIMES
Position du tiret :  0  dans la chaine :  ARDECHE
Position du tiret :  0  dans la chaine :  ARDENNES
Position du tiret :  0  dans la chaine :  ARIEGE
$

length : connaitre le nombre de caractères dans une chaine

$ tail depts2012.txt | awk '{lg=length($5) ; printf ("Il y a %3d caracteres dans la chaine %30s\n" , lg , $5)}'
Il y a   7 caracteres dans la chaine                        ESSONNE
Il y a  14 caracteres dans la chaine                 HAUTS-DE-SEINE
Il y a  17 caracteres dans la chaine              SEINE-SAINT-DENIS
Il y a  12 caracteres dans la chaine                   VAL-DE-MARNE
Il y a  10 caracteres dans la chaine                     VAL-D'OISE
Il y a  10 caracteres dans la chaine                     GUADELOUPE
Il y a  10 caracteres dans la chaine                     MARTINIQUE
Il y a   6 caracteres dans la chaine                         GUYANE
Il y a  10 caracteres dans la chaine                     LA_REUNION
Il y a   7 caracteres dans la chaine                        MAYOTTE
$

match : connaitre la position d'une expression dans une chaine

$ tail depts2012.txt | awk '{pos=match($5,/-DE-/) ; printf("Position de l expression recherchee \"-DE-\" : %2d dans la chaine %20s\tRSTART = %2d\tRLENGTH = %2d\n" , pos , $5 , RSTART , RLENGTH)}'
Position de l expression recherchee "-DE-" :  0 dans la chaine              ESSONNE     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  6 dans la chaine       HAUTS-DE-SEINE     RSTART =  6     RLENGTH =  4
Position de l expression recherchee "-DE-" :  0 dans la chaine    SEINE-SAINT-DENIS     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  4 dans la chaine         VAL-DE-MARNE     RSTART =  4     RLENGTH =  4
Position de l expression recherchee "-DE-" :  0 dans la chaine           VAL-D'OISE     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  0 dans la chaine           GUADELOUPE     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  0 dans la chaine           MARTINIQUE     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  0 dans la chaine               GUYANE     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  0 dans la chaine           LA_REUNION     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  0 dans la chaine              MAYOTTE     RSTART =  0     RLENGTH = -1
$

split : séparer une chaine en éléments dans un tableau

$ tail depts2012.txt | awk '/-/{split($5,tab,"-") ; printf("tab1 = %10s\ttab2 = %10s\ttab3 = %10s\n" , tab[1] , tab[2] , tab[3])}'
tab1 =      HAUTS       tab2 =         DE       tab3 =      SEINE
tab1 =      SEINE       tab2 =      SAINT       tab3 =      DENIS
tab1 =        VAL       tab2 =         DE       tab3 =      MARNE
tab1 =        VAL       tab2 =     D'OISE       tab3 =
$

sprintf (contrairement à la commande printf, avec sprintf le retour chariot \n n'est pas obligatoire)

$ tail depts2012.txt | awk '/-/{split($5,tab,"-") ; chaine=sprintf("tab1 = %10s\ttab2 = %10s\ttab3 = %10s" , tab[1] , tab[2] , tab[3]) ; print chaine}'
tab1 =      HAUTS       tab2 =         DE       tab3 =      SEINE
tab1 =      SEINE       tab2 =      SAINT       tab3 =      DENIS
tab1 =        VAL       tab2 =         DE       tab3 =      MARNE
tab1 =        VAL       tab2 =     D'OISE       tab3 =
$

sub : remplacer par un @ la première lettre a ou A de chaque ligne du fichier depts2012.txt

$ head depts2012.txt | awk '{sub(/a|A/,"@") ; print}'
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       @IN     Ain
22      02      02408   5       @ISNE   Aisne
83      03      03190   5       @LLIER  Allier
93      04      04070   4       @LPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       H@UTES-ALPES    Hautes-Alpes
93      06      06088   4       @LPES-MARITIMES Alpes-Maritimes
82      07      07186   5       @RDECHE Ardèche
21      08      08105   4       @RDENNES        Ardennes
73      09      09122   5       @RIEGE  Ariège
$

substr : extraire une partie d'une chaine

$ tail depts2012.txt | awk '{chaine=substr($5,1,3) ; printf("Les 3 premieres lettres de %20s sont : %3s\n" , $5 , chaine)}'
Les 3 premieres lettres de              ESSONNE sont : ESS
Les 3 premieres lettres de       HAUTS-DE-SEINE sont : HAU
Les 3 premieres lettres de    SEINE-SAINT-DENIS sont : SEI
Les 3 premieres lettres de         VAL-DE-MARNE sont : VAL
Les 3 premieres lettres de           VAL-D'OISE sont : VAL
Les 3 premieres lettres de           GUADELOUPE sont : GUA
Les 3 premieres lettres de           MARTINIQUE sont : MAR
Les 3 premieres lettres de               GUYANE sont : GUY
Les 3 premieres lettres de           LA_REUNION sont : LA_
Les 3 premieres lettres de              MAYOTTE sont : MAY
$

tolower : convertir une chaine majuscule en minuscule

$ tail depts2012.txt | awk '{chaine=tolower($5) ; printf("%20s en minuscule : %20s\n" , $5 , chaine)}'
             ESSONNE en minuscule :              essonne
      HAUTS-DE-SEINE en minuscule :       hauts-de-seine
   SEINE-SAINT-DENIS en minuscule :    seine-saint-denis
        VAL-DE-MARNE en minuscule :         val-de-marne
          VAL-D'OISE en minuscule :           val-d'oise
          GUADELOUPE en minuscule :           guadeloupe
          MARTINIQUE en minuscule :           martinique
              GUYANE en minuscule :               guyane
          LA_REUNION en minuscule :           la_reunion
             MAYOTTE en minuscule :              mayotte
$

toupper : convertir une chaine minuscule en majuscule

$ tail depts2012.txt | awk '{chaine=toupper($6) ; printf("%20s en MAJUSCULE : %20s\n" , $6 , chaine)}'
             Essonne en MAJUSCULE :              ESSONNE
      Hauts-de-Seine en MAJUSCULE :       HAUTS-DE-SEINE
   Seine-Saint-Denis en MAJUSCULE :    SEINE-SAINT-DENIS
        Val-de-Marne en MAJUSCULE :         VAL-DE-MARNE
          Val-d'Oise en MAJUSCULE :           VAL-D'OISE
          Guadeloupe en MAJUSCULE :           GUADELOUPE
          Martinique en MAJUSCULE :           MARTINIQUE
              Guyane en MAJUSCULE :               GUYANE
             Mayotte en MAJUSCULE :              MAYOTTE
$

Etiquettes: 

Les fonctions mathématiques

Les fonctions mathématiques

awk dispose également de fonctions dédiées aux traitements numériques. Celles-ci sont les suivantes :

Fonction mathématique Description
cos(r) Cosinus de l'angle r (r en radians)
exp(x) Exponentiel de x
int(x) Valeur entière de x
log(x) Logarithme de x
sin(r) Sinus de l'angle r (r en radians)
sqrt(x) Racine carrée de x
atan2(y,x) Arc tangente de y/x
rand() Nombre pseudo-aléatoire compris entre 0 et 1
srand(n) Réinitialise la fonction rand()

Exemple :

cos

$ echo 50 | awk '{print cos($1)}'
0.964966
$

exp

$ echo 5 | awk '{print exp($1)}'
148.413
$

int

$ echo 5.4 | awk '{print $1 , " ==> " , int($1)}'
5.4  ==>  5
$

log

$ echo 5 | awk '{print log($1)}'
1.60944
$

sin

$ echo 50 | awk '{print sin($1)}'
-0.262375
$

sqrt

$ echo 81 | awk '{print sqrt($1)}'
9
$

atan2

$ echo 50 25 | awk '{print atan2($1,$2)}'
1.10715
$

rand

$ echo | awk '{print rand()}'
0.795735
$ echo | awk '{print rand()}'
0.321886
$ echo | awk '{print int(rand()*1000)}'
792
$

Etiquettes: 

Autres fonctions

Etiquettes: 

La fonction getline

La fonction getline permet de lire la ligne suivante du flux sans remonter au début du traitement (contrairement à next) et de lire une ligne à partir d'un fichier, de l'entrée standard ou d'un tube.

Valeur de retour :
- 1 en cas de succès
- 0 en fin de fichier
- -1 en cas d'erreur

Il ne faut pas mettre de parenthèse lors de l'appel de la fonction getline.

Syntaxe

getline [variable]
Lecture de la ligne suivante du flux.
getline [variable] < "fichier"
Lecture d'une ligne à partir d'un fichier
"commande" | getline [variable]
Lecture d'une ligne provenant du résultat d'une commande du système.

La ligne lue par getline est stockée dans la variable variable ou $0 si aucun nom de variable n'est spécifié. Le nom de fichier "-" représente l'entrée standard.

Premier exemple :

Reconstituer des phrases écrites sur plusieurs lignes et délimitées par un caractère spécifique.

Le fichier suivant contient des phrases scindées en plusieurs lignes. Le scindement est caractérisé par un anti-slash en fin de ligne.

$ cat text.txt
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. \
Maecenas porttitor congue massa. \
Fusce posuere, magna sed pulvinar ultricies,purus lectus malesuada libero, \
sit amet commodo magna eros quis urna.
Nunc viverra imperdiet enim. \
Fusce est. \
Vivamus a tellus.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. \
Proin pharetra nonummy pede. Mauris et orci.
Aenean nec lorem. In porttitor. Donec laoreet nonummy augue.
Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. \
Mauris eget neque at sem venenatis eleifend. \
Ut nonummy.
$.

Le script suivant va analyser chaque ligne du fichier et reconstituer l'intégralité des phrases grâce à la fonction getline.

$ nl script10.awk
     1  {
     2          ligne=$0
     3          # Tant que la ligne se termine par \
     4          while(ligne ~ /\\$/){
     5                  # Suppression de \
     6                  sub(/\\$/,"",ligne)
     7                  # Lecture de la ligne suivante
     8                  getline nextline
     9                  # Concatenation de ligne et nextline
    10                  ligne=ligne nextline
    11          }
    12          # Affichage de la ligne globale
    13          printf("------- Contenu de la phrase %d -------\n%s\n",++num,ligne)
    14  }
$

Exécution du script

$ awk -f script10.awk text.txt
------- Contenu de la phrase 1 -------
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies,purus lectus malesuada libero, sit amet commodo magna eros quis urna.
------- Contenu de la phrase 2 -------
Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus.
------- Contenu de la phrase 3 -------
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci.
------- Contenu de la phrase 4 -------
Aenean nec lorem. In porttitor. Donec laoreet nonummy augue.
------- Contenu de la phrase 5 -------
Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.
$

Deuxième exemple :

Lire une entrée clavier et le contenu d'un fichier extérieur au flux courant.

Rechercher le nom d'un département dans le fichier depts2012.txt en saisissant son nom au clavier.

$ cat depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
...
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA_REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Le script suivant demande une saisie au clavier et recherche dans le fichier précédent le nom saisi.

$ nl script11.awk
     1  BEGIN{
     2          # Boucle infinie
     3          while(1){
     4                  printf("Rechercher le nom (ctrl+d pour quitter): ")
     5                  # Lecture clavier sur l'entree standard "-"
     6                  # Arret du programme si le code retour de getline est different de 1
     7                  if((getline nom < "-") != 1) break
     8                  # Si aucun nom saisi, on recommence
     9                  if(length(nom)==0) continue
    10                  trouve="false"
    11                  # Boucle de lecture du fichier depts2012.txt
    12                  # Tant que le code retour de getline est egal a 1
    13                  while((getline < "depts2012.txt")==1){
    14                          # Comparaison faite sans tenir compte de la casse
    15                          if(tolower($5)==tolower(nom)){
    16                                  trouve="true"
    17                                  # Affichage du resultat trouve
    18                                  print $0
    19                                  # On sort de la boucle while
    20                                  break
    21                          }
    22                  }
    23                  # Fermeture du fichier
    24                  close("depts2012.txt")
    25                  # Affichage du message si le nom n'a pas ete trouve
    26                  if(trouve=="false") print nom , "n'est pas dans le fichier"
    27          }
    28          # Affichage du message avant l'arret du programme
    29          print "\nA bientot"
    30  }
$

Exécution du script

$ awk -f script11.awk
Rechercher le nom (ctrl+d pour quitter): ain
82      01      01053   5       AIN     Ain
Rechercher le nom (ctrl+d pour quitter): somme
22      80      80021   3       SOMME   Somme
Rechercher le nom (ctrl+d pour quitter):
Rechercher le nom (ctrl+d pour quitter): var
93      83      83137   2       VAR     Var
Rechercher le nom (ctrl+d pour quitter):
A bientot
$

Troisième exemple :

Utiliser getline pour lire le résulat d'une commande système.

$ nl script12.awk
     1  BEGIN{
     2          "date" | getline
     3          print "$0 = ",$0
     4          for(i=1;i<=NF;i++){
     5                  printf("$%d = %s\n",i,$i)
     6          }
     7  }
$ awk -f script12.awk
$0 =  Tue May 15 19:35:25 CEST 2012
$1 = Tue
$2 = May
$3 = 15
$4 = 19:35:25
$5 = CEST
$6 = 2012
$

Etiquettes: 

La fonction close

La fonction close permet de fermer un fichier ou un tube de communication. Si l'appel à cette fonction est omis, les ressources sont libérées à la terminaison du script.

L'ordre de fermeture est intéressant dans les cas suivants :
- pouvoir se repositionner en début de fichier au sein du même processus (fermeture puis réouverture)
- fermer un tube de communication pour s'en servir à nouveau
- libérer les ressources au fur et à mesure des besoins (le système limite les processus, en ce qui concerne le nombre de fichiers/tubes ouverts simultanément).

Syntaxe

close("fichier")
close("commande")

Il est indispensable de fermer un fichier pour pouvoir le relire à partir du début.

Exemple :

$ nl script13.awk
     1  BEGIN {
     2          fichier = "/root/fichier1.txt"
     3          i=1
     4          while(i<=10){
     5                  print rand() > fichier
     6                  i++
     7          }
     8          # Fermeture du fichier
     9          close(fichier)
    10          while((getline<fichier)==1){
    11                  print $0
    12          }
    13  }
$ awk -f script13.awk
0.692303
0.532958
0.423364
0.484409
0.455168
0.0133607
0.554077
0.375875
0.325945
0.164046
$

Etiquettes: 

La fonction system

La fonction system permet d'exécuter une commande du système.

Syntaxe

system("commande")

La fonction retourne le statut renvoyé par la commande.

Exemple :

$ awk 'BEGIN{system("uptime")}'
 21:06:15 up 5 days,  2:22,  0 users,  load average: 0.00, 0.00, 0.00
$

Idem mais en se servant d'une variable

$ awk 'BEGIN{fic="depts2012.txt";system("ls -l " fic)}'
-rw-r--r-- 1 root root 3504 May 10 14:05 depts2012.txt
$

Etiquettes: 

Fonctions utilisateur

Les fonctions personnelles peuvent être définies dans le script awk, en dehors des blocs d'instructions, ou dans un autre script qui sera également appelé par l'option -f <script>.

Une fonction se définit par le mot clé function suivi du nom de la fonction et de ses paramètres. Une fonction peut recevoir de 0 à n arguments et retourner une valeur explicite.

Syntaxe

function nom_fonction (param1, param2, ..., paramn) {
     instructions
     return valeur
}

Tous les paramètres de la fonction sont des variables locales. Toute autre variable définie dans la fonction est globale.

Appel d'une fonction

valeur=nom_fonction(val1, val2, ..., valn)

Il ne doit pas y avoir d'espace entre le nom de la fonction et la parenthèse ouvrante.

Exemple

$ nl script14.awk
     1  function modulo(nb){
     2          mod=nb%2
     3          if(mod==0){
     4                  chaine=sprintf("%d est un nombre pair" , nb)
     5          }
     6          else{
     7                  chaine=sprintf("%d est un nombre impair" , nb)
     8          }
     9          return chaine
    10  }
    11  {
    12          for(i=1;i<=NF;i++){
    13                  print modulo($i)
    14          }
    15  }
$ echo "20 21 23 56 43 2.4 rr" | awk -f script14.awk
20 est un nombre pair
21 est un nombre impair
23 est un nombre impair
56 est un nombre pair
43 est un nombre impair
2 est un nombre impair
0 est un nombre pair
$

Etiquettes: 

Exemples de scripts

Etiquettes: 

Analyse des logs postfix

Le script suivant analyse les logs postfix et retourne le nombre de messages par code erreur SMTP.
3 niveaux de visualisation sont proposés dont le jour et le mois en cours.

Pour fonctionner, le script à besoin d'un fichier additionnel répertoriant la liste des codes erreurs et leurs descriptions.

Téléchargement des fichiers :

  1. codeErrorSmtp
  2. mail.awk

Détail du fichier codeErrorSmtp :

$ cat codeErrorSmtp
X.1.0|Other address status
X.1.1|Bad destination mailbox address
X.1.2|Bad destination system address
X.1.3|Bad destination mailbox address syntax
X.1.4|Destination mailbox address ambiguous
X.1.5|Destination mailbox address valid
X.1.6|Mailbox has moved
X.1.7|Bad sender's mailbox address syntax
X.1.8|Bad sender's system address
X.2.0|Other or undefined mailbox status
X.2.1|Mailbox disabled, not accepting messages
X.2.2|Mailbox full
X.2.3|Message length exceeds administrative limit.
X.2.4|Mailing list expansion problem
X.3.0|Other or undefined mail system status
X.3.1|Mail system full
X.3.2|System not accepting network messages
X.3.3|System not capable of selected features
X.3.4|Message too big for system
X.4.0|Other or undefined network or routing status
X.4.1|No answer from host
X.4.2|Bad connection
X.4.3|Routing server failure
X.4.4|Unable to route
X.4.5|Network congestion
X.4.6|Routing loop detected
X.4.7|Delivery time expired
X.5.0|Other or undefined protocol status
X.5.1|Invalid command
X.5.2|Syntax error
X.5.3|Too many recipients
X.5.4|Invalid command arguments
X.5.5|Wrong protocol version
X.6.0|Other or undefined media error
X.6.1|Media not supported
X.6.2|Conversion required and prohibited
X.6.3|Conversion required but not supported
X.6.4|Conversion with loss performed
X.6.5|Conversion failed
X.7.0|Other or undefined security status
X.7.1|Delivery not authorized, message refused
X.7.2|Mailing list expansion prohibited
X.7.3|Security conversion required but not possible
X.7.4|Security features not supported
X.7.5|Cryptographic failure
X.7.6|Cryptographic algorithm not supported
X.7.7|Message integrity failure
$

Détail du script commenté :

$ nl mail.awk
     1  BEGIN{
     2          # Si le nombre d'arguments est egal a 1
     3          # Arret du programme
     4          # Il faut au moins 2 arguments (1 nom de fichier de log)
     5          # Le script peut accepter plusieurs fichiers de log
     6          if(ARGC==1){
     7                  print "Nombre d'arguments incorrect"
     8                  print "Syntaxe : awk -f mail.awk fichierLogMail1 [fichierLogMailn...]"
     9                  exit 1
    10          }
    11          # Initialisation de la variable contenant le nom du fichier
    12          # des codes erreurs SMTP
    13          errorSmtp="codeErrorSmtp"
    14          # Execution d'une commande systeme pour tester si le fichier existe
    15          # Arret du programme si le fichier n'existe pas
    16          if((system("test -f " errorSmtp))==1){
    17                  print "Fichier codeErrorSmtp manquant"
    18                  exit 1
    19          }
    20          # Sauvegarde des donnees du fichier codeErrorSmtp
    21          # dans un tableau associatif avec le code en cle
    22          while((getline < errorSmtp) == 1){
    23                  ligne=$0
    24                  split(ligne,tab,"|")
    25                  tabErr[tab[1]]=tab[2]
    26          }
    27          # Fermeture du fichier codeErrorSmtp
    28          close(errorSmtp)
    29          # Affichage du menu
    30          while(1){
    31                  # Si le choix est passe en argument
    32                  if(ARGV[1] ~ /^choix=/){
    33                          split(ARGV[1],tab,"=")
    34                          choix=tab[2]
    35                          if(choix>=1 && choix<=3) break
    36                  }
    37                  print "Choix date"
    38                  print " 1 - all"
    39                  print " 2 - day"
    40                  print " 3 - month"
    41                  printf("Choix : ")
    42                  # Lecture de l'entree clavier
    43                  # Si erreur, on quitte la boucle while
    44                  if((getline choix < "-")!=1) break
    45                  # Si aucune saisie, reaffichage du menu
    46                  if(length(choix)==0) continue
    47                  # Si le choix est compris entre 1 et 3 on quitte la boucle while
    48                  if(choix>=1 && choix<=3) break
    49          }
    50          deb=systime()
    51          # Si le choix est egal a rien on quitte le programme
    52          if(choix==""){
    53                  print "Bye"
    54                  exit 2
    55          }
    56          print "-------------------------------------------"
    57          print "------- Analyse des fichiers de log -------"
    58          print "-------------------------------------------"
    59  }
    60  # Lecture des fichiers de log
    61  # Si la ligne courante contient l'expression recherchee
    62  $0 ~ /[0-9][0-9][0-9] [0-9]\.[0-9]\.[0-9]/ && (choix==1 || (choix==2 && $1==getMonth() && $2==getDay()) || (choix==3 && $1==getMonth())) {
    63          # Recherche de la position du premier caractere de l'expression xxx x.x.x
    64          pos=match($0,/[0-9][0-9][0-9] [0-9]\.[0-9]\.[0-9]/)
    65          # Extraction de l'expression xxx x.x.x
    66          code=substr($0,pos,9)
    67          # Sauvegarde de l'expression comme cle du tableau associatif
    68          # et incrementation du compteur
    69          codeSmtp[code]+=1
    70  }
    71  END{
    72          # Parcourt du tableau associatif codeSmtp
    73          totalMessages=0
    74          for(i in codeSmtp){
    75                  # Affichage des lignes du tableau
    76                  # et de la description du code avec la fonction descCode
    77                  printf("Code %s (%-45s) ==> %5d message(s)\n",i,descCode(i),codeSmtp[i])
    78                  totalMessages+=codeSmtp[i]
    79          }
    80          # Fin du programme
    81          fin=systime()
    82          duree=fin-deb
    83          printf("Total : %d messages\n%d lignes traitees en %d sec\n",totalMessages,NR,duree)
    84          exit 0
    85  }
    86  # Fonction permettant la recherche de la description du code
    87  function descCode(code){
    88          # Extraction des 4 derniers caracteres du code à partir du 6eme caractere
    89          codex=substr(code,6,4)
    90          # Concatenation de la lettre "X" aux 4 derniers caracteres du code
    91          codex="X" codex
    92          # Extraction de la description en fonction du code
    93          desc=tabErr[codex]
    94          # Si le code erreur n'existe pas alors OK
    95          desc=(desc != "" ? desc : "OK")
    96          # Retour de la description au programme appelant
    97          return desc
    98  }
    99  # Fonction permettant d'obtenir le numero du jour en cours
   100  function getDay(){
   101          day=int(strftime("%d"))
   102          return day
   103  }
   104  # Fonction permettant d'obtenir le nom du mois en cours
   105  function getMonth(){
   106          month=strftime("%b")
   107          return month
   108  }
$

Exécution du script :

$ awk -f mail.awk /var/log/mail.log
Choix date
 1 - all
 2 - day
 3 - month
Choix : 3
-------------------------------------------
------- Analyse des fichiers de log -------
-------------------------------------------
Code 554 5.7.1 (Delivery not authorized, message refused     ) ==>  1992 message(s)
Code 550 5.1.1 (Bad destination mailbox address              ) ==>    23 message(s)
Code 250 2.0.0 (OK                                           ) ==>  1620 message(s)
Total : 3635 messages
42719 lignes traitees en 0 sec
$

En passant le choix de la date en argument :

$ awk -f mail.awk choix=3 /var/log/mail.log
-------------------------------------------
------- Analyse des fichiers de log -------
-------------------------------------------
Code 554 5.7.1 (Delivery not authorized, message refused     ) ==>  1992 message(s)
Code 550 5.1.1 (Bad destination mailbox address              ) ==>    23 message(s)
Code 250 2.0.0 (OK                                           ) ==>  1620 message(s)
Total : 3635 messages
42719 lignes traitees en 0 sec
$

Etiquettes: 

Afficher les infos du processeur et de la mémoire dans une page web

Exemple extrait du Linux/Magazine N° 131 d'octobre 2010 page 52.

Le script infoproc.awk utilise les fichiers virtuels /proc/cpuinfo et /proc/meminfo pour extraire les informations à afficher dans une page HTML infoproc.html. Les fichiers sont à placer en derniers paramètres et peuvent être traités différemment par les variables FILENAME et FNR. La sortie du script est redirigée vers le fichier infoproc.html.

L'en-tête et la fin de page HTML sont codés dans le bloc BEGIN et END.

Le fichier virtuel /proc/cpuinfo contient des enregistrements séparés par une ligne vide. Chaque champs des enregistrements est défini sur une ligne. Pour ce fichier, la variable de séparation d'enregistrement RS est donc égale à un saut de ligne "\n\n" et la variable de séparation des champs FS est égale à un retour-chariot "\n". Le fichier /proc/meminfo contient un seul enregistrement et les champs sont séparés par un retour-chariot, aussi les variables RS et FS n'ont pas à être modifiées entre ces fichiers.

Télécharger le script

infoproc.awk

Détail du script infoproc.awk

$ nl infoproc.awk
     1  BEGIN{
     2          RS="\n\n"       # Separateur d'enregistrement
     3          FS="\n"         # Separateur de ligne
     4          # En-tete et debut de page HTML
     5          print "<html>"
     6          print "<head>"
     7          print "<title>Informations CPU et m&eacute;moire</title>"
     8          print "<style type=\"text/css\">"
     9          print "table {border: solid thin; padding 10px; margin 5px}"
    10          print "table.proc {color: DarkSlateBlue; border-color: DarkSlateBlue}"
    11          print "table.proc caption {color: white; background: DarkSlateBlue; text-align: center}"
    12          print "table.mem {color: DarkGreen; border-color: DarkGreen}"
    13          print "table.mem caption {color: white; background: DarkGreen; text-align: center}"
    14          print "</style>"
    15          print "</head>"
    16          print "<body>"
    17          print "<table><tr>"
    18  }
    19  FILENAME ~ /cpuinfo$/ { print "<td valign=\"top\"><table class=\"proc\">"}
    20  FILENAME ~ /meminfo$/ { print "<td valign=\"top\"><table class=\"mem\">"}
    21  {
    22          for(i=1; i<=NF; i++){
    23                  split($i, cpu, ":")
    24                  if(i==1) print "<caption>", cpu[1], cpu[2], "</caption>"
    25                  else print "<tr><td>", cpu[1], "</td><td>", cpu[2], "</td></tr>"
    26          }
    27          print "</table></td>"
    28  }
    29  END{
    30          # Fin de page HTML
    31          print "</tr></table>"
    32          print "</body>"
    33          print "</html>"
    34  }
$

Exécution du script

$ awk -f /root/infoproc.awk /proc/cpuinfo /proc/meminfo > /var/www/infoproc.html

Résultat obtenu

Fichier attachéTaille
HTML icon infoproc.html5.96 Ko
Etiquettes: 

Les commandes filtres

Les commandes filtres traitent un flux de données issues de l'entrée standard ou contenues dans un fichier. Elles peuvent être utilisées de manière autonome ou placées derrière un tube de communication. Les filtres s'utilisent pour la plupart de la même manière.

Syntaxe

commande_filtre -options fic1 fic2 ... ficn
commande_filtre -options -
commande_filtre -options < fichier
commande | commande_filtre -options

Visualisation de données

Consultation de données, création de fichier : cat

Outre la consultation de données, voici quelques exemples d'utilisation de la commande cat.

Syntaxe

cat [options] [fichiers]
Principales options
-e     Affiche le symbole "$" à la fin de chaque ligne
-t     Affiche le symbole "^I" (i majuscule) à chaque tabulation
-v     Matérialise les caractères non affichables

Exemples

Quand la commande cat ne reçoit pas de fichier en argument, elle affiche sur la sortie standard les données saisies sur l'entrée standard.

$ cat
ceci est un test     # Texte saisi au clavier sur l'entrée standard
ceci est un test     # Texte afficher automatiquement sur la sortie standard
^d
$

Ce qui est saisi sur l'entrée standard peut être enregistré dans un fichier

$ cat > fic1
ceci est un test
^d
$ cat fic1
ceci est un test
$

cat peut également concaténer des fichiers

$ cat fic1
ceci est un test
$ cat fic2
ceci est un autre test
$ cat fic1 fic2 > fic3
$ cat fic3
ceci est un test
ceci est un autre test
$

L'option -t permet de visualiser les tabulations. Celles-ci sont matérialisées par les caractères "^I" (i majuscule)

$ cat fic4
ceci    est     un      test
$ cat -t fic4
ceci^Iest^Iun^Itest
$

L'option -e permet de visualiser les fins de ligne. Celles-ci sont matérialisées par le symbole "$"

$ cat -e fic4
ceci    est     un      test$
$

L'option -A permet de visualiser tous les caractères non affichables

$ cat -A fic4
ceci^Iest^Iun^Itest$
$

Etiquettes: 

Valeur des octets d'un flux de données : od

La commande od permet de visualiser la valeur de chaque octet d'un fichier texte ou binaire. L'option -c permet une interprétation octet par octet. Il est ainsi possible de voir de manière exacte le contenu du fichier, de détecter l'emplacement des espaces, tabulations et caractères de sauts de ligne (CR, LF ou CR LF).

Avec od, les tabulations sont représentées par \t, les espaces par un espace, le caractère LF par \n

Exemple

$ cat fic5
ceci est un     test
et un   autre test
$ od -c fic5
0000000   c   e   c   i       e   s   t       u   n  \t   t   e   s   t
0000020  \n   e   t       u   n      \t   a   u   t   r   e       t   e
0000040   s   t  \t  \n
0000044
$

La colonne de gauche représente la position du premier caractère de la ligne dans le fichier, exprimée en octal.

Etiquettes: 

Filtrage de lignes : grep

La commande grep recherche une chaine de caractères dans un ou plusieurs fichiers textes et affiche à l'écran les lignes contenant cette chaine. La chaine recherchée est matérialisée par une expression régulière basique ERb (défaut) ou étendue ERe grâce à l'option -E.

Syntaxe

grep [options] expreg [fichiers...]
grep [options] -e expreg1 -e expreg2 [fichiers...]
grep [options] -f fichier_expreg [fichiers...]
Principales options :
-c affiche le nombre de lignes trouvées
-e permet de spécifier plusieurs expressions régulières
-E permet d'utiliser les ERe
-f permet de lire les expressions régulières à partir d'un fichier
-F permet de ne pas interpréter le motif de recherche comme une expression régulière
-i permet une recherche insensible à la casse
-l permet de seulement afficher les noms des fichiers contenant l'expression régulière recherchée
-n permet de numéroter les lignes trouvées
-q permet d'effectuer une recherche sans afficher le résultat à l'écran
-v permet une recherche ne contenant pas l'expression régulière
-w l'expression recherchée doit correspondre à un mot entier
-x l'expression recherchée doit correspondre à une ligne entière

Exemple

Utilisation du fichier depts2012.txt pour les exemples

$ cat depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
82      07      07186   5       ARDECHE Ardèche
21      08      08105   4       ARDENNES        Ardennes
73      09      09122   5       ARIEGE  Ariège
21      10      10387   5       AUBE    Aube
91      11      11069   5       AUDE    Aude
73      12      12202   5       AVEYRON Aveyron
93      13      13055   4       BOUCHES-DU-RHONE        Bouches-du-Rhône
25      14      14118   2       CALVADOS        Calvados
83      15      15014   2       CANTAL  Cantal
54      16      16015   3       CHARENTE        Charente
54      17      17300   3       CHARENTE-MARITIME       Charente-Maritime
24      18      18033   2       CHER    Cher
74      19      19272   3       CORREZE Corrèze
94      2A      2A004   3       CORSE-DU-SUD    Corse-du-Sud
94      2B      2B033   3       HAUTE-CORSE     Haute-Corse
26      21      21231   3       COTE-D'OR       Côte-d'Or
53      22      22278   4       COTES-D'ARMOR   Côtes-d'Armor
74      23      23096   3       CREUSE  Creuse
72      24      24322   3       DORDOGNE        Dordogne
43      25      25056   2       DOUBS   Doubs
82      26      26362   3       DROME   Drôme
23      27      27229   5       EURE    Eure
24      28      28085   1       EURE-ET-LOIR    Eure-et-Loir
53      29      29232   2       FINISTERE       Finistère
91      30      30189   2       GARD    Gard
73      31      31555   3       HAUTE-GARONNE   Haute-Garonne
73      32      32013   2       GERS    Gers
72      33      33063   3       GIRONDE Gironde
91      34      34172   5       HERAULT Hérault
53      35      35238   1       ILLE-ET-VILAINE Ille-et-Vilaine
24      36      36044   5       INDRE   Indre
24      37      37261   1       INDRE-ET-LOIRE  Indre-et-Loire
82      38      38185   5       ISERE   Isère
43      39      39300   2       JURA    Jura
72      40      40192   4       LANDES  Landes
24      41      41018   0       LOIR-ET-CHER    Loir-et-Cher
82      42      42218   3       LOIRE   Loire
83      43      43157   3       HAUTE-LOIRE     Haute-Loire
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
24      45      45234   2       LOIRET  Loiret
73      46      46042   2       LOT     Lot
72      47      47001   0       LOT-ET-GARONNE  Lot-et-Garonne
91      48      48095   3       LOZERE  Lozère
52      49      49007   0       MAINE-ET-LOIRE  Maine-et-Loire
25      50      50502   3       MANCHE  Manche
21      51      51108   3       MARNE   Marne
21      52      52121   3       HAUTE-MARNE     Haute-Marne
52      53      53130   3       MAYENNE Mayenne
41      54      54395   0       MEURTHE-ET-MOSELLE      Meurthe-et-Moselle
41      55      55029   3       MEUSE   Meuse
53      56      56260   2       MORBIHAN        Morbihan
41      57      57463   3       MOSELLE Moselle
26      58      58194   3       NIEVRE  Nièvre
31      59      59350   2       NORD    Nord
22      60      60057   5       OISE    Oise
25      61      61001   5       ORNE    Orne
31      62      62041   2       PAS-DE-CALAIS   Pas-de-Calais
83      63      63113   2       PUY-DE-DOME     Puy-de-Dôme
72      64      64445   4       PYRENEES-ATLANTIQUES    Pyrénées-Atlantiques
73      65      65440   4       HAUTES-PYRENEES Hautes-Pyrénées
91      66      66136   4       PYRENEES-ORIENTALES     Pyrénées-Orientales
42      67      67482   2       BAS-RHIN        Bas-Rhin
42      68      68066   2       HAUT-RHIN       Haut-Rhin
82      69      69123   2       RHONE   Rhône
43      70      70550   3       HAUTE-SAONE     Haute-Saône
26      71      71270   0       SAONE-ET-LOIRE  Saône-et-Loire
52      72      72181   3       SARTHE  Sarthe
82      73      73065   3       SAVOIE  Savoie
82      74      74010   3       HAUTE-SAVOIE    Haute-Savoie
11      75      75056   0       PARIS   Paris
23      76      76540   3       SEINE-MARITIME  Seine-Maritime
11      77      77288   0       SEINE-ET-MARNE  Seine-et-Marne
11      78      78646   4       YVELINES        Yvelines
54      79      79191   4       DEUX-SEVRES     Deux-Sèvres
22      80      80021   3       SOMME   Somme
73      81      81004   2       TARN    Tarn
73      82      82121   0       TARN-ET-GARONNE Tarn-et-Garonne
93      83      83137   2       VAR     Var
93      84      84007   0       VAUCLUSE        Vaucluse
52      85      85191   3       VENDEE  Vendée
54      86      86194   3       VIENNE  Vienne
74      87      87085   3       HAUTE-VIENNE    Haute-Vienne
41      88      88160   4       VOSGES  Vosges
26      89      89024   5       YONNE   Yonne
43      90      90010   0       TERRITOIRE_DE_BELFORT   Territoire de Belfort
11      91      91228   5       ESSONNE Essonne
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA_REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Afficher la ligne contenant 85191

$ grep 85191 depts2012.txt
52      85      85191   3       VENDEE  Vendée
$

Utilisation d'une ERe et d'une recherche insensible à la casse

$ grep -E -i '(paris|vosges)' depts2012.txt
11      75      75056   0       PARIS   Paris
41      88      88160   4       VOSGES  Vosges
$

Afficher les lignes ne commençant pas par le chiffre 9

$ grep -v '^9' depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
82      07      07186   5       ARDECHE Ardèche
21      08      08105   4       ARDENNES        Ardennes
73      09      09122   5       ARIEGE  Ariège
21      10      10387   5       AUBE    Aube
73      12      12202   5       AVEYRON Aveyron
25      14      14118   2       CALVADOS        Calvados
83      15      15014   2       CANTAL  Cantal
54      16      16015   3       CHARENTE        Charente
54      17      17300   3       CHARENTE-MARITIME       Charente-Maritime
24      18      18033   2       CHER    Cher
74      19      19272   3       CORREZE Corrèze
26      21      21231   3       COTE-D'OR       Côte-d'Or
53      22      22278   4       COTES-D'ARMOR   Côtes-d'Armor
74      23      23096   3       CREUSE  Creuse
72      24      24322   3       DORDOGNE        Dordogne
43      25      25056   2       DOUBS   Doubs
82      26      26362   3       DROME   Drôme
23      27      27229   5       EURE    Eure
24      28      28085   1       EURE-ET-LOIR    Eure-et-Loir
53      29      29232   2       FINISTERE       Finistère
73      31      31555   3       HAUTE-GARONNE   Haute-Garonne
73      32      32013   2       GERS    Gers
72      33      33063   3       GIRONDE Gironde
53      35      35238   1       ILLE-ET-VILAINE Ille-et-Vilaine
24      36      36044   5       INDRE   Indre
24      37      37261   1       INDRE-ET-LOIRE  Indre-et-Loire
82      38      38185   5       ISERE   Isère
43      39      39300   2       JURA    Jura
72      40      40192   4       LANDES  Landes
24      41      41018   0       LOIR-ET-CHER    Loir-et-Cher
82      42      42218   3       LOIRE   Loire
83      43      43157   3       HAUTE-LOIRE     Haute-Loire
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
24      45      45234   2       LOIRET  Loiret
73      46      46042   2       LOT     Lot
72      47      47001   0       LOT-ET-GARONNE  Lot-et-Garonne
52      49      49007   0       MAINE-ET-LOIRE  Maine-et-Loire
25      50      50502   3       MANCHE  Manche
21      51      51108   3       MARNE   Marne
21      52      52121   3       HAUTE-MARNE     Haute-Marne
52      53      53130   3       MAYENNE Mayenne
41      54      54395   0       MEURTHE-ET-MOSELLE      Meurthe-et-Moselle
41      55      55029   3       MEUSE   Meuse
53      56      56260   2       MORBIHAN        Morbihan
41      57      57463   3       MOSELLE Moselle
26      58      58194   3       NIEVRE  Nièvre
31      59      59350   2       NORD    Nord
22      60      60057   5       OISE    Oise
25      61      61001   5       ORNE    Orne
31      62      62041   2       PAS-DE-CALAIS   Pas-de-Calais
83      63      63113   2       PUY-DE-DOME     Puy-de-Dôme
72      64      64445   4       PYRENEES-ATLANTIQUES    Pyrénées-Atlantiques
73      65      65440   4       HAUTES-PYRENEES Hautes-Pyrénées
42      67      67482   2       BAS-RHIN        Bas-Rhin
42      68      68066   2       HAUT-RHIN       Haut-Rhin
82      69      69123   2       RHONE   Rhône
43      70      70550   3       HAUTE-SAONE     Haute-Saône
26      71      71270   0       SAONE-ET-LOIRE  Saône-et-Loire
52      72      72181   3       SARTHE  Sarthe
82      73      73065   3       SAVOIE  Savoie
82      74      74010   3       HAUTE-SAVOIE    Haute-Savoie
11      75      75056   0       PARIS   Paris
23      76      76540   3       SEINE-MARITIME  Seine-Maritime
11      77      77288   0       SEINE-ET-MARNE  Seine-et-Marne
11      78      78646   4       YVELINES        Yvelines
54      79      79191   4       DEUX-SEVRES     Deux-Sèvres
22      80      80021   3       SOMME   Somme
73      81      81004   2       TARN    Tarn
73      82      82121   0       TARN-ET-GARONNE Tarn-et-Garonne
52      85      85191   3       VENDEE  Vendée
54      86      86194   3       VIENNE  Vienne
74      87      87085   3       HAUTE-VIENNE    Haute-Vienne
41      88      88160   4       VOSGES  Vosges
26      89      89024   5       YONNE   Yonne
43      90      90010   0       TERRITOIRE_DE_BELFORT   Territoire de Belfort
11      91      91228   5       ESSONNE Essonne
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA_REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Afficher les lignes commençant par un 0 ou se terminant par un s

$ grep -e '^0' -e 's$' depts2012.txt
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
21      08      08105   4       ARDENNES        Ardennes
25      14      14118   2       CALVADOS        Calvados
43      25      25056   2       DOUBS   Doubs
73      32      32013   2       GERS    Gers
72      40      40192   4       LANDES  Landes
31      62      62041   2       PAS-DE-CALAIS   Pas-de-Calais
72      64      64445   4       PYRENEES-ATLANTIQUES    Pyrénées-Atlantiques
73      65      65440   4       HAUTES-PYRENEES Hautes-Pyrénées
91      66      66136   4       PYRENEES-ORIENTALES     Pyrénées-Orientales
11      75      75056   0       PARIS   Paris
11      78      78646   4       YVELINES        Yvelines
54      79      79191   4       DEUX-SEVRES     Deux-Sèvres
41      88      88160   4       VOSGES  Vosges
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA_REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Afficher et numéroter les lignes commençant par 11

$ grep -n '^11' depts2012.txt
77:11   75      75056   0       PARIS   Paris
79:11   77      77288   0       SEINE-ET-MARNE  Seine-et-Marne
80:11   78      78646   4       YVELINES        Yvelines
93:11   91      91228   5       ESSONNE Essonne
94:11   92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
95:11   93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
96:11   94      94028   2       VAL-DE-MARNE    Val-de-Marne
97:11   95      95500   2       VAL-D'OISE      Val-d'Oise
$

Effectuer une recherche sans afficher le résultat. Afficher le code de retour de la commande pour vérifier l'état de la recherche.

$ grep -q '^11' depts2012.txt
$ echo $?
0
$

Afficher seulement le nombre de lignes trouvées

$ grep -c -e '^0' -e 's$' depts2012.txt
21
$

Rechercher un mot exact

$ grep COTE depts2012.txt # Sans l'option -w 2 lignes sont affichées
26      21      21231   3       COTE-D'OR       Côte-d'Or
53      22      22278   4       COTES-D'ARMOR   Côtes-d'Armor
$ grep -w COTE depts2012.txt # Avec l'option -w seule la ligne contenant le mot COTE s'affiche
26      21      21231   3       COTE-D'OR       Côte-d'Or
$

Afficher les lignes contenant au moins 2 caractères. Utilisation du fichier fic7 en exemple.

$ cat fic7
erytert
e
er
..
uhy
2
e_
34
546
$ grep '..' fic7
erytert
er
..
uhy
e_
34
546
$

Ne pas interpréter le motif comme une expression régulière.

$ grep -F '..' fic7
..
$

Rechercher les lignes ne contenant que des chiffres. Utilisation d'une ERe.

$ grep -E -x '[0-9]+' fic7 # Avec l'option -x
2
34
546
$ grep -E '^[0-9]+$' fic7 # Même résultat mais sans l'option -x
2
34
546
$

Utiliser un fichier pour rechercher les expressions régulières

$ cat rech.er
# Rechercher les lignes se terminant par la lettre t
t$
# Rechercher les lignes contenant uniquement des chiffres
^[0-9]+$
$ grep -E -f rech.er fic7
erytert
2
34
546
$

Utiliser grep derrière un tube pour rechercher un processus particulier.

$ ps -ef | grep apache2
root       302     1  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
www-data   351   302  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
www-data   352   302  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
root      3783  3105  0 14:10 pts/0    00:00:00 grep --color=always apache2
$

Dans ce cas, la commande grep apparait également. L'utilisation de 2 tubes est donc nécessaire.

$ ps -ef | grep apache2 | grep -v grep # On demande à grep de ne pas afficher "grep"
root       302     1  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
www-data   351   302  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
www-data   352   302  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
$

Etiquettes: 

Dernières lignes d'un flux de données : tail

La commande tail permet d'afficher, par défaut, les 10 dernières lignes d'un flux de données. Cette commande est souvent utilisée pour afficher les dernières lignes d'un fichier de log.

Syntaxe

tail [options] [fichiers ...]
Principales fonctions :
-f Affiche en temps réel les ajouts en fin de fichier
-n Affiche les n dernières lignes indiquées
-n +x Affiche à partir de la ligne x jusqu'à la fin du fichier

Exemples

Afficher les 5 dernières lignes du fichier /var/log/messages

$ tail -n 5 /var/log/messages # Utilisation de -n 5
May 18 14:09:12 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="287" x-info="http://www.rsyslog.com"] (re)start
May 18 15:26:36 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 15:26:36 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
May 19 06:18:39 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 19 06:18:39 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
$ tail -5 /var/log/messages # Identique mais en indiquant seulement -5
May 18 14:09:12 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="287" x-info="http://www.rsyslog.com"] (re)start
May 18 15:26:36 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 15:26:36 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
May 19 06:18:39 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 19 06:18:39 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
$

Afficher à partir de la ligne 9

$ tail -n +9 /var/log/messages
May 18 12:21:34 kernel: Kernel logging (proc) stopped.
May 18 12:21:34 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] exiting on signal 15.
May 18 14:09:12 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 14:09:12 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="287" x-info="http://www.rsyslog.com"] (re)start
May 18 15:26:36 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 15:26:36 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
May 19 06:18:39 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 19 06:18:39 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
$

Afficher les dernières lignes d'un fichier en temps réel

$ tail -f /var/log/messages
May 18 04:54:03 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
May 18 12:21:15 shutdown[10971]: shutting down for system halt
May 18 12:21:34 kernel: Kernel logging (proc) stopped.
May 18 12:21:34 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] exiting on signal 15.
May 18 14:09:12 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 14:09:12 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="287" x-info="http://www.rsyslog.com"] (re)start
May 18 15:26:36 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 15:26:36 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
May 19 06:18:39 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 19 06:18:39 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
^C # Pour interrompre la commande
$

Etiquettes: 

Premières lignes d'un flux de données : head

La commande head permet de visualiser, par défaut, les 10 premières lignes d'un flux de données.

Syntaxe

head [-n] [fichiers ...]
Principales options :
-n Affiche les n premières lignes

Exemple

Afficher les 5 premières lignes du fichier /var/log/messages

$ head -5 /var/log/messages
May 13 04:54:06 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
May 14 04:54:03 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
May 15 04:54:03 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
May 15 04:54:03 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
May 16 04:54:03 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
$

Etiquettes: 

Duplication de la sortie standard : tee

La commande tee récupère un flux de données sur son entrée standard, l'envoie dans le fichier passé en argument et sur la sortie standard. Cela permet d'avoir à la fois le résultat à l'écran et dans un fichier.

Syntaxe

commande | tee [options] fichier
Principales options :
-a Ajoute à la fin de "fichier" si celui-ci existe déjà

Exemple

A partir du fichier /var/log/apache2/access.log, récupérer les lignes datant du 19/05/2012 en les affichant à l'écran et en les enregistrant dans un fichier

$ grep '19/May/2012' /var/log/apache2/access.log | tee 2012_05_19_log_apache
x.x.x.x - - [19/May/2012:14:49:03 +0200] "GET / HTTP/1.1" 200 7226 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:04 +0200] "GET /node.css?7 HTTP/1.1" 200 664 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:04 +0200] "GET /defaults.css?7 HTTP/1.1" 200 718 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
...
$

$ head -5 2012_05_19_log_apache
x.x.x.x - - [19/May/2012:14:49:03 +0200] "GET / HTTP/1.1" 200 7226 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:04 +0200] "GET /node.css?7 HTTP/1.1" 200 664 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:04 +0200] "GET /defaults.css?7 HTTP/1.1" 200 718 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:05 +0200] "GET /layout.css?7 HTTP/1.1" 200 1444 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:05 +0200] "GET /style.css?7 HTTP/1.1" 200 7648 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
$

Etiquettes: 

Numérotation de lignes : nl

La commande nl permet d'afficher un fichier texte en numérotant les lignes. Par défaut, les lignes vides ne sont pas numérotées.

Syntaxe

nl [options] [fichiers ...]
Principales options :
-b a Numéroter toutes les lignes
-b t Ne pas numéroter les lignes vides

Exemple

$ nl fic7
     1  erytert
 
     2  e
     3  er
     4  ..
     5  uhy
 
     6  2
     7  e_
     8  34
     9  546
 
$

Numéroter toutes les lignes, mêmes vides

$ nl -ba fic7
     1  erytert
     2
     3  e
     4  er
     5  ..
     6  uhy
     7
     8  2
     9  e_
    10  34
    11  546
    12
$

Etiquettes: 

Présentation d'un flux de données : pr

La commande pr affiche un texte de manière formatée. Par défaut, chaque page fait 66 lignes, dont 5 lignes d'en-tête et 5 ligne de pied de page.

Syntaxe

pr [options] [fichiers ...]
Quelques options :
-c nb Disposition en nb colonnes
-h texte Ecrire un  titre en en-tête
-l nb Nombre de lignes d'une page

Exemples

$ cat depts2012
CP : 01053      DEPT : AIN
CP : 02408      DEPT : AISNE
CP : 03190      DEPT : ALLIER
CP : 04070      DEPT : ALPES-DE-HAUTE-PROVENCE
CP : 05061      DEPT : HAUTES-ALPES
CP : 06088      DEPT : ALPES-MARITIMES
CP : 07186      DEPT : ARDECHE
CP : 08105      DEPT : ARDENNES
CP : 09122      DEPT : ARIEGE
CP : 10387      DEPT : AUBE
CP : 11069      DEPT : AUDE
CP : 12202      DEPT : AVEYRON
CP : 13055      DEPT : BOUCHES-DU-RHONE
CP : 14118      DEPT : CALVADOS
CP : 15014      DEPT : CANTAL
CP : 16015      DEPT : CHARENTE
CP : 17300      DEPT : CHARENTE-MARITIME
CP : 18033      DEPT : CHER
CP : 19272      DEPT : CORREZE
CP : 21231      DEPT : COTE-D'OR
CP : 22278      DEPT : COTES-D'ARMOR
CP : 23096      DEPT : CREUSE
CP : 24322      DEPT : DORDOGNE
CP : 25056      DEPT : DOUBS
CP : 26362      DEPT : DROME
CP : 27229      DEPT : EURE
CP : 28085      DEPT : EURE-ET-LOIR
CP : 29232      DEPT : FINISTERE
CP : 30189      DEPT : GARD
CP : 31555      DEPT : HAUTE-GARONNE
CP : 32013      DEPT : GERS
CP : 33063      DEPT : GIRONDE
CP : 34172      DEPT : HERAULT
CP : 35238      DEPT : ILLE-ET-VILAINE
CP : 36044      DEPT : INDRE
CP : 37261      DEPT : INDRE-ET-LOIRE
CP : 38185      DEPT : ISERE
CP : 39300      DEPT : JURA
CP : 40192      DEPT : LANDES
CP : 41018      DEPT : LOIR-ET-CHER
CP : 42218      DEPT : LOIRE
CP : 43157      DEPT : HAUTE-LOIRE
CP : 44109      DEPT : LOIRE-ATLANTIQUE
CP : 45234      DEPT : LOIRET
CP : 46042      DEPT : LOT
CP : 47001      DEPT : LOT-ET-GARONNE
CP : 48095      DEPT : LOZERE
CP : 49007      DEPT : MAINE-ET-LOIRE
CP : 50502      DEPT : MANCHE
CP : 51108      DEPT : MARNE
CP : 52121      DEPT : HAUTE-MARNE
CP : 53130      DEPT : MAYENNE
CP : 54395      DEPT : MEURTHE-ET-MOSELLE
CP : 55029      DEPT : MEUSE
CP : 56260      DEPT : MORBIHAN
CP : 57463      DEPT : MOSELLE
CP : 58194      DEPT : NIEVRE
CP : 59350      DEPT : NORD
CP : 60057      DEPT : OISE
CP : 61001      DEPT : ORNE
CP : 62041      DEPT : PAS-DE-CALAIS
CP : 63113      DEPT : PUY-DE-DOME
CP : 64445      DEPT : PYRENEES-ATLANTIQUES
CP : 65440      DEPT : HAUTES-PYRENEES
CP : 66136      DEPT : PYRENEES-ORIENTALES
CP : 67482      DEPT : BAS-RHIN
CP : 68066      DEPT : HAUT-RHIN
CP : 69123      DEPT : RHONE
CP : 70550      DEPT : HAUTE-SAONE
CP : 71270      DEPT : SAONE-ET-LOIRE
CP : 72181      DEPT : SARTHE
CP : 73065      DEPT : SAVOIE
CP : 74010      DEPT : HAUTE-SAVOIE
CP : 75056      DEPT : PARIS
CP : 76540      DEPT : SEINE-MARITIME
CP : 77288      DEPT : SEINE-ET-MARNE
CP : 78646      DEPT : YVELINES
CP : 79191      DEPT : DEUX-SEVRES
CP : 80021      DEPT : SOMME
CP : 81004      DEPT : TARN
CP : 82121      DEPT : TARN-ET-GARONNE
CP : 83137      DEPT : VAR
CP : 84007      DEPT : VAUCLUSE
CP : 85191      DEPT : VENDEE
CP : 86194      DEPT : VIENNE
CP : 87085      DEPT : HAUTE-VIENNE
CP : 88160      DEPT : VOSGES
CP : 89024      DEPT : YONNE
CP : 90010      DEPT : TERRITOIRE_DE_BELFORT
CP : 91228      DEPT : ESSONNE
CP : 92050      DEPT : HAUTS-DE-SEINE
CP : 93008      DEPT : SEINE-SAINT-DENIS
CP : 94028      DEPT : VAL-DE-MARNE
CP : 95500      DEPT : VAL-D'OISE
CP : 97105      DEPT : GUADELOUPE
CP : 97209      DEPT : MARTINIQUE
CP : 97302      DEPT : GUYANE
CP : 97411      DEPT : LA_REUNION
CP : 97608      DEPT : MAYOTTE
$

$ pr depts2012 | more
 
 
2012-05-19 17:19                    depts2012                     Page 1
 
 
CP : 01053      DEPT : AIN
CP : 02408      DEPT : AISNE
CP : 03190      DEPT : ALLIER
CP : 04070      DEPT : ALPES-DE-HAUTE-PROVENCE
CP : 05061      DEPT : HAUTES-ALPES
CP : 06088      DEPT : ALPES-MARITIMES
CP : 07186      DEPT : ARDECHE
CP : 08105      DEPT : ARDENNES
CP : 09122      DEPT : ARIEGE
CP : 10387      DEPT : AUBE
CP : 11069      DEPT : AUDE
CP : 12202      DEPT : AVEYRON
CP : 13055      DEPT : BOUCHES-DU-RHONE
CP : 14118      DEPT : CALVADOS
CP : 15014      DEPT : CANTAL
CP : 16015      DEPT : CHARENTE
CP : 17300      DEPT : CHARENTE-MARITIME
CP : 18033      DEPT : CHER
CP : 19272      DEPT : CORREZE
...
$

Présentation du fichier en 2 colonnes de 15 lignes par page avec un titre sur chaque page

$ pr -l 15 -c2 -h "Liste des departements" depts2012 | more
 
 
2012-05-19 17:19              Liste des departements              Page 1
 
 
CP : 01053      DEPT : AIN          CP : 06088  DEPT : ALPES-MARITI
CP : 02408      DEPT : AISNE        CP : 07186  DEPT : ARDECHE
CP : 03190      DEPT : ALLIER       CP : 08105  DEPT : ARDENNES
CP : 04070      DEPT : ALPES-DE-HAU CP : 09122  DEPT : ARIEGE
CP : 05061      DEPT : HAUTES-ALPES CP : 10387  DEPT : AUBE
 
 
 
 
 
 
 
2012-05-19 17:19              Liste des departements              Page 2
 
 
CP : 11069      DEPT : AUDE         CP : 16015  DEPT : CHARENTE
CP : 12202      DEPT : AVEYRON      CP : 17300  DEPT : CHARENTE-MAR
CP : 13055      DEPT : BOUCHES-DU-R CP : 18033  DEPT : CHER
CP : 14118      DEPT : CALVADOS     CP : 19272  DEPT : CORREZE
CP : 15014      DEPT : CANTAL       CP : 21231  DEPT : COTE-D'OR
...
$

Etiquettes: 

Traitement de données

Comptage de lignes, de mots et de caractères : wc

La commande wc compte le nombre de lignes, de mots et de caractères.

Syntaxe

wc [OPTION]... [FILE]...
Principales options :
-l Compter le nombre de lignes
-w Compter le nombre de mots
-c Compter le nombre d'octets
-m Compter le nombre de caractères

Exemples

Nombre de lignes, mots et caractères du fichier depts2012

$ wc depts2012
  99  594 2769 depts2012
$

Nombre de lignes uniquement

$ wc -l depts2012
99 depts2012
$

Nombre de mots uniquement

$ wc -w depts2012
594 depts2012
$

Nombre de caractères uniquement

$ wc -m depts2012
2769 depts2012
$

Nombre de caractères contenus dans un texte saisi au clavier (attention au saut de ligne ajouté par la commande echo)

$ read texte
ceci est un texte
$ echo "$texte" | wc -m
18     # Le saut de ligne est comptabilisé
$ echo -e "$texte\c" | wc -m
17     # Avec l'option -e et \c le saut de ligne n'est pas comptabilisé
$ echo -n "$texte" | wc -m
17     # Idem mais avec l'option -n
$

Etiquettes: 

Extraction de caractères : cut

La commande cut sert à récupérer des caractères ou des champs d'une ligne.

Syntaxe

Couper par caractères

cut -c3 [fichiers ...] Le 3ème caractère
cut -c3-5 [fichiers ...] Du 3ème au 5ème caractère
cut -c-3 [fichiers ...] Jusqu'au 3ème caractère
cut -c3- [fichiers ...] A partir du 3ème caractère
cut -c3,10 [fichiers ...] Le 3ème et le 10ème caractère

Couper par champs

cut -dsep -f3 [fichiers ...] Le 3ème champ
cut -dsep -f3-5 [fichiers ...] Du 3ème au 5ème champ
cut -dsep -f-3 [fichiers ...] Jusqu'au 3ème champ
cut -dsep -f3- [fichiers ...] A partir du 3ème champ
cut -dsep -f3,10 [fichiers ...] Le 3ème et le 10ème champ

L'option -d permet d'exprimer le caractère séparateur de champ. Le caractère séparateur par défaut est la tabulation.

Exemples

Couper les 2 Premiers chiffres d'un code postal

$ echo 44150 | cut -c1-2
44
$

Afficher le 1er, 6ème et 7ème champs des 5 dernières lignes du fichier /etc/passwd

$ tail -5 /etc/passwd | cut -d: -f1,6,7
nobody:/nonexistent:/bin/sh
libuuid:/var/lib/libuuid:/bin/sh
postfix:/var/spool/postfix:/bin/false
sshd:/var/run/sshd:/usr/sbin/nologin
mysql:/var/lib/mysql:/bin/false
$

Si le caractère séparateur est un caractère spécial du shell, il faut le protéger.

$ ligne="val1|val2|val3"
$ echo $ligne | cut -d'|' -f1
val1
$

Etiquettes: 

Tri de données : sort

La commande sort permet de trier les lignes d'un flux de données textes.

Syntaxe

sort [options] -k champs[.car] [options], champ[.car] [options] [fichiers ...]
Principales options :
-b : Option à utiliser lorsque le critère de tri est alphanumérique et que les valeurs dans les champs sont calés à droite et complétés avec des espaces à gauche
-k : champ [.car] [options] , champ [.car] [options] Spécifier le ou les champs à prendre en compte en tant que critère de tri. Le critère de tri peut commencer ou finir à une certaine position de caractère du champ.
-n : Préciser que le critère de tri doit être traité comme valeur numérique et non comme chaine de caractères.
-r : Demander un tri décroissant
-t sep : Préciser le caractère séparateur de champs (espace par défaut)
-u : Suppression des doublons

 

Exemples

Utilisation du fichier /etc/passwd

$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

Trier le fichier sans aucune option.
Le fichier est trié en fonction de la première lettre de chaque ligne.

$ sort /etc/passwd
backup:x:34:34:backup:/var/backups:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
news:x:9:9:news:/var/spool/news:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
proxy:x:13:13:proxy:/bin:/bin/sh
root:x:0:0:root:/root:/bin/bash
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
sys:x:3:3:sys:/dev:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
$

Trier le fichier sur le 3ème champ (valeurs numériques).
Le tri est alphanumérique.

$ sort -t: -k 3,3 /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
proxy:x:13:13:proxy:/bin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
$

Pour que le tri soit numérique il faut ajouter l'option -n

$ sort -t: -k 3,3 -n /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
$

Exemple de tri sur un fichier dont le champ servant de critère de tri est alphanumérique et complété avec des espaces à gauche.

$ cat listeFic
-rw-r--r-- 1 root root 17 May 17 21:31       fic1
-rw-r--r-- 1 root root 17 May 17 21:36       fic4
-rw-r--r-- 1 root root 23 May 17 21:33       fic2
-rw-r--r-- 1 root root 35 May 19 15:02       fic7
-rw-r--r-- 1 root root 36 May 17 21:52       fic5
-rw-r--r-- 1 root root 40 May 17 21:33       fic3
-rw-r--r-- 1 root root 48 May 19 17:59       fic8
-rw-r--r-- 1 root root 74 May 17 22:01   fic6.txt
$

Tri sur le champ 9. Valeurs alphanumériques complétées avec des espaces à gauche. Sans l'option -b, le tri est mal réalisé.

$ sort -k 9,9 listeFic
-rw-r--r-- 1 root root 17 May 17 21:31       fic1
-rw-r--r-- 1 root root 23 May 17 21:33       fic2
-rw-r--r-- 1 root root 40 May 17 21:33       fic3
-rw-r--r-- 1 root root 17 May 17 21:36       fic4
-rw-r--r-- 1 root root 36 May 17 21:52       fic5
-rw-r--r-- 1 root root 35 May 19 15:02       fic7
-rw-r--r-- 1 root root 48 May 19 17:59       fic8
-rw-r--r-- 1 root root 74 May 17 22:01   fic6.txt
$

Idem mais avec l'option -b

$ sort -b -k 9,9 listeFic
-rw-r--r-- 1 root root 17 May 17 21:31       fic1
-rw-r--r-- 1 root root 23 May 17 21:33       fic2
-rw-r--r-- 1 root root 40 May 17 21:33       fic3
-rw-r--r-- 1 root root 17 May 17 21:36       fic4
-rw-r--r-- 1 root root 36 May 17 21:52       fic5
-rw-r--r-- 1 root root 74 May 17 22:01   fic6.txt
-rw-r--r-- 1 root root 35 May 19 15:02       fic7
-rw-r--r-- 1 root root 48 May 19 17:59       fic8
$

Utilisation de 2 critères de tri: Tri sur le champ 7 (numérique) et sur le champ 9 (alphanumérique)

$ sort -k 7n,7 -k 9b,9 listeFic
-rw-r--r-- 1 root root 74 May 15 22:01   fic6.txt
-rw-r--r-- 1 root root 35 May 15 15:02       fic7
-rw-r--r-- 1 root root 17 May 17 21:31       fic1
-rw-r--r-- 1 root root 23 May 17 21:33       fic2
-rw-r--r-- 1 root root 40 May 17 21:33       fic3
-rw-r--r-- 1 root root 17 May 17 21:36       fic4
-rw-r--r-- 1 root root 36 May 17 21:52       fic5
-rw-r--r-- 1 root root 48 May 19 17:59       fic8
$

Trier un fichier suivant les mois de l'année

$ cat fic91
April
August
December
February
January
July
June
March
May
November
October
September
$

$ sort -M fic91
January
February
March
April
May
June
July
August
September
October
November
December
$

Etiquettes: 

paste

La commande paste concatène les lignes de même niveau des fichiers passés en argument. Avec l'option -s, chaque fichier est traité de manière indépendante et verra toutes ses lignes concaténées en une seule. Les éléments concaténés sont séparés par une tabulation.

Syntaxe

paste [options] [fichiers ...]
Principales options :
-s : Concaténer toutes les lignes en une seule
-d listesep : Les caractères cités dans listesep seront utilisés pour séparer les champs en sortie

Exemple

Exemple avec 2 fichiers contenant les dépenses d'un ménage pour le mois de janvier 2012 et février 2012. Les catégories sont mentionnées dans le même ordre.

$ cat janvier2012
Alimentation    50.00
Eau             25.00
Electricite     123.50
Loyer           456.90
Assurances      234.00
 
$ cat fevrier2012
Alimentation    67.00
Eau             34.00
Electricite     156.00
Loyer           456.90
Assurances      225.00
$

Mettre sur la même ligne les montants de chaque catégorie.

$ paste janvier2012 fevrier2012
Alimentation    50.00   Alimentation    67.00
Eau             25.00   Eau             34.00
Electricite     123.50  Electricite     156.00
Loyer           456.90  Loyer           456.90
Assurances      234.00  Assurances      225.00
$

Retirer la 3ème colonne et ajouter des en-têtes de colonnes et le total pour chaque mois.

$ paste janvier2012 fevrier2012 | awk 'BEGIN{printf("%-15s\t%7s\t%7s\n","POSTE","JANVIER","FEVRIER")}{printf("%-15s\t%7.2f\t%7.2f\n",$1,$2,$4);jan+=$2;fev+=$4}END{printf("%-15s\t%7.2f\t%7.2f\n","TOTAL",jan,fev)}'
POSTE           JANVIER FEVRIER
Alimentation      50.00   67.00
Eau               25.00   34.00
Electricite      123.50  156.00
Loyer            456.90  456.90
Assurances       234.00  225.00
TOTAL            889.40  938.90
$

Concaténer toutes les lignes en une seule.

$ paste -s janvier2012 fevrier2012
Alimentation    50.00   Eau             25.00   Electricite     123.50  Loyer           456.90  Assurances      234.00
Alimentation    67.00   Eau             34.00   Electricite     156.00  Loyer           456.90  Assurances      225.00
$

Etiquettes: 

split

La commande split permet d'éclater un fichier en fragments. Chaque fragment étant stocké dans des fichiers nommés PREFIXEaa, PREFIXEab, ... PREFIXE ayant pour valeur par défaut "x". Si aucun nom de fichier n'est spécifié, l'entrée standard est utilisée.

Syntaxe

split [options] [fichiers ... [prefixe]]
Principales options :
-b nb : Le fichier est éclaté tous les "nb" octets
-l nb : Le fichier est éclaté toutes les "nb" lignes

Exemples

Exemple avec un fichier contenant 9 lignes

$ cat fic10
ligne1
ligne2
ligne3
ligne4
ligne5
ligne6
ligne7
ligne8
ligne9
$

Eclater le fichier en différents fichiers de 3 lignes chacuns. Par défaut, chaque fichier généré sera préfixé par la lettre "x".

$ split -l3 fic10
$ ls -l x*
-rw-r--r-- 1 root root 21 May 23 08:56 xaa
-rw-r--r-- 1 root root 21 May 23 08:56 xab
-rw-r--r-- 1 root root 21 May 23 08:56 xac
$ cat xaa
ligne1
ligne2
ligne3
$ cat xab
ligne4
ligne5
ligne6
$ cat xac
ligne7
ligne8
ligne9
$

Modifier le préfixe des fichiers générés.

$ split -l3 fic10 fichier
$ ls -l fichier*
-rw-r--r-- 1 root root 21 May 23 08:58 fichieraa
-rw-r--r-- 1 root root 21 May 23 08:58 fichierab
-rw-r--r-- 1 root root 21 May 23 08:58 fichierac
$

Etiquettes: 

Transformation de caractères : tr

La commande tr permet d'appliquer un traitement sur certains caractères d'un flux de données : suppression, substitutions ... Cette commande exploite uniquement les données arrivant sur l'entrée standard.

Syntaxe

tr [options] ensemble1 [ensemble2]
Principale option :
[-d caractères] : La liste des caractères qui doivent être supprimés du flux de données.

Substitution de caractères

Deux ensembles de caractères doivent être spécifiés. Chaque ensemble comporte le même nombre de caractères. Tout caractère de ensemble1 trouvé dans le flux de données est substitué par le caractère de même position situé dans ensemble2.

Exemples

La chaine abcd est traitée : chaque caractère b est remplacé par 2 et chaque caractère d est remplacé par 4.

$ echo abcd | tr bd 24
a2c4
$

Il est possible d'utiliser la notion d'intervalles de caractères en utilisant des crochets. Ici chaque minuscule est remplacée par une majuscule correspondante :

$ echo abcd | tr "[a-z]" "[A-Z]"
ABCD
$

Pour traiter un fichier, il faut utiliser la redirection :

$ cat fic1
ceci est un test
$ tr "[a-z]" "[A-Z]" < fic1
CECI EST UN TEST
$

Suppression de caractères

L'option -d permet de supprimer certains caractères du flux de données.

Exemples

Transformation d'un fichier au format DOS (fin de lignes "\r\n") en un format UNIX ("\n"). Retrait du caractère "\r" :

$ od -c fic100
0000000   c   e   c   i       e   s   t       u   n       t   e   s   t
0000020  \r  \n
0000022
$ tr -d '\r' < fic100 > fic101
$ od -c fic101
0000000   c   e   c   i       e   s   t       u   n       t   e   s   t
0000020  \n
0000021
$

En ajoutant l'option -c à l'option -d, la commande supprime tous les caractères non présents dans la liste passée en argument :

$ echo "Linux Pratique HS N°26 – Février/Mars 2013 – En kiosque" | tr -dc a-zA-Z; echo
LinuxPratiqueHSNFvrierMarsEnkiosque

La dernière commande echo permet uniquement d'afficher un retour à la ligne.

Etiquettes: 

Dédoublonnage : uniq

La commande uniq permet de dédoublonner les lignes d'un fichier. Seules les lignes identiques consécutives sont traitées. Elle s'utilise le plus souvent à la suite de la commande sort. Le résultat est stocké dans un fichier de sortie, si celui-ci est spécifié, sur la sortie standard dans le cas contraire.

Syntaxe

uniq [options] [fichier_entree [fichier_sortie]]
Principales options :
-d : Affichage des doublons
-c : Comptage des doublons

Exemples

Le fichier fic20 contient des lignes en double

$ cat fic20
ceci est un test
ceci est un test
ceci est un test
ceci est un test
ceci est un autre test
ceci est un test
ceci est un autre test
ceci    est     un      test
ceci est un     test
et un   autre test
ceci est un fichier
$

Le fichier doit être trié pour que les lignes identiques soient consécutives :

$ sort fic20
ceci est un autre test
ceci est un autre test
ceci est un fichier
ceci est un test
ceci est un test
ceci est un test
ceci est un test
ceci est un test
ceci est un     test
ceci    est     un      test
et un   autre test
$

Et enfin, suppression des doublons :

$ sort fic20 | uniq
ceci est un autre test
ceci est un fichier
ceci est un test
ceci est un     test
ceci    est     un      test
et un   autre test
$

Résultat identique avec la commande sort et l'option -u :

$ sort -u fic20
ceci est un autre test
ceci est un fichier
ceci est un test
ceci est un     test
ceci    est     un      test
et un   autre test
$

Afficher devant chaque ligne son nombre d'occurences dans le fichier :

$ sort fic20 | uniq -c
      2 ceci est un autre test
      1 ceci est un fichier
      5 ceci est un test
      1 ceci est un     test
      1 ceci    est     un      test
      1 et un   autre test
$

Afficher uniquement les lignes ayant des doublons :

$ sort fic20 | uniq -d
ceci est un autre test
ceci est un test
$

Combinées les options -c et -d :

$ sort fic20 | uniq -cd
      2 ceci est un autre test
      5 ceci est un test
$

Afficher les lignes uniques :

$ sort fic20 | uniq -u
ceci est un fichier
ceci est un     test
ceci    est     un      test
et un   autre test
$ sort fic20 | uniq -cu
      1 ceci est un fichier
      1 ceci est un     test
      1 ceci    est     un      test
      1 et un   autre test
$

Etiquettes: 

Compressions, archivages et conversions

Compressions : gzip, bzip2

Les commandes gzip/gunzip, bzip2/bunzip2 compressent/décompressent chaque fichier passé en argument sur la ligne de commande. La commande bzip2 possède un meilleur taux de compression. Ces commandes se trouvent en standard sur les systèmes Linux et peuvent s'installer sur les systèmes UNIX si elles ne sont pas déjà présentes. La commande gzip produit l'extension .gz et la commande bzip2 produit l'extension .bz2.

Syntaxe de gzip

Compression :
gzip [options] [fichiers ...]
Décompression :
gunzip [options] [fichiers ...]
zcat [options] [fichiers ...]
Principales options :
-c : Envoie le résultat sur la sortie standard. La commande gunzip -c est équivalente à la commande zcat.
-t : Tester la validité de l'archive.

Si le nom de fichier est omis, les données à traiter sont lues sur l'entrée standard.

Exemple

Compression du fichier mon_fichier4. L'original est remplacé par le fichier compressé.

$ ls -lh mon_fichier4
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
$ gzip mon_fichier4
$ ls -lh mon_fichier4.gz
-rw-r--r-- 1 root root 2,1M 25 mai   16:36 mon_fichier4.gz
$

Décompression. Le fichier d'origine est restitué et remplace le fichier compressé.

$ gunzip mon_fichier4.gz
$ ls -lh mon_fichier4*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
$

Compression du fichier avec envoi du flux sur la sortie standard. Cette méthode permet de conserver le fichier d'origine.

$ gzip -c mon_fichier4 > mon_fichier4.gz
$ ls -lh mon_fichier4*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 2,1M 25 mai   16:42 mon_fichier4.gz
$

Décompressé en conservant le fichier compressé.

$ gunzip -c mon_fichier4.gz > mon_fichier5
$ ls -lh mon_fichier*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 2,1M 25 mai   16:42 mon_fichier4.gz
-rw-r--r-- 1 root root 6,6M 25 mai   16:44 mon_fichier5
$

Idem mais avec la commande zcat.

$ zcat mon_fichier4.gz > mon_fichier6
$ ls -lh mon_fichier*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 2,1M 25 mai   16:42 mon_fichier4.gz
-rw-r--r-- 1 root root 6,6M 25 mai   16:44 mon_fichier5
-rw-r--r-- 1 root root 6,6M 25 mai   16:46 mon_fichier6
$

Les commandes bzip2, bunzip2 et bzcat fonctionnent de manière similaire avec une légère différence au niveau des options.

Syntaxe de bzip2

Compression :
bzip2 [options] [fichiers ...]
Décompression :
bunzip2 [fichiers ...]
bzip2 -d
[options] [fichiers ...]
bzcat [options] [fichiers ...]
Principales options :
-c : Envoie le résultat sur la sortie standard. La commande bzip2 -dc est équivalente à la commande bzcat.
-d : Décompression
-t : Tester la validité de l'archive.

Exemple

Compression du fichier mon_fichier4. La compression avec bzip2 est meilleure qu'avec gzip.

$ ls -lh mon_fichier*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 2,1M 25 mai   16:42 mon_fichier4.gz
-rw-r--r-- 1 root root 6,6M 25 mai   16:44 mon_fichier5
-rw-r--r-- 1 root root 6,6M 25 mai   16:46 mon_fichier6
$ bzip2 -c mon_fichier4 > mon_fichier4.bz2
$ ls -lh mon_fichier*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 1,2M 25 mai   16:54 mon_fichier4.bz2
-rw-r--r-- 1 root root 2,1M 25 mai   16:42 mon_fichier4.gz
-rw-r--r-- 1 root root 6,6M 25 mai   16:44 mon_fichier5
-rw-r--r-- 1 root root 6,6M 25 mai   16:46 mon_fichier6
$

Analyse des différentes compressions

$ wc -c mon_fichier4
6903921 mon_fichier4
$ wc -m mon_fichier4
6903921 mon_fichier4
$ ls -l mon_fichier*
-rw-r--r-- 1 root root 6903921 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 1207370 25 mai   16:54 mon_fichier4.bz2
-rw-r--r-- 1 root root 2129319 25 mai   16:42 mon_fichier4.gz
-rw-r--r-- 1 root root 6903921 25 mai   16:44 mon_fichier5
-rw-r--r-- 1 root root 6903921 25 mai   16:46 mon_fichier6
$

Test réalisé avec un fichier contenant uniquement des caractères.
Le fichier mon_fichier4 contient 6 903 921 caractères et par conséquent une taille de 6 903 921 octets.

Compression avec gzip : Le fichier a été réduit à une taille de 2 129 319 octets. Ce qui représente une diminution de 69,16 %

Compression avec bzip2 : Le fichier a été réduit à une taille de 1 207 370 octets. Ce qui représente une diminution de 82,51%

Etiquettes: 

Archives tar

La commande tar permet de créer une archive à partir d'un ou plusieurs fichiers. Si le fichier est un répertoire, il sera archivé avec sa sous-arborescence.

Syntaxe

Création d'une archive :
tar -c [-zv] -f archive fichiers_a_archiver ...
Vérification d'une archive :
tar -t [-zv] -f archive [fichiers_a_verifier ...]
Extraction d'une archive :
tar -x [-zv] -f archive [fichiers_a_extraire ...]
Principales options :
-c : Création d'une archive
-t : Vérification d'une archive
-x : Extraction d'une archive
-f archive.tar : Nom de l'archive à créer, vérifier ou extraire
-f - : Dans le cas d'une création, le contenu de l'archive est envoyé sur la sortie standard. En extraction et vérification, le contenu de l'archive est attendu sur l'entrée standard
-v : Mode verbeux
-z : Sous Linux uniquement : permet de gérer en plus la compression
avec gzip
-j : Compression avec bzip2

Exemples

Archivage et compression du dossier dossier_a_sauvegarder. La commande tar envoi le contenu de l'archive à la commande gzip via un pipe et qui compresse l'archive dans un fichier dossier_a_sauvegarder.tar.gz

$ ls -R dossier_a_sauvegarder
dossier_a_sauvegarder:
aZerty  Bonjour  Coucou  help  history  last  monscript  pays2011.txt.1  piwik1  piwik2  result  ronan  ronan2  ronan3  search  test
$ tar -cvf - dossier_a_sauvegarder | gzip -c > dossier_a_sauvegarder.tar.gz
dossier_a_sauvegarder/
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
$ ls -l dossier_a_sauvegarder.tar.gz
-rw-r--r-- 1 root root 369562 25 mai   17:30 dossier_a_sauvegarder.tar.gz
$

Décompression et extraction. La commande gunzip décompresse le fichier et envoie le résultat à la commande tar qui extrait tout le contenu de l'archive.

$ gunzip -c dossier_a_sauvegarder.tar.gz | tar -xvf -
dossier_a_sauvegarder/
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
$

Lister le contenu d'une archive

$ gunzip -c dossier_a_sauvegarder.tar.gz | tar -tf -
dossier_a_sauvegarder/
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
$

Archivage et compression en utilisant l'option -z

$ tar -cvzf monArchive.tar.gz dossier_a_sauvegarder
dossier_a_sauvegarder/
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
$

Idem pour lister le contenu de l'archive. En fait, l'option -z n'est pas obligatoire. Que l'archive soit compressée ou pas les options -tf suffisent.

$ tar -tf monArchive.tar.gz
dossier_a_sauvegarder/
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
$

Ajouter un fichier à une archive existante en 3 étapes.

1 - Décompression (un fichier ne peut être ajouté à une archive compressée

$ gunzip monArchive.tar.gz

2 - Ajout du fichier xam à l'archive

$ tar -rf monArchive.tar xam

3 - Compression de l'archive

$ gzip monArchive.tar

Vérifier les différences entre le contenu de l'archive tar et les fichiers sources. Mettre à jour si nécessaire.

1 - Décompression de l'archive

$ gunzip monArchive.tar.gz

2 - Comparaison de l'archive avec les fichiers sources et affichage des différences existantes

$ tar -df monArchive.tar
dossier_a_sauvegarder/aZerty: La date de modification est différente.
dossier_a_sauvegarder/aZerty: La taille est différente.
$

3 - Suppresion du fichier ou des fichiers ayant des différences

$ tar --delete -f monArchive.tar dossier_a_sauvegarder/aZerty

4 - Mise à jour des fichiers en cas de différences trouvées.

$ tar -uf monArchive.tar dossier_a_sauvegarder

5 - Compression de l'archive

$ gzip monArchive.tar

Etiquettes: 

Archives cpio

La commande cpio permet également de faire des archives. Contrairement à la commande tar, elle n'est pas récursive. Tous les noms de fichiers entrant dans la composition de l'archive doivent être lus sur l'entrée standard. L'archive est envoyée sur la sortie standard. La commande est presque toujours utilisée derrière un tube lui-même précédé de la commande find qui permet de générer les noms de fichiers souhaités.

En vérification et restauration, l'archive à traiter est attendue sur l'entrée standard.

Syntaxe

Création d'une archive :
find . | cpio -o [-cv] > archive
Vérification d'une archive :
cpio -it [-cv] < archive [fichiers_a_verifier ...]
Extraction d'une archive :
cpio -i [-cdv] < archive [fichiers_a_extraire ...]
Principales options :
-c : Création/vérification/extraction d'une archive ayant un format d'en-tête d'archive portable entre les systèmes UNIX
-d : Extraction avec création des répertoires s'ils n'existent pas
-i : Extraction d'une archive
-it : Vérification d'une archive
-o : Création d'une archive
-v : Mode verbeux

Exemples

Archivage du dossier dossier_a_sauvegarder

$ find dossier_a_sauvegarder
dossier_a_sauvegarder
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
 
$ find dossier_a_sauvegarder | cpio -ocv > monArchive.cpio
dossier_a_sauvegarder
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
6674 blocs
$

Vérification de l'archive

$ cpio -icvt < monArchive.cpio
drwxr-xr-x   2 root     root            0 Apr 17 13:15 dossier_a_sauvegarder
-rw-r--r--   1 root     root          281 Apr 17 08:46 dossier_a_sauvegarder/ronan2
-rw-r--r--   1 root     root      3171822 Apr 17 08:46 dossier_a_sauvegarder/last
-rw-r--r--   1 root     root       195732 Apr 17 08:46 dossier_a_sauvegarder/search
-rw-r--r--   1 root     root          622 Apr 17 08:46 dossier_a_sauvegarder/piwik2
-rw-r--r--   1 root     root          281 Apr 17 08:46 dossier_a_sauvegarder/ronan3
-rw-r--r--   1 root     root          116 Apr 17 08:46 dossier_a_sauvegarder/result
-rw-r--r--   1 root     root         3083 Apr 17 08:46 dossier_a_sauvegarder/help
-rw-r--r--   1 root     root        26000 Apr 17 08:46 dossier_a_sauvegarder/history
-rw-r--r--   1 root     root            7 Apr 17 08:46 dossier_a_sauvegarder/Coucou
-rw-r--r--   1 root     root        15667 Apr 17 08:46 dossier_a_sauvegarder/pays2011.txt.1
-rw-r--r--   1 root     root           10 Apr 17 08:46 dossier_a_sauvegarder/monscript
-rw-------   1 root     root          281 Apr 17 08:46 dossier_a_sauvegarder/ronan
-rw-r--r--   1 root     root          387 Apr 17 08:46 dossier_a_sauvegarder/test
-rw-r--r--   1 root     root            0 Apr 17 08:46 dossier_a_sauvegarder/Bonjour
-rw-r--r--   1 root     root          622 Apr 17 08:46 dossier_a_sauvegarder/piwik1
-rw-r--r--   1 root     root           36 May 25 18:11 dossier_a_sauvegarder/aZerty
6674 blocs
$

Création d'une archive cpio compressée avec bzip2

$ find dossier_a_sauvegarder | cpio -ocv | bzip2 -c > monArchive.cpio.bz2
dossier_a_sauvegarder
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
6674 blocs
$

Décompression et extraction de l'archive

$ mkdir tmp
$ cp monArchive.cpio.bz2 tmp
$ cd tmp
$ ll
total 276
drwxr-xr-x  2 root root   4096 26 mai   08:59 .
drwx------ 12 root root   4096 26 mai   08:58 ..
-rw-r--r--  1 root root 266276 26 mai   08:59 monArchive.cpio.bz2
$ bzip2 -dc monArchive.cpio.bz2 | cpio -icvd
dossier_a_sauvegarder
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
6674 blocs
$ ll
total 280
drwxr-xr-x  3 root root   4096 26 mai   08:59 .
drwx------ 12 root root   4096 26 mai   08:58 ..
drwxr-xr-x  2 root root   4096 26 mai   08:59 dossier_a_sauvegarder
-rw-r--r--  1 root root 266276 26 mai   08:59 monArchive.cpio.bz2
$ ll dossier_a_sauvegarder/
total 3396
drwxr-xr-x 2 root root    4096 26 mai   08:59 .
drwxr-xr-x 3 root root    4096 26 mai   08:59 ..
-rw-r--r-- 1 root root      36 26 mai   08:59 aZerty
-rw-r--r-- 1 root root       0 26 mai   08:59 Bonjour
-rw-r--r-- 1 root root       7 26 mai   08:59 Coucou
-rw-r--r-- 1 root root    3083 26 mai   08:59 help
-rw-r--r-- 1 root root   26000 26 mai   08:59 history
-rw-r--r-- 1 root root 3171822 26 mai   08:59 last
-rw-r--r-- 1 root root      10 26 mai   08:59 monscript
-rw-r--r-- 1 root root   15667 26 mai   08:59 pays2011.txt.1
-rw-r--r-- 1 root root     622 26 mai   08:59 piwik1
-rw-r--r-- 1 root root     622 26 mai   08:59 piwik2
-rw-r--r-- 1 root root     116 26 mai   08:59 result
-rw------- 1 root root     281 26 mai   08:59 ronan
-rw-r--r-- 1 root root     281 26 mai   08:59 ronan2
-rw-r--r-- 1 root root     281 26 mai   08:59 ronan3
-rw-r--r-- 1 root root  195732 26 mai   08:59 search
-rw-r--r-- 1 root root     387 26 mai   08:59 test
$

Etiquettes: 

Copie physique, transformations : dd

La commande dd réalise une copie physique d'un flux d'entrée vers un flux de sortie. Chaque octet souhaité du flux d'entrée est copié vers le flux de sortie, indépendamment de ce que représente la donnée.

Syntaxe

dd [if=fichier_entree] [of=fichier_sortie] [ibs=nboctets] [obs=nboctets] [bs=nboctets] [skip=nbblocs] [seek=nbblocs] [count=nbblocs]
Principales options :
if=fichier_entree : Fichier à traiter. Si aucun fichier n'est spécifié, l'entrée standard est traité.
of=fichier_sortie : Fichier de sortie. Si aucun fichier n'est spécifié, la sortie standard est utilisée.
ibs=nboctets : Traiter les données en entrée par blocs de nboctets octets (défaut : 512 octets).
obs=nboctets : Ecrire les données en sortie par blocs de nboctets octets (défaut : 512 octets).
bs=nboctets : Traiter les données d'entrée et de sortie par bloc de nboctets.
skip=nbblocs : Sauter nbblocs en entrée avant de commencer le traitement.
seek=nbblocs : En sortie, écrire à partir du bloc nbblocs.
count=nbblocs : Ecrire uniquement nbblocs de l'entrée vers la sortie.

Exemples

Copie du disque /dev/sda sur le disque /dev/sdb par blocs de 8ko

$ fdisk -l
 
Disque /dev/sda: 8589 Mo, 8589934592 octets
255 têtes, 63 secteurs/piste, 1044 cylindres
Unités = cylindres de 16065 * 512 = 8225280 octets
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Identifiant de disque : 0x00040afc
 
Périphérique Amorce  Début        Fin      Blocs     Id  Système
/dev/sda1   *           1         994     7977984   83  Linux
La partition 1 ne se termine pas sur une frontière de cylindre.
/dev/sda2             994        1045      407553    5  Etendue
/dev/sda5             994        1045      407552   82  Linux swap / Solaris
 
Disque /dev/sdb: 8589 Mo, 8589934592 octets
255 têtes, 63 secteurs/piste, 1044 cylindres
Unités = cylindres de 16065 * 512 = 8225280 octets
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Identifiant de disque : 0x00000000
 
Le disque /dev/sdb ne contient pas une table de partition valide

$ dd if=/dev/sda of=/dev/sdb bs=8k
1048576+0 enregistrements lus
1048576+0 enregistrements écrits
8589934592 octets (8,6 GB) copiés, 859,097 s, 10,0 MB/s
$

$ fdisk -l
 
Disque /dev/sda: 8589 Mo, 8589934592 octets
255 têtes, 63 secteurs/piste, 1044 cylindres
Unités = cylindres de 16065 * 512 = 8225280 octets
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Identifiant de disque : 0x00040afc
 
Périphérique Amorce  Début        Fin      Blocs     Id  Système
/dev/sda1   *           1         994     7977984   83  Linux
La partition 1 ne se termine pas sur une frontière de cylindre.
/dev/sda2             994        1045      407553    5  Etendue
/dev/sda5             994        1045      407552   82  Linux swap / Solaris
 
Disque /dev/sdb: 8589 Mo, 8589934592 octets
255 têtes, 63 secteurs/piste, 1044 cylindres
Unités = cylindres de 16065 * 512 = 8225280 octets
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Identifiant de disque : 0x00040afc
 
Périphérique Amorce  Début        Fin      Blocs     Id  Système
/dev/sdb1   *           1         994     7977984   83  Linux
La partition 1 ne se termine pas sur une frontière de cylindre.
/dev/sdb2             994        1045      407553    5  Etendue
/dev/sdb5             994        1045      407552   82  Linux swap / Solaris

Idem mais sur un disque sur une machine distante

$ dd if=/dev/sda | ssh 192.168.1.19 dd of=/dev/sdb bs=8k

Supprimer les 5 premiers octets du fichier fic1

$ cat fic1
ceci est un test
$ od -c fic1
0000000   c   e   c   i       e   s   t       u   n       t   e   s   t
0000020  \n
0000021
$ dd bs=1 skip=5 if=fic1 of=fic11
12+0 enregistrements lus
12+0 enregistrements écrits
12 octets (12 B) copiés, 0,00010661 s, 113 kB/s
$ cat fic11
est un test
$ od -c fic11
0000000   e   s   t       u   n       t   e   s   t  \n
0000014
$

Retirer le dernier octet d'un fichier de 6 903 921 octets. La taille du bloc est fixée à 6 903 920 octets. Le nombre de blocs traités est de 1

$ ls -l mon_fichier4
-rw-r--r-- 1 root root 6903921 25 mai   16:36 mon_fichier4
$ dd if=mon_fichier4 of=mon_fichier41 bs=6903920 count=1
1+0 enregistrements lus
1+0 enregistrements écrits
6903920 octets (6,9 MB) copiés, 0,183292 s, 37,7 MB/s
$ ls -l mon_fichier41
-rw-r--r-- 1 root root 6903920 26 mai   11:21 mon_fichier41
$

Etiquettes: 

Changement d'encodage : iconv

La commande iconv permet de transformer l'encodage d'un flux de données, typiquement de l'encodage UTF8 vers ISO8859-15 et inversement.

Syntaxe

iconv options [fichiers_a_traiter]
Principales options :
-f encodage_entree : Encodage du fichier d'entrée
-t encodage_sortie : Encodage du fichier de sortie

Exemples

Conversion du fichier fic1 (UTF-8) vers fic2 (ISO8859-15)

$ cat fic1
systèmes
$ od -c fic1
0000000   s   y   s   t 303 250   m   e   s  \n
0000012
$ file fic1
fic1: UTF-8 Unicode text
$ iconv -f UTF8 -t ISO885915 fic1 > fic2
$ cat fic2
syst▒mes
$ od -c fic2
0000000   s   y   s   t 350   m   e   s  \n
0000011
$ file fic2
fic2: ISO-8859 text
$

Afficher la liste complète de tous les jeux des codes de caractères connus.

$ iconv -l
La liste suivante contient tous les jeux des codes de caractères connus. Ceci
ne signifie pas nécessairement que toutes les combinaisons de ces noms peuvent
être utilisées dans les paramètres « FROM » et « TO » des commandes. Un jeu de codes de
caractères peut être affiché avec différents noms (aliases).
 
 437, 500, 500V1, 850, 851, 852, 855, 856, 857, 860, 861, 862, 863, 864, 865,
  866, 866NAV, 869, 874, 904, 1026, 1046, 1047, 8859_1, 8859_2, 8859_3, 8859_4,
  8859_5, 8859_6, 8859_7, 8859_8, 8859_9, 10646-1:1993, 10646-1:1993/UCS4,
  ANSI_X3.4-1968, ANSI_X3.4-1986, ANSI_X3.4, ANSI_X3.110-1983, ANSI_X3.110,
  ARABIC, ARABIC7, ARMSCII-8, ASCII, ASMO-708, ASMO_449, BALTIC, BIG-5,
  BIG-FIVE, BIG5-HKSCS, BIG5, BIG5HKSCS, BIGFIVE, BRF, BS_4730, CA, CN-BIG5,
  CN-GB, CN, CP-AR, CP-GR, CP-HU, CP037, CP038, CP273, CP274, CP275, CP278,
  CP280, CP281, CP282, CP284, CP285, CP290, CP297, CP367, CP420, CP423, CP424,
  CP437, CP500, CP737, CP775, CP803, CP813, CP819, CP850, CP851, CP852, CP855,
  CP856, CP857, CP860, CP861, CP862, CP863, CP864, CP865, CP866, CP866NAV,
  CP868, CP869, CP870, CP871, CP874, CP875, CP880, CP891, CP901, CP902, CP903,
  CP904, CP905, CP912, CP915, CP916, CP918, CP920, CP921, CP922, CP930, CP932,
  CP933, CP935, CP936, CP937, CP939, CP949, CP950, CP1004, CP1008, CP1025,
  CP1026, CP1046, CP1047, CP1070, CP1079, CP1081, CP1084, CP1089, CP1097,
  CP1112, CP1122, CP1123, CP1124, CP1125, CP1129, CP1130, CP1132, CP1133,
  CP1137, CP1140, CP1141, CP1142, CP1143, CP1144, CP1145, CP1146, CP1147,
  CP1148, CP1149, CP1153, CP1154, CP1155, CP1156, CP1157, CP1158, CP1160,
  CP1161, CP1162, CP1163, CP1164, CP1166, CP1167, CP1250, CP1251, CP1252,
  CP1253, CP1254, CP1255, CP1256, CP1257, CP1258, CP1282, CP1361, CP1364,
  CP1371, CP1388, CP1390, CP1399, CP4517, CP4899, CP4909, CP4971, CP5347,
  CP9030, CP9066, CP9448, CP10007, CP12712, CP16804, CPIBM861, CSA7-1, CSA7-2,
  CSASCII, CSA_T500-1983, CSA_T500, CSA_Z243.4-1985-1, CSA_Z243.4-1985-2,
  CSA_Z243.419851, CSA_Z243.419852, CSDECMCS, CSEBCDICATDE, CSEBCDICATDEA,
  CSEBCDICCAFR, CSEBCDICDKNO, CSEBCDICDKNOA, CSEBCDICES, CSEBCDICESA,
  CSEBCDICESS, CSEBCDICFISE, CSEBCDICFISEA, CSEBCDICFR, CSEBCDICIT, CSEBCDICPT,
  CSEBCDICUK, CSEBCDICUS, CSEUCKR, CSEUCPKDFMTJAPANESE, CSGB2312, CSHPROMAN8,
  CSIBM037, CSIBM038, CSIBM273, CSIBM274, CSIBM275, CSIBM277, CSIBM278,
  CSIBM280, CSIBM281, CSIBM284, CSIBM285, CSIBM290, CSIBM297, CSIBM420,
  CSIBM423, CSIBM424, CSIBM500, CSIBM803, CSIBM851, CSIBM855, CSIBM856,
  CSIBM857, CSIBM860, CSIBM863, CSIBM864, CSIBM865, CSIBM866, CSIBM868,
  CSIBM869, CSIBM870, CSIBM871, CSIBM880, CSIBM891, CSIBM901, CSIBM902,
  CSIBM903, CSIBM904, CSIBM905, CSIBM918, CSIBM921, CSIBM922, CSIBM930,
  CSIBM932, CSIBM933, CSIBM935, CSIBM937, CSIBM939, CSIBM943, CSIBM1008,
  CSIBM1025, CSIBM1026, CSIBM1097, CSIBM1112, CSIBM1122, CSIBM1123, CSIBM1124,
  CSIBM1129, CSIBM1130, CSIBM1132, CSIBM1133, CSIBM1137, CSIBM1140, CSIBM1141,
  CSIBM1142, CSIBM1143, CSIBM1144, CSIBM1145, CSIBM1146, CSIBM1147, CSIBM1148,
  CSIBM1149, CSIBM1153, CSIBM1154, CSIBM1155, CSIBM1156, CSIBM1157, CSIBM1158,
  CSIBM1160, CSIBM1161, CSIBM1163, CSIBM1164, CSIBM1166, CSIBM1167, CSIBM1364,
  CSIBM1371, CSIBM1388, CSIBM1390, CSIBM1399, CSIBM4517, CSIBM4899, CSIBM4909,
  CSIBM4971, CSIBM5347, CSIBM9030, CSIBM9066, CSIBM9448, CSIBM12712,
  CSIBM16804, CSIBM11621162, CSISO4UNITEDKINGDOM, CSISO10SWEDISH,
  CSISO11SWEDISHFORNAMES, CSISO14JISC6220RO, CSISO15ITALIAN, CSISO16PORTUGESE,
  CSISO17SPANISH, CSISO18GREEK7OLD, CSISO19LATINGREEK, CSISO21GERMAN,
  CSISO25FRENCH, CSISO27LATINGREEK1, CSISO49INIS, CSISO50INIS8,
  CSISO51INISCYRILLIC, CSISO58GB1988, CSISO60DANISHNORWEGIAN,
  CSISO60NORWEGIAN1, CSISO61NORWEGIAN2, CSISO69FRENCH, CSISO84PORTUGUESE2,
  CSISO85SPANISH2, CSISO86HUNGARIAN, CSISO88GREEK7, CSISO89ASMO449, CSISO90,
  CSISO92JISC62991984B, CSISO99NAPLPS, CSISO103T618BIT, CSISO111ECMACYRILLIC,
  CSISO121CANADIAN1, CSISO122CANADIAN2, CSISO139CSN369103, CSISO141JUSIB1002,
  CSISO143IECP271, CSISO150, CSISO150GREEKCCITT, CSISO151CUBA,
  CSISO153GOST1976874, CSISO646DANISH, CSISO2022CN, CSISO2022JP, CSISO2022JP2,
  CSISO2022KR, CSISO2033, CSISO5427CYRILLIC, CSISO5427CYRILLIC1981,
  CSISO5428GREEK, CSISO10367BOX, CSISOLATIN1, CSISOLATIN2, CSISOLATIN3,
  CSISOLATIN4, CSISOLATIN5, CSISOLATIN6, CSISOLATINARABIC, CSISOLATINCYRILLIC,
  CSISOLATINGREEK, CSISOLATINHEBREW, CSKOI8R, CSKSC5636, CSMACINTOSH,
  CSNATSDANO, CSNATSSEFI, CSN_369103, CSPC8CODEPAGE437, CSPC775BALTIC,
  CSPC850MULTILINGUAL, CSPC862LATINHEBREW, CSPCP852, CSSHIFTJIS, CSUCS4,
  CSUNICODE, CSWINDOWS31J, CUBA, CWI-2, CWI, CYRILLIC, DE, DEC-MCS, DEC,
  DECMCS, DIN_66003, DK, DS2089, DS_2089, E13B, EBCDIC-AT-DE-A, EBCDIC-AT-DE,
  EBCDIC-BE, EBCDIC-BR, EBCDIC-CA-FR, EBCDIC-CP-AR1, EBCDIC-CP-AR2,
  EBCDIC-CP-BE, EBCDIC-CP-CA, EBCDIC-CP-CH, EBCDIC-CP-DK, EBCDIC-CP-ES,
  EBCDIC-CP-FI, EBCDIC-CP-FR, EBCDIC-CP-GB, EBCDIC-CP-GR, EBCDIC-CP-HE,
  EBCDIC-CP-IS, EBCDIC-CP-IT, EBCDIC-CP-NL, EBCDIC-CP-NO, EBCDIC-CP-ROECE,
  EBCDIC-CP-SE, EBCDIC-CP-TR, EBCDIC-CP-US, EBCDIC-CP-WT, EBCDIC-CP-YU,
  EBCDIC-CYRILLIC, EBCDIC-DK-NO-A, EBCDIC-DK-NO, EBCDIC-ES-A, EBCDIC-ES-S,
  EBCDIC-ES, EBCDIC-FI-SE-A, EBCDIC-FI-SE, EBCDIC-FR, EBCDIC-GREEK, EBCDIC-INT,
  EBCDIC-INT1, EBCDIC-IS-FRISS, EBCDIC-IT, EBCDIC-JP-E, EBCDIC-JP-KANA,
  EBCDIC-PT, EBCDIC-UK, EBCDIC-US, EBCDICATDE, EBCDICATDEA, EBCDICCAFR,
  EBCDICDKNO, EBCDICDKNOA, EBCDICES, EBCDICESA, EBCDICESS, EBCDICFISE,
  EBCDICFISEA, EBCDICFR, EBCDICISFRISS, EBCDICIT, EBCDICPT, EBCDICUK, EBCDICUS,
  ECMA-114, ECMA-118, ECMA-128, ECMA-CYRILLIC, ECMACYRILLIC, ELOT_928, ES, ES2,
  EUC-CN, EUC-JISX0213, EUC-JP-MS, EUC-JP, EUC-KR, EUC-TW, EUCCN, EUCJP-MS,
  EUCJP-OPEN, EUCJP-WIN, EUCJP, EUCKR, EUCTW, FI, FR, GB, GB2312, GB13000,
  GB18030, GBK, GB_1988-80, GB_198880, GEORGIAN-ACADEMY, GEORGIAN-PS,
  GOST_19768-74, GOST_19768, GOST_1976874, GREEK-CCITT, GREEK, GREEK7-OLD,
  GREEK7, GREEK7OLD, GREEK8, GREEKCCITT, HEBREW, HP-GREEK8, HP-ROMAN8,
  HP-ROMAN9, HP-THAI8, HP-TURKISH8, HPGREEK8, HPROMAN8, HPROMAN9, HPTHAI8,
  HPTURKISH8, HU, IBM-803, IBM-856, IBM-901, IBM-902, IBM-921, IBM-922,
  IBM-930, IBM-932, IBM-933, IBM-935, IBM-937, IBM-939, IBM-943, IBM-1008,
  IBM-1025, IBM-1046, IBM-1047, IBM-1097, IBM-1112, IBM-1122, IBM-1123,
  IBM-1124, IBM-1129, IBM-1130, IBM-1132, IBM-1133, IBM-1137, IBM-1140,
  IBM-1141, IBM-1142, IBM-1143, IBM-1144, IBM-1145, IBM-1146, IBM-1147,
  IBM-1148, IBM-1149, IBM-1153, IBM-1154, IBM-1155, IBM-1156, IBM-1157,
  IBM-1158, IBM-1160, IBM-1161, IBM-1162, IBM-1163, IBM-1164, IBM-1166,
  IBM-1167, IBM-1364, IBM-1371, IBM-1388, IBM-1390, IBM-1399, IBM-4517,
  IBM-4899, IBM-4909, IBM-4971, IBM-5347, IBM-9030, IBM-9066, IBM-9448,
  IBM-12712, IBM-16804, IBM037, IBM038, IBM256, IBM273, IBM274, IBM275, IBM277,
  IBM278, IBM280, IBM281, IBM284, IBM285, IBM290, IBM297, IBM367, IBM420,
  IBM423, IBM424, IBM437, IBM500, IBM775, IBM803, IBM813, IBM819, IBM848,
  IBM850, IBM851, IBM852, IBM855, IBM856, IBM857, IBM860, IBM861, IBM862,
  IBM863, IBM864, IBM865, IBM866, IBM866NAV, IBM868, IBM869, IBM870, IBM871,
  IBM874, IBM875, IBM880, IBM891, IBM901, IBM902, IBM903, IBM904, IBM905,
  IBM912, IBM915, IBM916, IBM918, IBM920, IBM921, IBM922, IBM930, IBM932,
  IBM933, IBM935, IBM937, IBM939, IBM943, IBM1004, IBM1008, IBM1025, IBM1026,
  IBM1046, IBM1047, IBM1089, IBM1097, IBM1112, IBM1122, IBM1123, IBM1124,
  IBM1129, IBM1130, IBM1132, IBM1133, IBM1137, IBM1140, IBM1141, IBM1142,
  IBM1143, IBM1144, IBM1145, IBM1146, IBM1147, IBM1148, IBM1149, IBM1153,
  IBM1154, IBM1155, IBM1156, IBM1157, IBM1158, IBM1160, IBM1161, IBM1162,
  IBM1163, IBM1164, IBM1166, IBM1167, IBM1364, IBM1371, IBM1388, IBM1390,
  IBM1399, IBM4517, IBM4899, IBM4909, IBM4971, IBM5347, IBM9030, IBM9066,
  IBM9448, IBM12712, IBM16804, IEC_P27-1, IEC_P271, INIS-8, INIS-CYRILLIC,
  INIS, INIS8, INISCYRILLIC, ISIRI-3342, ISIRI3342, ISO-2022-CN-EXT,
  ISO-2022-CN, ISO-2022-JP-2, ISO-2022-JP-3, ISO-2022-JP, ISO-2022-KR,
  ISO-8859-1, ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6,
  ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-8859-9E, ISO-8859-10, ISO-8859-11,
  ISO-8859-13, ISO-8859-14, ISO-8859-15, ISO-8859-16, ISO-10646,
  ISO-10646/UCS2, ISO-10646/UCS4, ISO-10646/UTF-8, ISO-10646/UTF8, ISO-CELTIC,
  ISO-IR-4, ISO-IR-6, ISO-IR-8-1, ISO-IR-9-1, ISO-IR-10, ISO-IR-11, ISO-IR-14,
  ISO-IR-15, ISO-IR-16, ISO-IR-17, ISO-IR-18, ISO-IR-19, ISO-IR-21, ISO-IR-25,
  ISO-IR-27, ISO-IR-37, ISO-IR-49, ISO-IR-50, ISO-IR-51, ISO-IR-54, ISO-IR-55,
  ISO-IR-57, ISO-IR-60, ISO-IR-61, ISO-IR-69, ISO-IR-84, ISO-IR-85, ISO-IR-86,
  ISO-IR-88, ISO-IR-89, ISO-IR-90, ISO-IR-92, ISO-IR-98, ISO-IR-99, ISO-IR-100,
  ISO-IR-101, ISO-IR-103, ISO-IR-109, ISO-IR-110, ISO-IR-111, ISO-IR-121,
  ISO-IR-122, ISO-IR-126, ISO-IR-127, ISO-IR-138, ISO-IR-139, ISO-IR-141,
  ISO-IR-143, ISO-IR-144, ISO-IR-148, ISO-IR-150, ISO-IR-151, ISO-IR-153,
  ISO-IR-155, ISO-IR-156, ISO-IR-157, ISO-IR-166, ISO-IR-179, ISO-IR-193,
  ISO-IR-197, ISO-IR-199, ISO-IR-203, ISO-IR-209, ISO-IR-226, ISO/TR_11548-1,
  ISO646-CA, ISO646-CA2, ISO646-CN, ISO646-CU, ISO646-DE, ISO646-DK, ISO646-ES,
  ISO646-ES2, ISO646-FI, ISO646-FR, ISO646-FR1, ISO646-GB, ISO646-HU,
  ISO646-IT, ISO646-JP-OCR-B, ISO646-JP, ISO646-KR, ISO646-NO, ISO646-NO2,
  ISO646-PT, ISO646-PT2, ISO646-SE, ISO646-SE2, ISO646-US, ISO646-YU,
  ISO2022CN, ISO2022CNEXT, ISO2022JP, ISO2022JP2, ISO2022KR, ISO6937,
  ISO8859-1, ISO8859-2, ISO8859-3, ISO8859-4, ISO8859-5, ISO8859-6, ISO8859-7,
  ISO8859-8, ISO8859-9, ISO8859-9E, ISO8859-10, ISO8859-11, ISO8859-13,
  ISO8859-14, ISO8859-15, ISO8859-16, ISO11548-1, ISO88591, ISO88592, ISO88593,
  ISO88594, ISO88595, ISO88596, ISO88597, ISO88598, ISO88599, ISO88599E,
  ISO885910, ISO885911, ISO885913, ISO885914, ISO885915, ISO885916,
  ISO_646.IRV:1991, ISO_2033-1983, ISO_2033, ISO_5427-EXT, ISO_5427,
  ISO_5427:1981, ISO_5427EXT, ISO_5428, ISO_5428:1980, ISO_6937-2,
  ISO_6937-2:1983, ISO_6937, ISO_6937:1992, ISO_8859-1, ISO_8859-1:1987,
  ISO_8859-2, ISO_8859-2:1987, ISO_8859-3, ISO_8859-3:1988, ISO_8859-4,
  ISO_8859-4:1988, ISO_8859-5, ISO_8859-5:1988, ISO_8859-6, ISO_8859-6:1987,
  ISO_8859-7, ISO_8859-7:1987, ISO_8859-7:2003, ISO_8859-8, ISO_8859-8:1988,
  ISO_8859-9, ISO_8859-9:1989, ISO_8859-9E, ISO_8859-10, ISO_8859-10:1992,
  ISO_8859-14, ISO_8859-14:1998, ISO_8859-15, ISO_8859-15:1998, ISO_8859-16,
  ISO_8859-16:2001, ISO_9036, ISO_10367-BOX, ISO_10367BOX, ISO_11548-1,
  ISO_69372, IT, JIS_C6220-1969-RO, JIS_C6229-1984-B, JIS_C62201969RO,
  JIS_C62291984B, JOHAB, JP-OCR-B, JP, JS, JUS_I.B1.002, KOI-7, KOI-8, KOI8-R,
  KOI8-RU, KOI8-T, KOI8-U, KOI8, KOI8R, KOI8U, KSC5636, L1, L2, L3, L4, L5, L6,
  L7, L8, L10, LATIN-9, LATIN-GREEK-1, LATIN-GREEK, LATIN1, LATIN2, LATIN3,
  LATIN4, LATIN5, LATIN6, LATIN7, LATIN8, LATIN9, LATIN10, LATINGREEK,
  LATINGREEK1, MAC-CENTRALEUROPE, MAC-CYRILLIC, MAC-IS, MAC-SAMI, MAC-UK, MAC,
  MACCYRILLIC, MACINTOSH, MACIS, MACUK, MACUKRAINIAN, MIK, MS-ANSI, MS-ARAB,
  MS-CYRL, MS-EE, MS-GREEK, MS-HEBR, MS-MAC-CYRILLIC, MS-TURK, MS932, MS936,
  MSCP949, MSCP1361, MSMACCYRILLIC, MSZ_7795.3, MS_KANJI, NAPLPS, NATS-DANO,
  NATS-SEFI, NATSDANO, NATSSEFI, NC_NC0010, NC_NC00-10, NC_NC00-10:81,
  NF_Z_62-010, NF_Z_62-010_(1973), NF_Z_62-010_1973, NF_Z_62010,
  NF_Z_62010_1973, NO, NO2, NS_4551-1, NS_4551-2, NS_45511, NS_45512,
  OS2LATIN1, OSF00010001, OSF00010002, OSF00010003, OSF00010004, OSF00010005,
  OSF00010006, OSF00010007, OSF00010008, OSF00010009, OSF0001000A, OSF00010020,
  OSF00010100, OSF00010101, OSF00010102, OSF00010104, OSF00010105, OSF00010106,
  OSF00030010, OSF0004000A, OSF0005000A, OSF05010001, OSF100201A4, OSF100201A8,
  OSF100201B5, OSF100201F4, OSF100203B5, OSF1002011C, OSF1002011D, OSF1002035D,
  OSF1002035E, OSF1002035F, OSF1002036B, OSF1002037B, OSF10010001, OSF10010004,
  OSF10010006, OSF10020025, OSF10020111, OSF10020115, OSF10020116, OSF10020118,
  OSF10020122, OSF10020129, OSF10020352, OSF10020354, OSF10020357, OSF10020359,
  OSF10020360, OSF10020364, OSF10020365, OSF10020366, OSF10020367, OSF10020370,
  OSF10020387, OSF10020388, OSF10020396, OSF10020402, OSF10020417, PT, PT2,
  PT154, R8, R9, RK1048, ROMAN8, ROMAN9, RUSCII, SE, SE2, SEN_850200_B,
  SEN_850200_C, SHIFT-JIS, SHIFT_JIS, SHIFT_JISX0213, SJIS-OPEN, SJIS-WIN,
  SJIS, SS636127, STRK1048-2002, ST_SEV_358-88, T.61-8BIT, T.61, T.618BIT,
  TCVN-5712, TCVN, TCVN5712-1, TCVN5712-1:1993, THAI8, TIS-620, TIS620-0,
  TIS620.2529-1, TIS620.2533-0, TIS620, TS-5881, TSCII, TURKISH8, UCS-2,
  UCS-2BE, UCS-2LE, UCS-4, UCS-4BE, UCS-4LE, UCS2, UCS4, UHC, UJIS, UK,
  UNICODE, UNICODEBIG, UNICODELITTLE, US-ASCII, US, UTF-7, UTF-8, UTF-16,
  UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, UTF-32LE, UTF7, UTF8, UTF16, UTF16BE,
  UTF16LE, UTF32, UTF32BE, UTF32LE, VISCII, WCHAR_T, WIN-SAMI-2, WINBALTRIM,
  WINDOWS-31J, WINDOWS-874, WINDOWS-936, WINDOWS-1250, WINDOWS-1251,
  WINDOWS-1252, WINDOWS-1253, WINDOWS-1254, WINDOWS-1255, WINDOWS-1256,
  WINDOWS-1257, WINDOWS-1258, WINSAMI2, WS2, YU
$

Etiquettes: