Passer de cron à launchd

Il existe des tas de bonnes raisons d'utiliser cron plutôt que launchd pour lancer des tâches périodiques sur un Mac OS X. La première étant l'écrasante complexité de launchd face à cron. Néanmoins, Apple fait presque tout pour nous décourager d'utiliser cron, donc il est peut être temps de jeter un œil à launchd. J'ai déjà montré comment ce dernier permet de créer des tunnels ssh à la demande pour la connexion d'applications. Voyons maintenant comment il peut aussi être utilisé, dans une certaine mesure, pour s'acquitter de tâches périodiques.

Vous êtes peut être arrivé sur cette page à cause d'un vilain message dans vos fichiers de log système :

...cron...Could not setup Mach task special port 9: (os/kern) no access

Si vous trouvez ce message dans votre fichier /var/log/system.log, c'est que vous utilisez, peut être à votre insu, des crontabs. Les crontabs sont des fichiers de configuration pour cron. Le système râle pour vous faire comprendre à demi-mot qu'il serait temps d'utiliser launchd, et de laisser cron à vos ancêtres.

Généralement, une crontab se présente sous cette forme (sortie de la commande crontab -l) :

# Collect system information every minute
* * * * *       /Users/patpro/bin/cpu_load.sh >/dev/null
# Status graphs generation
*/15 * * * *    /Users/patpro/bin/cpu_load_graphs.sh >/dev/null

J'ai donc ici un script lancé toutes les minutes, et un script lancé toutes les 15 minutes.

La première complexité de launchd c'est que nous allons avoir un fichier de configuration de plusieurs lignes pour chaque ligne de la configuration de cron. Chaque action gérée par launchd fait l'objet d'une déclaration dans un fichier XML. Pour les actions périodiques de type crontab, il faut que launchd les prennent en charge dès le démarrage de la machine. Il faut aussi que les scripts ou applications soient lancés indépendamment de l'utilisateur. Pour remplir ces conditions, il faut que les fichiers XML soient placés dans /Library/LaunchDaemons/.
Voici le fichier XML correspondant à la première ligne de ma crontab, il se nomme ma.crontab.cpu_load.plist et est enregistré dans /Library/LaunchDaemons/ :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" 
                        "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>ma.crontab.cpu_load</string>
        <key>UserName</key>
        <string>patpro</string>
        <key>ProgramArguments</key>
        <array>
                <string>/Users/patpro/bin/cpu_load.sh</string>
        </array>
        <key>StartInterval</key>
        <integer>60</integer>
</dict>
</plist>

Le champs Label doit être unique, on reprend en général le nom du fichier, ici ma.crontab.cpu_load.plist, en omettant le suffix .plist.
Le Username correspond au login de l'utilisateur sous le quel sera lancée la commande.
ProgramArguments est un tableau qui peut contenir plusieurs arguments. Le premier argument (string) est toujours le nom de la commande à exécuter. Les strings suivants (facultatifs) sont les arguments que l'on souhaite passer à la commande.
Pour finir, StartInterval est le nombre de secondes qui sépare chaque lancement de la commande. Il ne peut pas être inférieur à 60, car launchd ne scrute sa liste de lancement que toutes les 60 secondes. Pour la gestion de la périodicité de date (tous les jours à 3h, ou le dimanche à 12h...) on utilise à la place de StartInterval le mot clé StartCalendarInterval (voir le man de launchd.plist pour la syntaxe).

De la même manière, la seconde ligne de ma crontab se traduirait par un fichier ma.crontab.cpu_load_graphs.plist au contenu suivant :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" 
                        "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>ma.crontab.cpu_load_graphs</string>
        <key>UserName</key>
        <string>patpro</string>
        <key>ProgramArguments</key>
        <array>
                <string>/Users/patpro/bin/cpu_load_graphs.sh</string>
        </array>
        <key>StartInterval</key>
        <integer>900</integer>
</dict>
</plist>

Il est possible d'ajouter une directive à ces fichiers plist si on souhaite logger les erreurs d'exécution :

        <key>StandardErrorPath</key>
        <string>/tmp/sortie-err.log</string>

