<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet title="XSL formatting" type="text/xsl" href="http://www.bonz.org/tech/feed/rss2/xslt" ?><rss version="2.0"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
  <title>ZenTech - code</title>
  <link>http://www.bonz.org/tech/</link>
  <description>Om Mani Padme Hum</description>
  <language>fr</language>
  <pubDate>Mon, 10 Nov 2008 00:41:06 +0100</pubDate>
  <copyright></copyright>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <generator>Dotclear</generator>
  
    
  <item>
    <title>Qui est le plus fort entre l'éléphant et l'hippopotame</title>
    <link>http://www.bonz.org/tech/post/2008/04/01/Qui-est-le-plus-fort-entre-lelephant-et-lhippopotame</link>
    <guid isPermaLink="false">urn:md5:680d04d6d6bc409ad11e407ba1a7f22f</guid>
    <pubDate>Wed, 02 Apr 2008 15:31:00 +0200</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>performance</category><category>python</category><category>regexp</category><category>timeit</category>    
    <description>&lt;p&gt;Je me pose toujours des questions de performance lorsque je développe. Il y a souvent plusieurs façons de rêgler un problème et certaines plus consommatrices de ressources que d'autres. Dans le cas qui m'intéresse, je me questionne sur la performance des expressions régulières en regard d'autres méthodes de traitement de chaîne. Je dois effectuer un traitement sur un très grand nombre de lignes de log au fur et à mesure de leur émission en &lt;code&gt;syslog&lt;/code&gt;.&lt;/p&gt;    &lt;p&gt;La plupart du temps avec python, avoir une idée innovante est modérée par l'existence d'un module qui couvre déjà le sujet. Dans ce cas, le module &lt;code&gt;&lt;a href=&quot;http://docs.python.org/lib/module-timeit.html&quot; hreflang=&quot;en&quot;&gt;timeit&lt;/a&gt;&lt;/code&gt; va me permettre de tester la rapidité d'exécution de différents bouts de code.&lt;/p&gt;


&lt;p&gt;Le format d'une ligne de log syslog est connu et décrit par la &lt;a href=&quot;http://www.ietf.org/rfc/rfc3164.txt&quot; hreflang=&quot;en&quot;&gt;RFC3164&lt;/a&gt;, ce format me permet différentes approches pour en extraire différents champs. Dans mon cas, je vais extraire le champ appelé &lt;code&gt;HEADER&lt;/code&gt; de différentes façons. Un ligne de log syslog doit être de la forme&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;     &amp;gt;TIMESTAMP HOSTNAME TAG CONTENT
| PRI |      HEADER      |    MSG
&lt;/pre&gt;

&lt;p&gt;Dans le code python ci-dessous, les trois fonctions testées sont&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;split()&lt;/code&gt; qui récupère le champ header en effectuant un split sur le caractère espace et en réunissant les champs résultants.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match()&lt;/code&gt; effectue une sélection par expression régulière.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match_full()&lt;/code&gt; effectue une capture par expression régulière et occurence nommée.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il est important de bien faire attention au contexte et à la portée des variables et fonctions (&quot;scope&quot; et &quot;namespace&quot;) lorsqu'on utilise &lt;code&gt;timeit&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;
import timeit

def split():
    str = '&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'
    ' '.join(str.split()[0:4]).split('&amp;gt;')[1]

def match():
    import re
    pattern = r'^&amp;lt;(\d+)&amp;gt;(\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (\w+.+)'
    _pattern = re.compile(pattern)
    str = '&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'
    _pattern.search(str).group(2)

def match_full():
    import re
    pattern_full = r'^&amp;lt;(?P&amp;lt;pri&amp;gt;\d+)&amp;gt;(?P&amp;lt;header&amp;gt;\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (?P&amp;lt;msg&amp;gt;\w+.+)'
    _pattern_full = re.compile(pattern_full)
    str = '&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'
    _pattern_full.search(str).groupdict()['header']

print &amp;quot;split:&amp;quot;
print timeit.Timer(&amp;quot;split()&amp;quot;, &amp;quot;from __main__ import split&amp;quot;).timeit()
print &amp;quot;match:&amp;quot;
print timeit.Timer(&amp;quot;match()&amp;quot;, &amp;quot;from __main__ import match&amp;quot;).timeit()
print &amp;quot;match_full:&amp;quot;
print timeit.Timer(&amp;quot;match_full()&amp;quot;, &amp;quot;from __main__ import match_full&amp;quot;).timeit()
&lt;/pre&gt;

&lt;p&gt;Ce qui donne après exécution&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ python stress.py
split:
6.14724898338
match:
14.80702281
match_full:
19.0696430206
&lt;/pre&gt;


&lt;p&gt;En y réfléchissant bien, les fonctions testées ne sont pas vraiment optimisées et ne sont pas donc comparables en vitesse d'exécution. Pour vraiment jouer sur un pied d'égalité, je procède aux modifications suivantes&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Import du module re en début de script, ça fonctionne finalement, j'avais peur que les problèmes de contexte de &lt;code&gt;timeit&lt;/code&gt; ne posent problème.&lt;/li&gt;
&lt;li&gt;La compilation des expressions régulières est faite une fois pour toute et pas à chaque lancement des fonctions &lt;code&gt;cmatch()&lt;/code&gt; et &lt;code&gt;cmatch_full()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Introduction de deux nouvelles fonctions effectuant une capture par expression régulière sans compilation de l'expression.&lt;/li&gt;
&lt;li&gt;Utilisation de la méthode &lt;code&gt;repeat()&lt;/code&gt; plutôt que &lt;code&gt;timeit()&lt;/code&gt; pour essayer d'obtenir un résultat moins influencé par le fonctionnement du système.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
import re, timeit

