Unix

Introduction

Unix est une marque déposée par la société AT&T d'un systeme d'exploitation d'ordinateur.

Note: A définir.

L'objectif d'un systeme d'exploitation est de faciliter l'interface entre l'utilisateur et le matériel. Par des abstractions, un OS (Operating System, abbrévation de système d'exploitation) masque à l'utilisateur les ressources matérielles et les lui présente sous une forme unifiée. Ceci est valable pour le/les processeurs, la mémoire, les périphériques, les disques, etc. Ces abstractions se font par le biais d'une API qui standardise les interactions entre l'utilisateur et le matériel.

Unix est un système multi-utilisateur, multi-taches, préemptif (termes a définir). Il repose donc à ce titre sur des principes de partage des ressources entre les différents utilisateurs.

Gestion de fichiers

On distingue plusieurs types de fichiers, parmi lesquels les deux plus importants : les fichiers réguliers et les répertoires. Le séparateur de noms de répertoires est le symbole /. Peu ou pas de contraintes existent sur les noms de fichiers. Leur longueur est généralement limitée à 512 ou 1024 caractères selon les systèmes, ce qui laisse le temps de voir venir.

Gestion des répertoires

mkdir: création
rmdir: destruction
pwd: répertoire courant
cd: changement du répertoire courant

Attributs des répertoires et des fichiers

ls

Un utilisateur est identifié sous Unix pour un nom et un numéro d'utilisateur, ainsi que par son appartenance à un ou plusieurs groupes. Les fichiers Unix disposent donc de droits d'acces à ces deux niveau. Les droits se décomposent classiquement en droits de lecture (r), écriture (w) et exécution (x) et sont définis respectivement pour l'utilisateur, pour les membres du groupe et pour les autres utilisateurs (chaine rwxr-xr-x dans le listing).

Les droits sur répertoires ont des significations particulieres, indiquant la possibilite de lire le contenu d'un répertoire (r), de créer de nouveaux fichiers dans un répertoire (w), et la possibilité de faire un cd dans ce répertoire (x).

On change les droits des fichiers avec la commande chmod qui a la syntaxe suivante: chmod [ugoa][+-=][rwx] files....

Example 4. Les droits des fichiers

% cd /
% ls -l
total 108
drwxr-xr-x   2 root     root         4096 Oct 24 16:02 bin/
drwxr-xr-x   2 root     root         4096 Oct 24 17:02 boot/
drwxr-xr-x   6 root     root        36864 Oct 26 15:26 dev/
drwxr-xr-x  41 root     root         4096 Oct 26 22:30 etc/
-rw-r--r--   1 root     root            2 Sep 24 23:53 fonts.dir
drwxr-xr-x   6 root     root         4096 Feb  6  1996 home/
drwxr-xr-x   4 root     root         4096 Oct 24 16:00 lib/
drwxr-xr-x   2 root     root        16384 Sep 24 23:27 lost+found/
drwxr-xr-x   2 root     root         4096 Sep 20 17:13 misc/
drwxr-xr-x   4 root     root         4096 Oct  9  1998 mnt/
drwxr-xr-x   2 root     root         4096 Aug 23 18:03 opt/
dr-xr-xr-x  70 root     root            0 Oct 26 15:25 proc/
drwxr-x---   9 root     root         4096 Oct 27 11:06 root/
drwxr-xr-x   3 root     root         4096 Oct 24 17:17 sbin/
drwxrwxrwt  13 root     root         4096 Oct 27 14:45 tmp/
drwxr-xr-x  21 root     root         4096 Aug  2  1998 usr/
drwxr-xr-x  21 root     root         4096 Oct 24 16:02 var/
      

Les manipulations possibles de fichiers sont:

mv: renommer, déplacer
cp: copier
rm: effacer
cat ou more: visualiser

Shell

L'interaction avec l'utilisateur passe par un interpréteur de commandes (aussi appelé shell). Il s'agit d'un processus Unix, un programme, qui interprete séquentiellement chaque commande entrée au clavier. Chaque commande est un processus Unix. (

Note: parler des filiations entre les processus

). On termine l'exécution d'un shell de commande par la commande exit.

Les processus lancés par le shell, peuvent fonctionner en mode synchrone (par défaut) ou asynchrone. Exemple:

Example 5. Exemples de processus en tâche de fond

% sleep 2; echo coucou; sleep 3; echo coucou
% sleep 2 & echo coucou & sleep 3 & echo coucou
     