Une fois que l'on a créé tous les fichiers adéquats dans /Library/LaunchDaemons/, on peut activer les plists launchd (sans oublier de neutraliser les crontabs) :

sudo launchctl load /Library/LaunchDaemons/ma.crontab.cpu_load*

Il peut arriver que certaines crontabs soient trop complexes pour launchd. Par exemple, je pense que launchd ne sait pas gérer une périodicité de lancement d'une fois toutes les 15 minutes du lundi au vendredi. J'avoue néanmoins que je n'ai pas vérifié ;)
edit : launchd peut gérer cela, mais ce n'est pas élégant, voir l'épisode 2.

Related posts

Configurer un serveur Subversion sur Mac OS X 10.5

Plus le temps passe et plus les distributions de Mac OS X s'enrichissent de logiciels intéressants. Leopard est livré de base avec tout ce qu'il faut pour gérer un serveur svn : Apache 2.2 et subversion. Voyons comment configurer rapidement un serveur svn+http avec accès authentifié. Il est possible bien sûr de se contenter d'un serveur svn natif, mais l'accès aux dépots par l'intermédiaire d'Apache apporte une grande souplesse en terme d'authentification et de permission d'accès.

Pour pouvoir faire les manipulations décrites ci-dessous, il faut être administrateur d'une machine sous Mac OS X 10.5. Mac OS X 10.4 et inférieurs ne sont pas livrés par défaut avec les logiciels nécessaires à la mise en place d'un serveur svn.
Ceci posé, il va nous falloir : un logiciel type "Terminal", un éditeur de texte (vi, bbedit, textmate, ...) et un accès internet.

L'installation présentée ici est faite dans le répertoire /Library/WebServer/, dans le quel tous les membres du groupe admin ont le droit d'écrire par défaut.
La première étape est la création de l'espace physique des dépots de sources (repository). On se déplace dans /Library/WebServer/, on crée un répertoire Subversion, on crée le premier repository, et on ajoute les fichiers d'authentification et de permission pour les utilisateurs.

cd /Library/WebServer/
mkdir Subversion
svnadmin create Subversion/mon_projet
vi Subversion/deny.htsvnaccesspolicy
# ici on colle/édite/adapte le contenu 
# suivant en fonction de ses besoins :
[groups]
admins = patpro
autregroupe = eric, robert
tous = sam, patpro, eric, robert, gonzague

[mon_projet:/]
@admins = rw
@autregroupe = rw
sam = r

# ensuite on génère les couples login/mot de passe
# pour les utilisateurs :
htpasswd -bc Subversion/deny.htpasswd patpro mot-de-passe

sudo chown -R www:www /Library/WebServer/Subversion/mon_projet

Ensuite, on peut ajouter des feuilles de styles XSL/CSS pour agrémenter le rendu des repositories dans Apache. Il est nettement plus simple en terme de configuration de stocker des feuilles de styles à la racine du serveur, c'est à dire dans le répertoire /Library/WebServer/Documents/ :

cd /Library/WebServer/Documents/ 
curl -O http://svn.apache.org/repos/asf/subversion/trunk/tools/xslt/svnindex.xsl 
curl -O http://svn.apache.org/repos/asf/subversion/trunk/tools/xslt/svnindex.css

Maintenant, il faut modifier la configuration d'Apache pour qu'il charge les modules Subversion et sache lire un repository. On modifie d'abord le fichier /private/etc/apache2/httpd.conf pour assurer le chargement des modules, puis on crée un fichier de configuration dédié à nos dépots.
À l'aide de son éditeur de texte préféré, on modifie /private/etc/apache2/httpd.conf en ajoutant les deux lignes ci-dessous à la suite des autres directives "LoadModule" :

LoadModule dav_svn_module	libexec/apache2/mod_dav_svn.so
LoadModule authz_svn_module	libexec/apache2/mod_authz_svn.so

Puis on crée un fichier /private/etc/apache2/other/subversion.conf contenant ces directives :