str='&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'

split_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import split&amp;quot;&amp;quot;&amp;quot;
match_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import match&amp;quot;&amp;quot;&amp;quot;
match_full_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import match_full&amp;quot;&amp;quot;&amp;quot;
cmatch_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import cmatch&amp;quot;&amp;quot;&amp;quot;
cmatch_full_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import cmatch_full&amp;quot;&amp;quot;&amp;quot;

pattern = r'^&amp;lt;(\d+)&amp;gt;(\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (\w+.+)'
pattern_full = r'^&amp;lt;(?P&amp;lt;pri&amp;gt;\d+)&amp;gt;(?P&amp;lt;header&amp;gt;\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (?P&amp;lt;msg&amp;gt;\w+.+)'
_pattern = re.compile(pattern)
_pattern_full = re.compile(pattern_full)

def split():
    ' '.join(str.split(' ')[0:4]).split('&amp;gt;')[1]

def match():
    re.search(pattern, str).group(2)

def match_full():
    re.search(pattern_full, str).groupdict()['header']

def cmatch():
    _pattern.search(str).group(2)

def cmatch_full():
    _pattern_full.search(str).groupdict()['header']

print &amp;quot;Testing %d occurences of each match, %d times&amp;quot; % (timeit.default_number, timeit.default_repeat)
split_result = timeit.Timer(&amp;quot;split()&amp;quot;, split_init).repeat()
print &amp;quot;split: %fs %s&amp;quot; % (min(split_result), split_result)
match_result = timeit.Timer(&amp;quot;match()&amp;quot;, match_init).repeat()
print &amp;quot;match: %fs %s&amp;quot; % (min(match_result), match_result)
match_full_result = timeit.Timer(&amp;quot;match_full()&amp;quot;, match_full_init).repeat()
print &amp;quot;match_full: %fs %s&amp;quot; % (min(match_full_result), match_full_result)
cmatch_result = timeit.Timer(&amp;quot;cmatch()&amp;quot;, cmatch_init).repeat()
print &amp;quot;cmatch: %fs %s&amp;quot; % (min(cmatch_result), cmatch_result)
cmatch_full_result = timeit.Timer(&amp;quot;cmatch_full()&amp;quot;, cmatch_full_init).repeat()
print &amp;quot;cmatch_full: %fs %s&amp;quot; % (min(cmatch_full_result), cmatch_full_result)
&lt;/pre&gt;


&lt;p&gt;Qui me donne les résultats suivants&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ python stress.py
Testing 1000000 occurences of each match, 3 times
split: 5.818318s [5.8183181285858154, 6.5981872081756592, 6.327794075012207]
match: 8.220056s [8.2262570858001709, 8.2361149787902832, 8.2200560569763184]
match_full: 12.399510s [12.40582799911499, 12.399509906768799, 12.501888036727905]
cmatch: 3.818037s [3.8180370330810547, 3.8276369571685791, 3.8310809135437012]
cmatch_full: 6.724747s [6.7247469425201416, 6.7392361164093018, 6.7564170360565186]
&lt;/pre&gt;

&lt;p&gt;Il est surprenant de constater la différence énorme entre les expressions régulières compilées et non compilées, l'avantage de compiler ses expressions dans le cas d'une utilisation de masse est évident. La rapidité de &lt;code&gt;split()&lt;/code&gt; est plutôt décevante par rapport à l'utilisation des expressions régullières.&lt;/p&gt;


&lt;p&gt;Ces tests sont intéressant et donnent des indications de performance mais ne remplacent pas des tests complet de &lt;a href=&quot;http://docs.python.org/lib/profile.html&quot; hreflang=&quot;en&quot;&gt;profilage&lt;/a&gt;.&lt;/p&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>C'est curieux chez les marins ce besoin de faire des phrases</title>
    <link>http://www.bonz.org/tech/post/2008/03/18/Cest-curieux-chez-les-marins-ce-besoin-de-faire-des-phrases</link>
    <guid isPermaLink="false">urn:md5:b67d011b6c4f2f48a14c8b2ac225f6c1</guid>
    <pubDate>Tue, 18 Mar 2008 15:47:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>c</category><category>sed</category>    
    <description>    &lt;p&gt;Suite à &lt;a href=&quot;http://www.bonz.org/tech/post/2008/03/17/Pour-en-finir-avec-Lulu-la-Nantaise#c543&quot; hreflang=&quot;fr&quot;&gt;une remarque judicieuse&lt;/a&gt; j'ai été obligé de gérer un petit cas particulier qui conduisait à un conflit. Le &lt;a href=&quot;http://www.bonz.org/files/ccmtstrip.sed&quot;&gt;script sed&lt;/a&gt; est maintenant modifié, ce n'est pas extrêmement propre mais c'est assez efficace. Le bout du script concerné est&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