La commande ps permet de lister tous les processus.

% ps axu
USER       PID %CPU %MEM   VSZ  RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.2  1104  464 ?        S    00:39   0:03 init
root         2  1.1  0.0     0    0 ?        SW   00:39  10:14 [kapmd]
root         3  0.0  0.0     0    0 ?        SW   00:39   0:00 [kswapd]
root         4  0.0  0.0     0    0 ?        SW   00:39   0:00 [kflushd]
bin        157  0.0  0.2  1200  396 ?        S    00:40   0:00 portmap
root       173  0.0  0.2  1100  496 ?        S    00:40   0:01 /usr/sbin/apmd -p
root       226  0.0  0.2  1160  524 ?        S    00:40   0:00 syslogd -m 0
root       237  0.0  0.4  1416  768 ?        S    00:40   0:00 klogd
daemon     253  0.0  0.2  1132  484 ?        S    00:40   0:00 /usr/sbin/atd
root       269  0.0  0.3  1308  600 ?        S    00:40   0:00 crond
bellet   28007  0.1  2.3  7372 4412 ?        S    09:48   0:31 gvim development.sgml
bellet   28013  0.0  2.0  7180 3976 ?        S    09:56   0:01 gvim corba.sgml
bellet   28418  0.0  0.7  2440 1520 ?        S    14:35   0:00 rxvt
bellet   28419  0.0  0.6  2252 1304 pts/3    S    14:35   0:00 -csh
bellet   28488  0.0  1.2  3608 2364 pts/3    S    14:40   0:00 gv development.ps
bellet   28497  0.0  1.6  5136 3104 pts/3    S    14:44   0:01 gs -dNOPLATFONTS -sDEVICE=x11alpha -dNOPAUSE -dQUIE
bellet   28559  0.0  0.7  2444 1520 pts/3    S    15:14   0:00 rxvt
bellet   28560  0.0  0.5  2112 1144 pts/4    S    15:14   0:00 -csh
bellet   28627  0.0  0.4  2548  912 pts/0    R    15:42   0:00 ps axu
    
% ps axgf
  PID TTY      STAT   TIME COMMAND
    1 ?        S      0:03 init
    2 ?        SW    10:14 [kapmd]
    3 ?        SW     0:00 [kswapd]
    4 ?        SW     0:00 [kflushd]
  157 ?        S      0:00 portmap
  173 ?        S      0:01 /usr/sbin/apmd -p 10 -w 5 -W -u
27981 ?        S      0:07 /usr/lib/netscape/netscape-communicator -irix-session-management corba-gnome-gnorba.htm
27996 ?        S      0:00  \_ (dns helper)
28007 ?        S      0:31 gvim development.sgml
28013 ?        S      0:01 gvim corba.sgml
28418 ?        S      0:00 rxvt
28419 pts/3    S      0:00  \_ -csh
28488 pts/3    S      0:00      \_ gv development.ps
28497 pts/3    S      0:01      |   \_ gs -dNOPLATFONTS -sDEVICE=x11alpha -dNOPAUSE -dQUIET -dSAFER -
28559 pts/3    S      0:00      \_ rxvt
28560 pts/4    S      0:00          \_ -csh
    

On termine un processus avec la commande kill. On peut envoyer à un processus plusieurs types de signaux (kill -l pour avoir la liste). Le plus connu est le signal 9 (KILL) pour tuer un processus lancé en tâche de fond. Les shells modernes permettent aussi d'utiliser des numéros de jobs locaux au shell, ce qui évite de faire la commande ps. Exemple:

% yes > /dev/null &
[1] 28646
% yes > /dev/null &
[2] 28647
% ps axg | grep yes
28646 pts/0    R      0:43 yes
28647 pts/0    R      0:14 yes
% kill -STOP %1
[1]  + Suspended (signal)            yes > /dev/null
% ps axg | grep yes
28646 pts/0    T      1:01 yes
28647 pts/0    R      0:48 yes
% kill -CONT %1
[1]    yes > /dev/null &
% kill -TERM %1
[1]    Terminated                    yes > /dev/null
71.700u 0.220s 2:44.82 43.6%    0+0k 0+0io 88pf+0w
% fg %2
yes > /dev/null
CTRL-Z