<IfModule dav_module>
 <IfModule dav_fs_module>
  <IfModule dav_svn_module>
   <IfModule authz_svn_module>

   <Location /Subversion>
      DAV svn
      SVNParentPath /Library/WebServer/Subversion
      # regle la politique d'acces des users authentifies
      AuthzSVNAccessFile /Library/WebServer/Subversion/deny.htsvnaccesspolicy
      # regle les login.pass pour les users
      AuthType Basic
      AuthName "Subversion : espace de travail"
      AuthUserFile /Library/WebServer/Subversion/deny.htpasswd
      Require valid-user
      SVNIndexXSLT "/svnindex.xsl"
   </Location>

   </IfModule>
  </IfModule>
 </IfModule>
</IfModule>

Et pour finir on relance Apache, soit par le tableau de bord "Partage", soit par la commande sudo apachectl restart. À partir de là, notre serveur subversion doit être accessible en http sur le port 80. Le repository créé plus haut est vide, il faut faire un import initial pour commencer à travailler avec. L'habitude veut qu'on crée toujours un répertoire "trunk", un autre "branches", et un troisième "tags". Voici comment les créer en local puis comment les importer dans le dépot :

cd /tmp
mkdir -p mon_projet/trunk mon_projet/branches mon_projet/tags 
svn import mon_projet http://localhost/Subversion/mon_projet -m "init"
# ici, svn me demande mon mot de passe, celui défini 
# via la commande htpasswd quelques étapes plus haut.
# ensuite je peux supprimer des répertoires vides.
rm -r /tmp/mon_projet

C'est fini ! Je peux créer ma copie de travail locale à l'emplacement de mon choix :

cd /Users/moi/mon-dossier/ 
svn checkout http://localhost/Subversion/mon_projet/trunk mon_projet

De plus, je peux naviguer dans l'arborescence svn de mon projet en pointant mon navigateur habituel vers http://localhost/Subversion/mon_projet.

Une petite note sur la sécurité : l'utilisation du protocole http et non https fait circuler les mots de passe en clair sur le réseau. Donc n'utilisez pas un mot de passe important (pas celui du système par exemple).

edit : les fichiers css et xsl ont déménagé chez Apache. J'ai donc corrigé les URL.

Related posts

Serveur OpenArena 0.7 sur FreeBSD

J'ai déjà montré précédemment comment on peut installer un serveur de jeu OpenArena 0.6 sur un FreeBSD en se basant sur le port ioquake3. Voici une mise à jour succinte qui vous permettra d'installer la version 0.7 d'OpenArena. Je ne reprends pas toutes les explications ici, donc si quelque chose n'est pas clair, c'est probablement que l'explication est donnée dans mon article précédent.

1- récupération des sources de ioquake3, et du patch d'Open Arena
Le patch se nomme maintenant oachanges.diff, au lieu de 060-oachanges.diff :

cd /usr/ports/games/ioquake3/ 
make extract 
wget http://openarena.ws/svn/source/oachanges.diff

2- modification du patch
Il y a pas mal de différences entre certains fichiers, si bien que la commande patch échoue à deux endroits. Le plus simple est de "patcher" à la main les fichiers work/ioquake3_1.34-rc3/code/qcommon/q_shared.h et work/ioquake3_1.34-rc3/code/client/cl_main.c en fonction des indications du fichier oachanges.diff. Ensuite, il faut éliminer de oachanges.diff les patches correspondants aux fichiers ci-dessus en supprimant les sections Index: qcommon/q_shared.h et Index: client/cl_main.c.

3- patch des sources, et compilation de ioquake3
Le reste des patches s'applique simplement par :

cd /usr/ports/games/ioquake3/work/ioquake3_1.34-rc3/code 
patch < ../../../oachanges.diff

Si la version précédente était installée, on peut installer la nouvelle en faisant :

FORCE_PKG_REGISTER=yes make install

Ensuite, les étapes 4,5,6 sont les mêmes que pour la version 0.6. Si c'est votre première installation, n'oubliez pas d'aller faire un tour ici pour avoir un exemple de configuration fonctionnelle.

Related posts

Créer une borne Wifi sur FreeBSD 6