# remove /* */ comments, multiline or not
/\/\*/{
    # special case of /*/ starting comments that fools the next test
    s:/\*/:/\* /:
    :mline
    /\*\// {
        s:/\*.*\*/::
        b mlineend
    }
    N
    b mline
}
:mlineend
&lt;/pre&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>Pour en finir avec Lulu la Nantaise</title>
    <link>http://www.bonz.org/tech/post/2008/03/17/Pour-en-finir-avec-Lulu-la-Nantaise</link>
    <guid isPermaLink="false">urn:md5:cf7307e34e9c4d47776363b984d66d4d</guid>
    <pubDate>Mon, 17 Mar 2008 12:32:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>c</category><category>sed</category>    
    <description>&lt;p&gt;Au final, je me suis occupé des espaces, le but de ce script étant au départ de fournir un code nettoyé qui pourra passer au banc des statistiques. Le script sed est &lt;a href=&quot;http://www.bonz.org/files/ccmtstrip.sed&quot;&gt;disponible&lt;/a&gt; et placé sous license type *BSD. Quelques corrections d'erreur et de nouvelles fonctions constituent cette dernière version du script.&lt;/p&gt;    &lt;p&gt;J'utilise un petit fichier de définition &lt;code&gt;test.h&lt;/code&gt;&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
/*
 * Big story here
 * my life
 */
// one line
#define HEHE 1

/* includes */
#include &amp;quot;blah&amp;quot;
#include &amp;quot;foo&amp;quot; // end line comment

#include /* weird comment */ &amp;quot;pouet&amp;quot;
int woop /* to see */
/* let's continue with weird comments */ void sigpipe_handle(int);

/*-------------
  bloh
---------------*/
int main(int argc, char **argv);
int parseCommandLine(int argc, char* argv[]);
&lt;/pre&gt;

&lt;p&gt;Ce qui donne, une fois passé par sed&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ sed -nf ccmtstrip.sed test.h
#define HEHE 1
#include &amp;quot;blah&amp;quot;
#include &amp;quot;foo&amp;quot;
#include &amp;quot;pouet&amp;quot;
int woop
void sigpipe_handle(int);
int main(int argc, char **argv);
int parseCommandLine(int argc, char* argv[]);
&lt;/pre&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>Les commentaires déssoudés par Jo le trembleur</title>
    <link>http://www.bonz.org/tech/post/2008/03/15/Les-commentaires-dessoudes-par-Jo-le-trembleur</link>
    <guid isPermaLink="false">urn:md5:9892afc0ad555608da23da5264809480</guid>
    <pubDate>Sat, 15 Mar 2008 21:21:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>c</category><category>sed</category>    
    <description>&lt;p&gt;La version &lt;a href=&quot;http://www.bonz.org/tech/post/2008/03/14/Du-C-par-Teddy-de-Montreal&quot; hreflang=&quot;fr&quot;&gt;précédente&lt;/a&gt; du script sed ne me convenait pas. Elle ne gérait pas certaines formes de commentaires horribles, qui mériterait une sentence extrême et douloureuse. C'est maintenant chose rêglée avec une version améliorée.&lt;/p&gt;    &lt;pre&gt;
#!/usr/bin/sed -nf

# remove single line comments
/^\/\//d

# remove /* */ comments, multiline or not
/\/\*/{
    :mline
    /\*\// {
        s:/\*.*\*/::
        b mlineend
    }
    N
    b mline
}
:mlineend

# remove empty lines
/^$/d

p
&lt;/pre&gt;

&lt;p&gt;Il peut arriver que certains espaces inutiles apparaissent dans le code suite à la suppression des commentaires multiligne mais ce n'est pas fondamentalement un problème.&lt;/p&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>Du C par Teddy de Montreal</title>
    <link>http://www.bonz.org/tech/post/2008/03/14/Du-C-par-Teddy-de-Montreal</link>
    <guid isPermaLink="false">urn:md5:3e74dbfb5103e8942b71f2b23dbc3227</guid>
    <pubDate>Fri, 14 Mar 2008 17:25:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>c</category><category>sed</category>    
    <description>&lt;p&gt;&quot; ... un fondu qui travaillait qu'à la dynamite.&quot; Je me suis mis en tête de compter le nombre de lignes efficaces d'un développement en C. Par ligne efficace j'entends le nombre de lignes de code vraiment compilées. Je dois donc éliminer les commentaires sous leurs diverses formes et les lignes blanches. Et comme j'aime bien me faire mal, au lieu de faire ça en 10 minutes en Perl ou Python, je l'ai fait grâce à &lt;code&gt;sed(1)&lt;/code&gt;.&lt;/p&gt;    &lt;p&gt;Ce script sed doit donc gérer les lignes blanches, les commentaires sur une ligne commençant pas '&lt;code&gt;//&lt;/code&gt;', les commentaires sur plusieurs lignes (ou pas) du type &lt;code&gt;/* comment */&lt;/code&gt;. Ca donne alors le script suivant&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
#!/bin/sed -nf

# remove empty lines
/^$/d
# remove single line comments
/^\/\//d

# remove /* */ comments, multiline or not 
/\/\*/{
    :mline
    /\*\//b mlineend
    N
    b mline
    :mlineend
    d
}
p
&lt;/pre&gt;