Suspended
% jobs
[2]  + Suspended                     yes > /dev/null
% bg %2
[2]    yes > /dev/null &
% jobs
[2]    Running                       yes > /dev/null
% kill -KILL %2
[2]    Killed                        yes > /dev/null
160.400u 0.400s 3:47.71 70.6%   0+0k 0+0io 88pf+0w
    

La commande top permet d'avoir la liste remise régulièrement à jour des processus sur la machine, classé par utilisation du temps CPU. Elle est particulièrement pour contrôler que des processus oubliés ne continuent pas à tourner inutilement sur la machine. Exemple:

  3:57pm  up 15:18,  4 users,  load average: 0.17, 0.54, 0.32
60 processes: 59 sleeping, 1 running, 0 zombie, 0 stopped
CPU states:  0.0% user,  0.9% system,  0.0% nice, 99.0% idle
Mem:  191208K av, 178356K used,  12852K free,      0K shrd,   2052K buff
Swap: 385452K av,      0K used, 385452K free                 38648K cached

  PID USER     PRI  NI  SIZE  RSS SHARE STAT  LIB %CPU %MEM   TIME COMMAND
28654 bellet    16   0  1064 1064   856 R       0  0.5  0.5   0:00 top
25525 bellet     3   0  1588 1588  1112 S       0  0.3  0.8   0:02 rxvt
    1 root       0   0   464  464   392 S       0  0.0  0.2   0:03 init
    2 root       0   0     0    0     0 SW      0  0.0  0.0  10:14 kapmd
    3 root       0   0     0    0     0 SW      0  0.0  0.0   0:00 kswapd
    4 root       0   0     0    0     0 SW      0  0.0  0.0   0:00 kflushd
  157 bin        0   0   396  396   316 S       0  0.0  0.2   0:00 portmap
  173 root       0   0   496  496   424 S       0  0.0  0.2   0:01 apmd
  226 root       0   0   524  524   424 S       0  0.0  0.2   0:00 syslogd
  237 root       0   0   768  768   392 S       0  0.0  0.4   0:00 klogd
  253 daemon     0   0   484  484   404 S       0  0.0  0.2   0:00 atd
  269 root       0   0   600  600   504 S       0  0.0  0.3   0:00 crond
  285 root       0   0   512  512   432 S       0  0.0  0.2   0:00 inetd
    

Utilisation avancée du C-Shell

Plusieurs shells (interpréteurs de commandes) existent sous Unix. Ils possedent chacun leur syntaxe et leurs built-ins spécifiques, mais ils se composent en deux grandes catégories:

Compatibles Bourne Shell

Les shells compatibles avec le Bourne Shell (/bin/sh): ksh, bash, zsh, etc. Ils se distinguent entre eux par leur facilité d'édition des commandes, par la gestion ou pas d'un historique, par leur license d'utilisation entre autres. Ils sont généralement dédiés à l'écriture de scripts système.

Compatibles C-Shell

Les shells de la famille des C-shells: csh, tcsh. Leur syntaxe est proche de celle du langage C, ce qui leur a donné ce nom.

L'utilisateur a la possibilité de changer son shell courant avec la commande chsh ou passwd. Pour qu'un shell soit autorisé, il doit être référencé dans le fichier /etc/shells pour des raisons de sécurité.

Les shells ne sont pas une simple boucle d'attente des commande de l'utilisateur. Ils sont des langages de commandes à part entiere, avec leurs variables, leurs structures de controle.

Les variables

Les variables ne sont pas typées.

% set X=123
% set Y=toto
% echo $X $Y
123 toto
      

Les variables peuvent être visibles uniquement dans le shell courant (set) ou bien dans tous les processus fils (setenv). setenv sert pour affecter les variables d'environnement, par exemple DISPLAY.

% set V1=1
% setenv V2 2
% csh
% echo $V1
V1: Undefined variable.
% echo $V2
2
% exit
% echo $V1 $V2
1 2
      

Les structures de controle

Les boucles, les itérations, les tests sont au rendez-vous:

% foreach i ( 1 2 3 4 )
? echo $i
? end
1
2
3
4

% foreach file ( * )
? echo $file
? end
fichier.c
fichier.o
Makefile

% if ( $i < 4 ) then
?    echo oui
? else
?    echo non
? fi
oui

% while ( $condition )
? instruction
? instruction
? end
      

Substitution de variables