LOGO WIFI Quand on ajoute une carte wifi dans un boîtier d'ordinateur, on est souvent limité à deux modes d'utilisation. Le mode "infrastructure" permet de lier la carte wifi de l'ordinateur à une borne wifi (access point). Le mode ad-hoc permet quant à lui de créer un réseau poste à poste entre deux machines, sans passer par une borne intermédiaire.
Il existe néanmoins des logiciels qui permettent de créer, sur un ordinateur, l'équivalent d'une borne wifi. Cela permet à d'autres ordinateurs de s'y connecter en mode infrastructure, plutôt qu'en mode ad-hoc. Il est alors possible d'installer un serveur DHCP, d'avoir une politique d'authentification et de filtrage des utilisateurs, et d'utiliser le WPA là où le mode ad-hoc n'autorise parfois que le WEP (cas d'un réseau ad-hoc entre deux Mac par exemple).
Les instructions d'installation si dessous sont valables pour une machine sous FreeBSD 6.2-RELEASE #0, avec l'arbre des ports à jour, et à condition de n'avoir pas fait une installation minimale du système.

Continue reading

Related posts

Faire du streaming à la maison

QuickTime BroadcasterComme disait Andy Warhol, tout le monde sera célèbre pendant 15 minutes. Pour vous préparer convenablement à cet éphémère moment il faut vous entraîner à diffuser votre image, ou au moins, tout mettre en œuvre pour vous rendre visible en deux clicks sur la terre entière. Rien n'est plus facile avec un Mac, une caméra, et le Darwin Streaming Server.

Pour monter un serveur de streaming, il faut trois choses :

  1. un logiciel de broadcast
  2. un serveur de streaming
  3. une caméra compatible avec votre broadcaster

J'ai opté lâchement pour ce que j'ai déjà, c'est à dire une webcam firewire (iSight), QuickTime Broadcaster 1.5.1, et j'ai installé Darwin Streaming Server 5.5.4. Dans ce qui suit, je présente une installation avec deux machines, une faisant broadcaster, l'autre faisant serveur de streaming. Il est possible (et même plus simple) de tout faire sur la même machine.

Installation de Darwin Streaming Server

La machine utilisée comme serveur de streaming est un PC x86 sous FreeBSD 6.2. La puissance nécessaire au streaming est tout à fait négligeable, un celeron 500MHz peut convenir. L'installation du DSS est rapide et indolore. Il faut néanmoins s'inscrire sur le site Developer d'Apple (mais vous êtes bien sûr déjà inscrit). C'est gratuit et cela se fait par ici : https://connect.apple.com. Une fois cette formalité accomplie, on peut télécharger le code source du Darwin Streaming Server (DarwinStreamingSrvr5.5.4-Source.tar). ll faut copier cette archive dans /usr/ports/distfiles/ sur la machine FreeBSD.
Sur cette dernière, on enchaîne finalement :

$ su
# cd /usr/ports/net/DarwinStreamingServer/
# make install clean

On doit ensuite configurer quelques paramètres pour que DSS fonctionne. On peut se référer au fichier /var/db/pkg/DarwinStreamingServer-5.5.4_1/+DISPLAY si on a fermé trop vite le terminal après l'installation de DSS. Il convient donc de créer un administrateur pour le serveur de streaming. Appelons le "admin", on lui donnera le mot de passe de son choix.

# qtpasswd -c admin
(on fourni deux fois le mot de passe voulu)
# echo admin: admin > /usr/local/etc/streaming/qtgroups

Ensuite, on configure le fichier /etc/rc.conf pour permettre le lancement de DSS au démarrage. Les scripts de lancement/arrêt de l'application ne fonctionnent pas comme attendu. En effet, le lancement se fait pour le streamingadminserver et pour le DSS, mais seul le premier quitte proprement quand l'argument stop est invoqué. Il faut tuer manuellement les processus DarwinStreamingServer pour quitter complètement l'application.

# echo 'streamingadminserver_enable="YES"' >> /etc/rc.conf
# echo 'streamingadminserver_flags=""' >> /etc/rc.conf
# cd /; env - /usr/local/etc/rc.d/streamingadminserver start

À partir de là, le paramétrage du serveur de streaming peut se faire via un navigateur. Il suffit de pointer vers l'adresse http://ip-de-la-machine-dss:1220/ dans un navigateur récent (Safari fait très bien l'affaire).
Il faut vous authentifier dans l'interface web pour accéder à la configuration. Les identifiants et mot de passe sont ceux créés à l'aide de la commande qtpasswd plus haut.
La première connexion déclenche la configuration initiale du DSS. En général il suffit de laisser les valeurs par défaut, chaque réglage pouvant de toute manière être modifié par la suite. Néanmoins, on veillera à bien décocher la case proposant le streaming sur le port 80, à moins de savoir très exactement ce que l'on fait.