&lt;p&gt;Le seul passage qui me semble nécessiter une explication est celui qui gère les commentaires sur plusieurs lignes. Ce qui est fait là est assez simple (sisi c'est vrai)&amp;nbsp;: on ne traite que les lignes comprenant '&lt;code&gt;/*&lt;/code&gt;', si la ligne comprend aussi '&lt;code&gt;*/&lt;/code&gt;' on passe à la balise mlineend, sinon on ajoute la ligne suivante aux lignes déjà lues et on passe à la balise mline (boucle). Le saut à la balise mlineend provoque la destruction des lignes lues et le passage à la ligne suivante en recommençant le script.&lt;br /&gt;
On finit par afficher la ligne (grâce à 'p') si aucun des cas précédents ne s'est produit.&lt;/p&gt;


&lt;p&gt;Le défaut de ce script est de ne pas gérer les cas où les commentaires sont mixés avec du code. Je pense l'améliorer dans un deuxième temps pour prendre en compte ces cas.&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;&lt;strong&gt;Mise à jour&lt;/strong&gt;&lt;/ins&gt;: une &lt;a href=&quot;http://www.bonz.org/tech/post/2008/03/15/Les-commentaires-dessoudes-par-Jo-le-trembleur&quot; hreflang=&quot;fr&quot;&gt;nouvelle version&lt;/a&gt; de ce script règle ces soucis.&lt;/p&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>Booooooonjouuuuuuuuuuuur jeee m'aaaappeeeeeeeeeeeeelle Dooooooooriiiiiiiiiiiis</title>
    <link>http://www.bonz.org/tech/post/2008/03/12/Booooooonjouuuuuuuuuuuur-jeee-maaaappeeeeeeeeeeeeelle-Dooooooooriiiiiiiiiiiis</link>
    <guid isPermaLink="false">urn:md5:3b3acdda0ae236ebe8705160e7ffb8e8</guid>
    <pubDate>Thu, 13 Mar 2008 15:54:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>bash</category><category>snmp</category>    
    <description>&lt;p&gt;Suite à la configuration de net-snmp pour &lt;a href=&quot;http://www.bonz.org/tech/post/2008/03/04/All-your-poll-are-belong-to-us&quot; hreflang=&quot;fr&quot;&gt;utiliser des scripts&lt;/a&gt;, il a bien fallu coder un compteur 32bits. Aimant les défis, je me suis décidé à écrire un compteur 32 bits en bash qui compte les entrées d'une base de donnée.&lt;/p&gt;    &lt;p&gt;Comment doit fonctionner un compteur 32 bits en script&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le compteur est un entier modulo 2^32&lt;/li&gt;
&lt;li&gt;Le compteur augmente uniquement de la différence de valeur entre deux requêtes&lt;/li&gt;
&lt;li&gt;Le compteur doit être conservé entre deux requêtes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour implémenter le modulo, ce n'est pas très compliqué en &lt;code&gt;bash/sh&lt;/code&gt;&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$(($count % 2**32))
&lt;/pre&gt;

&lt;p&gt;J'ai choisi de stocker les données dans un &lt;a href=&quot;http://tldp.org/LDP/abs/html/arrays.html&quot; hreflang=&quot;en&quot;&gt;tableau bash&lt;/a&gt;. Pour conserver les données entre 2 appels, je stocke directement le tableau dans un fichier sous la forme d'une définition de tableau bash. De cette façon, je peux inclure le fichier au début de mon script et j'évite d'avoir à lire un fichier de donnée. Les données que je conserve sont le résultat de ma dernière requête SQL, la différence entre la dernière requête et la précédente, et finalement mon compteur. Pourquoi conserver le dernier résultat&amp;nbsp;? Ca me permet au passage de choisir le comportement du compteur entre compteur 32 bits et donnée directe (gauge).&lt;br /&gt;
La requête SQL est tout simplement un &lt;code&gt;count(*)&lt;/code&gt; sur une table, le nombre de lignes de cette table peut augmenter ou diminuer (si il ne faisait qu'augmenter, je pourrais directement prendre le &lt;code&gt;count(*)&lt;/code&gt; modulo 2^32).&lt;/p&gt;
&lt;pre&gt;
#!/bin/sh

counters='pgcount.txt'
# call the counters
if [ -f $counters ]; then
    . $counters
else
    # last count / last diff / counter
    count=(0 0 0)
fi

# adds 2 values
# return the sum modulo 2**32
counter() {
    counter=$(($1 + $2))
    echo -n $(($counter % 2**32))
}

# dumps the values as bash arrays
dump() {
    echo &amp;quot;count=(${count[@]})&amp;quot; &amp;gt; $counters
}

getdata() {
    count=($@)
    rows=$(getcount $table)
    if [ &amp;quot;${count[1]}&amp;quot; -ne &amp;quot;$count&amp;quot; ]; then
        count[1]=$(($rows - ${count[0]}))
        count[0]=$rows
        count[2]=$(counter ${count[2]} ${count[1]})
    fi
    # return needed values
    echo ${count[@]}
}

count=($(getdata ${count[@]}))
dump()
&lt;/pre&gt;

&lt;p&gt;La fonction &lt;code&gt;getcount()&lt;/code&gt; effectue le &lt;code&gt;count(*)&lt;/code&gt; et retourne le nombre de lignes lues.&lt;/p&gt;


&lt;p&gt;Ce script est lancé toutes les minutes car la requêtes peut parfois prendre du temps et provoquer un timeout snmp. Mon serveur net-snmp appelle donc un bête script qui inclut le fichier de sauvegarde et renvoit les données sauvées.&lt;/p&gt;
&lt;pre&gt;
#!/bin/sh

counters='pgcount.txt'
# call the counters
if [ -f $counters ]; then
    . $counters
else
    # last count / last diff / counter
    count=(0 0 0)
fi

echo ${count[1]}
echo ${count[2]}
&lt;/pre&gt;

&lt;p&gt;En sélectionnant la donnée que je veux dans mon chemin snmp je peux obtenir le nombre de lignes insérées sous forme de compteur 32 bits ou le nombre de lignes insérées entre deux exécutions du scipt. Cette dernière valeur est bancale puisqu'elle ne représente pas le nombre de lignes insérées entre deux consultations du compteur. Il serait possible de le faire fonctionner correctement si le script de lecture réécrivait le fichier de sauvegarde à chaque lecture pour signifier qu'il a été consulté. Il faudrait alors implémenter un verrou sur ce fichier pour éviter qu'il soit utilisé par les 2 scripts en même temps.&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;NB&lt;/ins&gt;: Désolé pour le titre du post, je ne trouvais rien de plus inspiré mais un compteur 32bits c'est un peu comme &lt;a href=&quot;http://fr.wikipedia.org/wiki/Le_Monde_de_Nemo&quot; hreflang=&quot;fr&quot;&gt;Doris&lt;/a&gt;, au bout d'un certain temps, il revient à zero. Oui, je sais ...&lt;/p&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>HAL&gt; Affirmative, Dave, I read you.</title>
    <link>http://www.bonz.org/tech/post/2008/01/10/HAL-Affirmative-Dave-I-read-you</link>
    <guid isPermaLink="false">urn:md5:1b24cb50e2d7f4d973321a63fd0da31d</guid>
    <pubDate>Fri, 11 Jan 2008 17:39:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>freebsd</category><category>irc</category><category>linux</category><category>python</category>    
    <description>&lt;p&gt;&lt;img src=&quot;http://www.bonz.org/tech/public/tech/Hal-9000.jpg&quot; alt=&quot;HAL&quot; style=&quot;float:right; margin: 0 0 1em 1em;&quot; /&gt; Ah la vacuité d'une session &lt;a href=&quot;http://www.faqs.org/rfcs/rfc1459.html&quot; hreflang=&quot;en&quot;&gt;IRC&lt;/a&gt; sur un canal dirigé par un automate. Quoi de plus extraordinaire que pouvoir connaitre la météo de &lt;a href=&quot;http://www.mairie-felletin.fr/&quot; hreflang=&quot;fr&quot;&gt;Felletin dans la Creuse&lt;/a&gt; ou d'avoir une &lt;a href=&quot;http://eggnet.warpzone.org/moduledesc.php?module=9&quot; hreflang=&quot;en&quot;&gt;blague carambar&lt;/a&gt; à la demande. En ce moment je fais beaucoup de python, donc je me suis penché sur le problème pour un &lt;a href=&quot;http://www.binaries.fr/blog/&quot; hreflang=&quot;fr&quot;&gt;ami&lt;/a&gt;. Je vais essayer de vous exposer les clés de l'utilisation d'&lt;code&gt;&lt;a href=&quot;http://python-irclib.sourceforge.net/&quot; hreflang=&quot;en&quot;&gt;irclib&lt;/a&gt;&lt;/code&gt; pour construire un automate irc simple.&lt;/p&gt;    &lt;p&gt;L'automate est présenté ci-après, ses fonctions sont simplement la connexion à un serveur irc local sur le port 6667 et l'entrée dans le canal #test. L'automate s'attend à lire des commandes préfixées de '&lt;code&gt;!&lt;/code&gt;' dans le canal. La commande &lt;code&gt;!cycle&lt;/code&gt; demande à l'automate de partir et revenir sur le canal, la commande &lt;code&gt;!die&lt;/code&gt; provoque la déconnexion du serveur puis l'arrêt de l'automate. Le script de l'automate ci-après se trouve en téléchargement &lt;a href=&quot;http://www.bonz.org/files/mybot.py&quot;&gt;ici&lt;/a&gt; et peut être lancé par la commande &lt;code&gt;python mybot.py&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;
import sys
try:
    from ircbot import SingleServerIRCBot
    import irclib
except ImportError, e:
    print e
    sys.exit(1)

class pybot(SingleServerIRCBot):
    def __init__(self, server, nick, realname, chan):
        SingleServerIRCBot.__init__(self, server, nick, realname)
        self.chan = chan
        self.nick = nick

    ## Event callbacks
    def on_welcome(self, c, e):
        if self.chan:
            c.join(self.chan)

    def on_join(self, c, e):
        who = e.source().split('!', 1)
        if who[0] == self.nick:
            c.privmsg(e.target(), 'Hello world!')

    def on_pubmsg(self, c, e):
        msg = e.arguments()[0].strip()
        if msg.startswith('!'):
            cmd = msg[1:].split(' ', 1)[0]
            self.trigger(c, e, cmd)

    ## Command triggers
    def trigger(self, c, e, cmd):
        try:
            getattr(self, 'cmd_%s' % cmd)(c, e)
        except AttributeError:
            pass

    def cmd_cycle(self, c, e):
        chan = e.target()
        c.part(chan, 'cycle')
        c.join(chan)

    def cmd_die(self, c, e):
        SingleServerIRCBot.die(self, 'Argh')

## Here comes the real script
if __name__ == '__main__':
    server = [('localhost', 6667)]
    nick = 'monbot'
    realname = 'monbot'
    channel = '#test'

    bot = pybot(server, nick, realname, channel)
    try:
        bot.start()
    except KeyboardInterrupt:
        bot.connection.disconnect(&amp;quot;Ctrl-C m'a tuer&amp;quot;)
        bot.connection.close()
&lt;/pre&gt;


&lt;p&gt;La librairie &lt;code&gt;irclib&lt;/code&gt; est basée sur la gestion d'évènements, des méthodes vont être lancées sur apparition d'un évènement au sens IRC. Les méthodes de l'objet &lt;code&gt;pybot&lt;/code&gt; commençant par '&lt;code&gt;on_&lt;/code&gt;' sont les méthodes de gestion d'évènements, par exemple lorsqu'un message est envoyé sur un canal (&lt;code&gt;PUBMSG&lt;/code&gt;) la méthode on_pubsmg() est appelée. La création de l'automate se base sur la surcharge des méthodes de gestion d'évènements. Ces méthodes ont toutes la même définition d'appel&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
def on_foo(self, c, e):
    [...]
&lt;/pre&gt;

&lt;p&gt;La variable &lt;code&gt;c&lt;/code&gt; est un objet &lt;code&gt;ServerConnection&lt;/code&gt;, cet objet permet de manipuler tout ce qui concerne le serveur irc d'où provient l'évènement. On peut par exemple rejoindre un canal (&lt;code&gt;c.join()&lt;/code&gt;) ou envoyer un message à un canal ou une personne (commande &lt;code&gt;PRIVMSG&lt;/code&gt; via &lt;code&gt;c.privmsg()&lt;/code&gt;). La variable &lt;code&gt;e&lt;/code&gt; est un objet &lt;code&gt;Event&lt;/code&gt;, elle permet d'obtenir toutes les informations sur l'évènement qui a provoqué l'appel de la méthode. L'origine de l'évènement (&lt;code&gt;e.source()&lt;/code&gt;), la destination de cet évènement (&lt;code&gt;e.target()&lt;/code&gt;) ou encore les arguments de cet évènement (&lt;code&gt;e.arguments()&lt;/code&gt;). Toutes les informations sur ces 2 méthodes se trouvent dans le module irclib (&lt;code&gt;pydoc irclib&lt;/code&gt;).&lt;/p&gt;


&lt;p&gt;A partir de la connaissance de ces éléments de base, la construction d'un automate devient assez simple. Si l'automate doit effectuer des tâches régulières, les méthodes &lt;code&gt;execute_at()&lt;/code&gt; et &lt;code&gt;execute_delayed()&lt;/code&gt; (héritage de l'objet IRC parent de SingleServerIRCBot) peuvent être utilisées. Plus d'information sur &lt;code&gt;irclib&lt;/code&gt; devrait se trouver sur mon &lt;a href=&quot;http://wiki.bonz.org/python/irclib&quot; hreflang=&quot;fr&quot;&gt;wiki&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Il est également possible d'utiliser &lt;a href=&quot;http://twistedmatrix.com/documents/current/api/twisted.words.protocols.irc.html&quot; hreflang=&quot;en&quot;&gt;twisted.words&lt;/a&gt; pour effectuer la même chose mais ça me semble une librairie un peu imposante pour un simple automate.&lt;/p&gt;


&lt;p&gt;Il faut noter qu'on peut faire également le même genre de chose en &lt;a href=&quot;http://search.cpan.org/~bingos/POE-Component-IRC-5.48/lib/POE/Component/IRC.pm&quot; hreflang=&quot;en&quot;&gt;Perl&lt;/a&gt;.&lt;/p&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>WOPR&gt; Global Thermonuclear War</title>
    <link>http://www.bonz.org/tech/post/2008/01/03/WOPR-Global-Thermonuclear-War</link>
    <guid isPermaLink="false">urn:md5:e11fe7ac496ab81a907768d677602a9c</guid>
    <pubDate>Thu, 03 Jan 2008 18:33:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>freebsd</category><category>linux</category><category>python</category>    
    <description>&lt;p&gt;Dans mon parcours d'initiation à python, je me suis retrouvé face à un problème existentiel (ce qui donne tout le caractère initiatique au parcours, le problème, parce qu'il est existentiel ... laisse tomber). Lors de l'utilisation du module &lt;a href=&quot;http://docs.python.org/lib/module-cmd.html&quot; hreflang=&quot;en&quot;&gt;cmd&lt;/a&gt;, comment gérer correctement les interruptions classiques que sont &lt;code&gt;ctrl+D&lt;/code&gt; (&lt;code&gt;EOF&lt;/code&gt;) et &lt;code&gt;ctrl-C&lt;/code&gt; (&lt;code&gt;SIGINT&lt;/code&gt;).&lt;/p&gt;    &lt;p&gt;Ci-après, un squelette pour la gestion des 2 signaux évoqués. Juste après ci-après, les quelques explications qui s'imposent.&lt;/p&gt;
&lt;pre&gt;
import sys
import cmd

class shell(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)

    # Let's handle ctrl+D signals and quit gently
    def do_EOF(self, str):
        # CLEAN HERE
        return 1

if __name__ == &amp;quot;__main__&amp;quot;:
    console = shell()
    try:
        console.cmdloop()
    except KeyboardInterrupt:
        # CLEAN HERE
        sys.exit(0)
&lt;/pre&gt;


&lt;h2&gt;&lt;code&gt;ctrl-D&lt;/code&gt; ou la fin du monde&lt;/h2&gt;

&lt;p&gt;Le manuel de &lt;a href=&quot;http://docs.python.org/lib/Cmd-objects.html#l2h-4880&quot; hreflang=&quot;en&quot;&gt;cmd&lt;/a&gt; nous dit&amp;nbsp;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;An end-of-file on input is passed back as the string 'EOF'.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Ce qui signifie que la combinaison de touches &lt;code&gt;ctrl+D&lt;/code&gt; revient à gérer la commande &lt;code&gt;EOF&lt;/code&gt;, la méthode appelée est donc &lt;code&gt;do_EOF()&lt;/code&gt;.&lt;br /&gt;
Une autre information à propos de &lt;code&gt;cmdloop()&lt;/code&gt; donnée dans le manuel est&amp;nbsp;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;This method will return when the postcmd() method returns a true value. The stop argument to postcmd() is the return value from the command's corresponding do_*() method.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Donc si la méthode &lt;code&gt;do_EOF()&lt;/code&gt; retourne simplement 1, la boucle va tout bêtement s'arrêter.&lt;/p&gt;


&lt;h2&gt;&lt;code&gt;ctrl-C&lt;/code&gt; ou la fin de l'univers&lt;/h2&gt;

&lt;p&gt;En Python, l'utilisation de &lt;code&gt;ctrl-C&lt;/code&gt; lève l'exception &lt;code&gt;&lt;a href=&quot;http://docs.python.org/lib/module-exceptions.html#l2h-111&quot; hreflang=&quot;en&quot;&gt;KeyboardInterrupt&lt;/a&gt;&lt;/code&gt;, en capturant cette interruption, on permet au script de sortir de façon maitrisée.&lt;/p&gt;


&lt;h2&gt;Evolution&lt;/h2&gt;

&lt;p&gt;Il est conseillé d'ajouter des actions de nettoyage au niveau des commentaires &lt;code&gt;# CLEAN HERE&lt;/code&gt; pour permettre une sortie de la console vraiment sans encombre. Il reste encore à trouver un moyen élégant d'utiliser &lt;code&gt;ctrl-c&lt;/code&gt; pour annuler la ligne en cours et récupérer l'invite vierge sans quitter &lt;code&gt;cmd.cmdloop()&lt;/code&gt;&lt;br /&gt;
Attention toutefois, ce code n'a pas été testé dans un environnement microsoft. Les signaux ne sont surement pas traités de la même façon, ça n'étonnera personne.&lt;/p&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>A chacun sa petite fiche de présence</title>
    <link>http://www.bonz.org/tech/post/2007/11/11/A-chacun-sa-petite-fiche-de-presence</link>
    <guid isPermaLink="false">urn:md5:f9d9225fb0acef7466b41d7ffd18c4bf</guid>
    <pubDate>Sun, 11 Nov 2007 12:52:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>freebsd</category><category>lighttpd</category><category>lighttpd 1.4</category>    
    <description>&lt;p&gt;J'aime bien avoir des logs d'accès séparés par vhost sur mes serveurs, ça permet de simplifier la recherche d'erreur et la construction de statistiques. Comme je gère mes vhosts par un &lt;a href=&quot;http://www.bonz.org/files/include_vhost&quot;&gt;script&lt;/a&gt;, j'ai pensé que rajouter la création automatique de logs d'accès serait un plus. C'est toujours ça qui ne sera pas oublié lors de l'ajout d'un nouveau vhost.&lt;br /&gt;
Au passage, j'ai rajouté le &lt;code&gt;document-root&lt;/code&gt; automatique, les données d'un site virtuel doivent se trouver dans un répertoire du nom de son fichier de configuration.&lt;/p&gt;
&lt;pre&gt;
# ./include_vhost -h
Usage: include_dir [-hptx] [-d docroot_path] [-l log_path] &amp;lt;dir&amp;gt;
    -h      displays this help
    -d      Automatic document-root specification
    -l      Add vhost access logs in log_path
    -p      print the parsed config files and exit (default behaviour)
    -t      test the config files
    -x      specify a lighttpd binary (default /usr/local/sbin/lighttpd)
&lt;/pre&gt;    &lt;p&gt;Si les logs doivent se trouver dans &lt;code&gt;/var/log/lighttpd&lt;/code&gt;, que les sites se trouvent dans &lt;code&gt;/usr/local/www/data&lt;/code&gt; et que les fichiers de configurations de vhost sont dans &lt;code&gt;/usr/local/etc/lighttpd/vhost.d&lt;/code&gt;, on obtient l'appel suivant au script&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
./include_vhost -l /var/log/lighttpd -d /usr/local/www/data /usr/local/etc/lighttpd/vhost.d
&lt;/pre&gt;

&lt;p&gt;Au passage, le script change de nom étant donné qu'il se spécialise vraiment dans la gestion de fichiers de configuration d'hôte virtuel.&lt;/p&gt;


&lt;p&gt;Pour information, je n'ai pas rajouté les logs d'erreur dans cet automate, pour la simple et bonne raison que la &lt;a href=&quot;http://trac.lighttpd.net/trac/wiki/server.errorlogDetails&quot;&gt;documentation de configuration&lt;/a&gt; déconseille de placer plusieurs appels à &lt;code&gt;server.errorlog&lt;/code&gt; dans la configuration de lighty.&lt;/p&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>Je veux bien prendre mais pas de là</title>
    <link>http://www.bonz.org/tech/post/2007/07/21/Je-veux-bien-prendre-mais-pas-de-la</link>
    <guid isPermaLink="false">urn:md5:fcc06c420cb815398b7ea84c68ef7f75</guid>
    <pubDate>Sat, 21 Jul 2007 22:14:00 +0200</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>freebsd</category><category>lighttpd</category><category>lighttpd 1.4</category>    
    <description>    &lt;p&gt;C'est toujours autant amusant de jouer avec lighttpd, mais parfois on se trompe dans un fichier de configuration et là, c'est le drame. Mon script &lt;code&gt;&lt;a href=&quot;http://www.bonz.org/files/include_dir&quot;&gt;include_dir&lt;/a&gt;&lt;/code&gt; ne permet pas de savoir quel fichier de configuration est brokmut lors de l'utilisation de &lt;code&gt;lighttpd -p -f lighttpd.conf&lt;/code&gt;. C'est maintenant chose faite, avec la nouvelle version toujours téléchargeable au &lt;a href=&quot;http://www.bonz.org/files/include_dir&quot;&gt;même endroit&lt;/a&gt;. Au passage, il est passé en license &lt;a href=&quot;http://en.wikipedia.org/wiki/Beerware&quot;&gt;beerware&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;
# ./include_dir -h
Usage: include_dir [-hptx] &amp;lt;dir&amp;gt;
    -h      displays this help
    -p      print the parsed config files and exit (default behaviour)
    -t      test the config files
    -x      specify a lighttpd binary (default /usr/local/sbin/lighttpd)
# ./include_dir -t vhost.d/
&amp;gt;&amp;gt;&amp;gt; Testing vhost.d/www.vhost1.org
Syntax OK
&amp;gt;&amp;gt;&amp;gt; Testing vhost.d/www.vhost2.org
2007-07-21 22:28:47: (configfile.c.833) source: vhost.d/www.vhost2.org line: 3 pos: 21 parser failed somehow near here: (EOL) 
&amp;gt;&amp;gt;&amp;gt; Testing vhost.d/www.vhost3.org
Syntax OK
&lt;/pre&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>httpd.zero = enable</title>
    <link>http://www.bonz.org/tech/post/2007/03/30/httpdzero-enable</link>
    <guid isPermaLink="false">urn:md5:a313b05949ad947c651b05724ce4e318</guid>
    <pubDate>Fri, 30 Mar 2007 18:55:00 +0200</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>freebsd</category><category>lighttpd</category><category>lighttpd 1.4</category>    
    <description>&lt;p&gt;Toujours à m'amuser avec &lt;a href=&quot;http://www.lighttpd.net/&quot;&gt;asparttpd&lt;/a&gt; et je me dis que ce serait sympa de pouvoir utiliser le mécanisme auquel je me suis habitué pour les vhosts. Un dossier &lt;code&gt;vhost.d&lt;/code&gt; et un fichier de conf par vhost dans ce répertoire.&lt;/p&gt;    &lt;p&gt;Rien de plus facile en fait, grâce à &lt;code&gt;include_shell&lt;/code&gt; je lance un script de ma composition, ce script est un bête bout de &lt;code&gt;sh&lt;/code&gt; qui inclut tous les fichiers du répertoire donné en argument au script. Ca donne ce genre de chose&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
#### vhosts
include_shell &amp;quot;./include_dir vhost.d&amp;quot;
&lt;/pre&gt;

&lt;p&gt;et le script&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
#!/bin/sh

# I prefer doing nothing if no dir is given on command line
# that way I do not break anything
if [ &amp;quot;$#&amp;quot; = &amp;quot;0&amp;quot; ]; then
        exit 0
fi

# exclude vim temporary files
# configuration file has to have the vhost domain name
for site in $(ls $1 | grep -v .swp); do
        echo &amp;quot;\$HTTP[\&amp;quot;host\&amp;quot;] == \&amp;quot;$site\&amp;quot; {&amp;quot;
        cat $1/$site
        echo &amp;quot;}&amp;quot;
        echo
done
&lt;/pre&gt;

&lt;p&gt;l'utilisation d'un petit &lt;code&gt;lighttpd -p -f lighttpd.conf&lt;/code&gt; permet de s'assurer que tout fonctionne correctement, on remarque également que de cholis commentaires sont ajoutés pour faciliter la lecture.
Le script &lt;code&gt;&lt;a href=&quot;http://www.bonz.org/files/include_dir&quot;&gt;include_dir&lt;/a&gt;&lt;/code&gt; est présent dans le même répertoire que le fichier de configuration de lighttpd (je sais, c'est sale).&lt;/p&gt;</description>
    
    
    
      </item>
    
</channel>
</rss>