% set variable = 123
% echo $variable
123
% set v2 = un nom avec un espace
% echo $v2
un
% set v2 = "variable vaut $variable"
% echo $v2
variable vaut 123
% set v2 = 'variable vaut $variable'
% echo $v2
variable vaut $variable
% set v2 = variables\ vaut\ $variable
% echo $v2
variable vaut 123
% set d  = "la date est `date`"
% echo $d
la date est Wed Oct 27 17:16:05 MET DST 1999
      

Les simples quotes interdisent l'évaluation du contenu à la différence des doubles quotes.

L'anti-slash \ interdit l'interprétation du caractère qui le suit.

% set v2 = xxxx\'yyyy
% echo $v2
% xxxx'yyyy
      

Il est possible de compléter des noms de fichiers.

% set v2 = fichier.*
% echo $v2
fichier.c fichier.o
      

Les flots standards d'entrée/sortie

Tout processus Unix lit les données en entrée sur un fichier stdin, écrit les données en sortie sur un fichier stdout, et écrit les messages d'erreur sur un troisième fichier stderr. Clavier et écran ne sont, pour le système Unix, que des fichiers particuliers, en lecture seule pour le premier et en écriture seule pour le deuxième. Ces trois flots sont représentés en csh par les symboles suivants:

Table 1. Les flots d'entrées/sorties

FlotsSymbole
stdin<
stdout>
stdout (append)>>
stdout + stderr>&
stdout + stderr (append)>>&

On connecte les flux d'entrée/sorties de deux processus avec un “pipe” Unix:

% cat development.sgml | wc
    571    3147   25034
% make |& more
      

On utilise un sous-shell pour séparer stdin et stdout:

% ( make > make.log ) >& make.err
      

Intérêt des sous-shells

L'utilisation des back-quotes permet de laner des commandes dans des sous-shells et de substituer la commande par son résultat dans le shell courant.

% foreach f (`cat file_list`)
? rm -f $file
? end
      

Substitutions des noms de fichiers

Il existe un certain nombre de regles de substitution, qui, même si une certaine ressemblance existe, ne s'apparentent pas à des expression régulières.

  • *: plusieurs caractères différents de ".".

  • ?: un seul caractère different de ".".

  • [abc]: une alternative de caractères.

  • {chaine1,chaine2}: une alternative de chaines de caractères.

Exemples:

  • *.*: tous les fichiers contenant un point.

  • .??*: tous les fichiers commencant par un point et avec au moins deux autres caractères. Cela permet de lister les fichiers cachés (commencant par .) et d'exclure . et .. (le répertoire courant et le répertoire parent respectivement).

  • ~/src/[12]/Makefile: permet de matcher les deux fichiers /home/bellet/src/1/Makefile et /home/bellet/src/1/Makefile. En effet, le symbole ~ est un raccourci pour la variable d'environnement HOME, indiquant le home-directory de l'utilisateur.

  • *.[hc]: permet de matcher tous les fichiers .c et .h du répertoire courant.

La galere des expressions numériques

Il faut utiliser la commande externe expr.

% set a = 3
% set b = `expr \( 1 + 3 \) \* $a`
% echo $b
12
      

Faire un compteur est possible:

% set a = 0
% while ( $a < 10 )
?   echo Traitement du fichier numéro $a
?   set a = `expr $a + 1`
? end
      

Les conditions

L'écriture des conditions est tres similaire à la syntaxe du langage C. Encore une chance, ce shell a été écrit pour ca!

% if ( $x == "chaine" ) ...
% if ( $x <= 10 && $y == 2 ) ...
% if ( $x != 1 || "$2" == `hostname` ) ...
% if ( -r fichier1 && -x fichier2 && -d fichier3 ) ...
      

Un autre moyen d'écrire des tests conditionnels consiste à utiliser une caractéristique des processus Unix, qui retournent un entier lorsqu'ils se terminent indiquant si la commande s'est bien exécutée ou pas. Si la commande s'est bien terminée, la variable d'environnement $status vaut 0, sinon tout autre valeur correspond à un code d'erreur. Voir les pages de manuel de chaque commande Unix pour connaitre la signification des codes d'erreur retournés. Cet entier est la valeur renvoyée par les programmes en C écrit scrupuleusement:

int main() {
        ... plein de choses;
        procedure_truc();
        ... encore plein d'autres choses;
        /* 
         * fin normale du programme
         */
        return 0;
}
void procedure_truc () {
        ...
        if (erreur) {
           fprintf (stderr,"erreur irrecuperable. bye.\n");
           /*
            * fin anormale
            */
           exit (1);
        }
}
     