Si le DSS ne réside pas sur la même machine que le QuickTime Broadcaster (c'est le cas ici) il faut créer un compte utilisateur de broadcast pour permettre au QTB d'envoyer la description de son flux vidéo au DSS. Dans le menu "General Settings", le lien "Change Movie Broadcast Password..." permet de créer un tel compte. La case "Allow unrestricted broadcasting" doit être cochée, et on saisit un login et un mot de passe de son choix. On valide.

Pour être certain de ne pas compliquer la configuration du DSS, il est bon de savoir que ni les "Relay" ni les "Playlists" ne vont nous servir pour un broadcast de flux en direct. Je recommande de neutraliser tous les relais en éditant les relais définis dans "Relay Settings". Il suffit de décocher "Enabled" et d'enregistrer. Il est aussi possible de supprimer les playlists. Si la flemme est plus forte, ne touchez ni aux uns ni aux autres.

Installation de QuickTime Broadcaster

Je n'ai pas la moindre idée de la manière dont QuickTime Broadcaster a atterri sur mon G5. Si contrairement à moi vous ne trouvez pas ce logiciel dans votre dossier Applications, il suffit de vous rendre sur le site Apple pour le télécharger gratuitement.

Branchez et allumez votre iSight, si elle est externe, puis lancez l'application QuickTime Broadcaster. Si le bouton en bas à droite de la fenêtre de QTB indique "Show details", cliquez dessus d'un mouvement fluide.
La partie la plus importante du réglage se trouve sous l'onglet "Network". Les réglages que j'utilise sont les suivants :

  • Transmission: "Automatic Unicast (Announce)"
  • Host Name: l'adresse IP de la machine qui héberge le DSS
  • File: mystream.mov
  • Username: le login du compte de broadcast créé plus haut, Password: le mot de passe correspondant. Si votre DSS est sur la même machine que le QTB, le login et le mot de passe sont inutiles.
  • Buffer Delay: vide
  • Broadcast over TCP: décoché

Une fois ces paramètres saisis, vous pouvez jouer avec les réglages audio et vidéo comme bon vous semble. Voilà à tout hasard un exemple de réglage testé avec succès sur une connexion ADSL avec 95 Ko/s de débit montant :

  • Audio: pas d'audio
  • Video:
  • - Source: iSight
  • - Width: 240 Height: 180
  • - Compressor: MPEG-4 Video
  • - Quality: Medium
  • - Frames per second: 24
  • - Key frame every: (vide) (décoché)
  • - Limit data rate to 1000 kbits/sec (coché)

Une fois les réglages effectués, la diffusion est lancée en cliquant sur "Broadcast" dans la partie gauche de la fenêtre ("record to disk" doit être décoché). Les données de cette fenêtre sont alors mises à jour, et l'URL du flux vidéo est donnée. Elle suit normalement ce schéma : rtsp://adresse-dss/mystream.mov.sdp. C'est cette URL que vous devez distribuer à vos admirateurs pour qu'ils la collent dans leur QuickTime (ou dans leur navigateur, si ce dernier sait que les URL rtsp sont à renvoyer à QuickTime). Bien sûr, si la machine qui héberge le DSS a plusieurs adresses IP, elles sont toutes utilisables. Il faut cependant penser à ouvrir le firewall de celle-ci pour autoriser les connexions entrantes sur le port 554.
Les chanceux qui ont une connexion très rapide en montée pourront aisément ajouter le son tout en gardant une qualité de vidéo tout à fait correcte. Par ailleurs, avec les réglages ci-dessus, un client qui subirait entre 20 et 40% de perte aurait tout de même une image correcte. Il est donc probable qu'on puisse baisser le débit de la vidéo au profit d'un son de bonne qualité.

Related posts

Configuration d’un serveur OpenArena

Pour faire suite à mon article sur l'installation d'un serveur OpenArena sur FreeBSD, voilà à peu de choses près le fichier de configuration que j'utilise :

sv_hostname "Mon Serveur OA 0.6"
sv_maxclients 8
sv_master1 ""
sv_maxPing 100
sv_pure 0
sv_maxRate 10000
sv_allowdownload 1
sv_privateClients "0" // slots substracted from sv_maxclients
//sv_privatePassword "privpass for privclients"
capturelimit 4
timelimit 10
fraglimit 10
rconPassword "rconpass" // for remote ingame servercontrol
g_motd "there is no place like 127.0.0.1"
g_quadfactor 4
g_inactivity 0
g_allowvote 1
g_friendlyFire 1
g_teamAutoJoin 1
g_teamForceBalance 1

set bot_enable "1"      // 0 = off, 1 = on
set bot_minplayers "4"  // manages the addition/removal of autobots. 

set d1 "g_gametype 3; map aggressor; set nextmap vstr d2"
set d2 "g_gametype 0; map czest1dm; set nextmap vstr d3"
set d3 "g_gametype 4; map cbctf1; set nextmap vstr d4"
set d4 "g_gametype 3; map ce1m7; set nextmap vstr d5"
set d5 "g_gametype 0; map dm4ish; set nextmap vstr d6"
set d6 "g_gametype 4; map oa_ctf2; set nextmap vstr d7"
set d7 "g_gametype 3; map kaos; set nextmap vstr d8"
set d8 "g_gametype 0; map void4; set nextmap vstr d9"
set d9 "g_gametype 3; map oa_rpg3dm2;set nextmap vstr d10"
set d10 "g_gametype 0; map q3dm6ish; set nextmap vstr d11"
set d11 "g_gametype 3; map hydronex; set nextmap vstr d1"
vstr d1 // start loop at d1

Ne vous attendez pas à en tirer un grand plaisir en solo, les bots sont d'une stupidité crasse. En plus, le serveur a bien du mal à réellement équilibrer les équipes et parfois les bots ne parviennent pas à se connecter au jeu, un comble. Pour s'amuser il faudra impérativement jouer avec et contre des humains. Toutes les cartes listées sont agréables à jouer avec 4 joueurs, certaines s'accommodent très bien de 6 ou 8, mais il faut alors monter le fraglimit.

Related posts

Serveur de jeu OpenArena sur FreeBSD

Open Arena est un jeu libre basé sur le moteur de Quake 3 Arena. Comme il est entièrement libre, contrairement à ioquake3, on peut jouer sans posséder le CD original de Quake 3 Arena.
Open Arena utilise ses propres graphismes et cartes, le tout propulsé par le moteur ioquake3. Ce dernier est présent dans les ports de FreeBSD, il est donc facilement installable. Seulement, tel quel, il est conçu pour fonctionner avec les données de Quake 3 Arena issues du CD officiel.
Pour l'utiliser avec les données libres d'Open Arena, il faut modifier les sources avant la compilation. Cela se fait simplement grace au patch fourni par Open Arena.

Continue reading

Related posts

Astuces pour gérer les redirections d’erreurs en Bash

Ça faisait bien longtemps que je n'avais rien posté de constructif. Alors pour les débutants que ça intéresse, voici un petit baratin sur les redirections d'erreurs en bash. Je ne vais pas traiter tous les types de redirections mais uniquement certains cas appliqués aux erreurs. Pour obtenir quelque chose d'exhaustif je vous conseille comme d'habitude l'advanced bash scripting guide.

Les cas simples.

Par défaut, les shells comme bash affichent la sortie standard (stdout) et la sortie d'erreur (stderr) dans la fenêtre de terminal où tourne le process bavard. L'utilisateur n'a donc pas, a priori, de moyen de discerner l'une de l'autre, sauf à connaître les formes respectives d'une erreur et d'une sortie normale :

$ touch existant
$ ls inexistant existant
ls: inexistant: No such file or directory   # sortie stderr
existant                                    # sortie stdout

Les entrées et sorties correspondent à des descripteurs de fichiers. L'entrée standard porte le descripteur de fichier 0, la sortie stdout le 1, et stderr le 2. Ce sont ces descripteurs de fichiers qui permettent d'utiliser ces entrées-sorties.
Ainsi, pour un usage tout à fait basique, je peux vouloir enregistrer les erreurs dans un fichier, et n'afficher que les résultats "normaux" de stdout dans mon terminal.
En reprenant l'exemple ci-dessus, cela donne :

$ ls inexistant existant 2>/tmp/ls-erreur
existant 

le descripteur de fichier 2 est dirigé vers le fichier /tmp/ls-erreur, et seule la sortie standard est affichée dans le shell. On peut vérifier que le fichier /tmp/ls-erreur contient bien l'erreur qu'on a constatée lors du test précédent :

$ cat /tmp/ls-erreur
ls: inexistant: No such file or directory

Quand on veut simplement se débarrasser des erreurs, on peut utiliser /dev/null comme déversoir. C'est le trou noir habituel Unix, ce qui y entre n'en sort jamais.
On peut aussi choisir de diriger stderr vers stdout. C'est très utile si on souhaite enchaîner des commandes via des pipes ou des redirections, et que l'on souhaite traiter les erreurs aussi bien que les résultats de stdout. Le résultat de cette manipulation, c'est que nos erreurs ne s'affichent plus dans stderr, mais bien dans stdout. On peut donc les filtrer avec grep, les manipuler avec awk, sed, ... Notez la différence de comportement :

$ ls inexistant existant | awk '/No such/ {print "un fichier absent !"}'
ls: inexistant: No such file or directory
$ ls inexistant existant 2>&1 | awk '/No such/ {print "un fichier absent !"}'
un fichier absent !

Dans le premier cas, stderr n'est pas redirigée dans stdout, donc elle ne passe pas dans le pipe, et n'est pas vue par la commande awk. Dans le second cas, stderr est envoyée dans stdout, qui, elle, est prise en compte par le pipe, et ensuite par awk.

Le cas des scripts.

Dans un script shell, les cas de figure peuvent être plus complexes. La redirection par "2>..." fonctionne bien, mais pour une seule commande. Il faudrait pour l'utiliser dans un script, soit l'utiliser à chaque commande, soit lancer le script en le faisant suivre de cette redirection.
Que faire quand on souhaite par exemple garder un log d'exécution d'un script, ou juste ses erreurs sans s'encombrer d'une redirection à chaque appel du script ?
On utilise la commande exec, qui permet de créer/lier/détruire des descripteurs de fichiers.

Grace à la commande exec, je peux dupliquer un descripteur de fichier existant (une sorte de sauvegarde), ce qui me permet de ré-attribuer son numéro à un autre fichier (mon fichier de log par exemple, ou /dev/null, ...).
Un example va nous permettre de voir comment cela fonctionne.

01 #!/bin/bash
02 exec 6>&2
03 exec 2>>A.log
04 echo "ici on va générer quelques erreurs :"
05 ls -XYZ
06 exec 2>&6 6>&-
07 echo "Fin du script, les erreurs sortent normalement"
08 ls -XYZ

La ligne 02 sert à sauvegarder ma stderr dans le descripteur de fichier 6. Les descripteurs 3 à 9 sont disponibles, 6 est un choix arbitraire.
La ligne 03 remplace le descripteur 2 par mon fichier de log A.log.
Si on teste ce script, on voit bien que l'erreur générée à la ligne 05 ne sort pas dans le shell, elle est écrite directement dans A.log. Bien sûr, la ligne 05 peut être remplacée par autant de ligne de commande que l'on souhaite.
La ligne 06 ré-initialise le descripteur de fichier 2 à son ancienne valeur (stockée dans le 6), détruit le descripteur 6, et ferme le fichier de log.
Les erreurs suivantes, comme celle de la ligne 08, sont donc libres de sortir normalement dans stderr.

J'espère que ça pourra servir de point de départ à certains curieux pour approfondir les différents types de redirection dans le shell. Il y a de quoi s'occuper un bon moment avec.

Pour aller un peu plus loin, n'hésitez pas à consulter les autres articles sur bash !

Related posts