Développement sous Unix | ||
---|---|---|
Prev |
Unix est une marque déposée par la société AT&T d'un systeme d'exploitation d'ordinateur.
Note: A définir.
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.
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.
mkdir: création |
rmdir: destruction |
pwd: répertoire courant |
cd: changement du répertoire courant |
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 |
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
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 |
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:
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.
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 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 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 |
% 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 |
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
Flots | Symbole |
---|---|
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 |
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 |
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.
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 |
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 |
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
Symbole | Signification |
---|---|
. | 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 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 |
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, à 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.
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.