Ainsi, les primitives de comparaison && et || utilisent la valeur de $status pour fonctionner. On peut écrire des conditions également et de façon très compactes ainsi:

% echo azerty | grep -q aze && echo found
found
% echo azerty | grep -q qwe && echo found
% echo azerty | grep -q qwe || echo not found
not found
      

Gestions de flot d'entrées/sortie et grep

La commande grep lit des données depuis un fichier ou depuis l'entrée standard, et affiche sur sa sortie standard les lignes contenant la chaine de caractères passée en paramètre sur la ligne de commande.

Les instructions grep et egrep permettent de faire des recherches d'expression régulières dans le flux stdin. Par exemple on peut effectuer une recherche de deux chaines de caractères dans un fichier en utilisant une expression régulière dans la commande egrep. On notera qu'il est nécessaire de mettre l'expression régulière entre quotes, afin que le | ne soit pas interprété comme le pipe Unix dans cet exemple:

% egrep 'NNTPSERVER|PATH' ~/.cshrc 
setenv  NNTPSERVER      demo2.univ-lyon1.fr
setenv  LD_LIBRARY_PATH ${HOME}/creatis/lib:/usr/local/lib
setenv  CLASSPATH       /usr/lib/netscape/java/classes/java40.jar
      

On peut faire un ET avec un pipe Unix cette fois-ci. Dans cet exemple, on va matcher par exemple la ligne qui contient les deux chaines passées a chacun des grep:

% grep define *.[hc] | grep MA_CONSTANTE
fichier.c:#define MA_CONSTANTE 10        /* Ma constante */
      

L'option -i est très utile pour ne pas différencier majuscules et minuscules dans la recherche.

A savoir sur les expressions régulières:

Table 2. Expression régulières

SymboleSignification
.un caractère quelconque
|une alternative
()une sous-expression régulière
* le caractère ou l'expression précédents se répètent n fois, avec n ≥ 0
+ le caractère ou l'expression précédents se répètent n fois, avec n > 0
^le début de la ligne
$la fin de la ligne

La commande find

La commande find permet de rechercher des fichiers selon certains critères. Sa syntaxe est : find [chemin] [exp1] [exp2] ... , où chemin est le répertoire de départ de la recherche. Les principales expressions utiles sont:

  • -name "filename": des substitutions sont possibles.

  • -print ou -ls: affiche les fichiers trouvés.

  • -type f ou -type d: matche les fichiers ou les répertoires.

  • -exec execute une commande sur chaque fichier matché.

Exemple d'utilisation, les trois dernières commandes sont équivalentes:

% find ~ -name "*.o" -print

% find -name core -exec rm -f {} \;
% /bin/rm -f `find -name core -print`
% find -name core -print | xargs rm -f
      

sed, stream editor

C'est une commande très complexe. Elle est intéressante pour substituer des expressions entre le stdin et le stdout. Exemple:

% sed '30,50s/nom1/nom2/g' < fichier.c > fichier.new
      

30,50 indique les numéros de lignes du flot d'entrée concernés par la substitution. g autorise plusieurs substitutions sur la même ligne. Un détail important dans l'écriture des expressions régulières est que la substitution concernera toujours la chaîne la plus longue possible.

L'intérêt de sed est qu'il est utilisable sous l'éditeur vi, en mode commande, accessible par Escape. Il peut aussi servir d'excellent outils de filtre.

awk

awk, à la différence de sed est un véritable langage de traitement de flot, et pas seulement un filtre d'expressions régulières. Quelques exemples d'utilisation:

Pour récupérer la troisième colonne d'un tableau de résultats:

% cat fichier | awk '{print $3}'
      

print peut etre remplacé par un printf pour une meilleure mise en forme, avec les mêmes arguments qu'en langage C.

Pour faire la somme des valeurs d'une colonne:

% awk 'BEGIN{n=0}{n+=$3}END{print n}' < fichier
      

Il existe une abondante documentation sur le sujet.

Le séparateur de champs est l'espace par défaut. Il est paramétrable.

Quelques fonctions largement utilisées dans les shells

basename retourne le nom d'un fichier en lui otant les répertoire qui précedent son nom et éventuellement l'extension de son nom de fichier.

% basename /a/b/c/d.txt .txt
d
      

Une application peut etre de changer toutes les extensions des fichiers d'un repertoire:

foreach file ( *.c )
?  mv $file `basefile $file .c`.old
?  end
      

On peut faire la meme chose avec un sed.