nb=$( ps -fu root | grep “mon_programme” | grep -v grep | wc -l ) … FAILED 1/2

Bon voici un billet sans doute “technique” pour qui n’en fait pas du tout mais il est assez simple, si, si promis, et représentatif d’un problème courant sur les serveurs, à savoir qu’un programme vérifie à son lancement qu’il n’est pas déjà lancé afin d’éviter des collisions dans le traitement de son activité.
L’idée est donc de compter le nombre d’instances et de prendre nos décisions sur cette base et c’est LA ligne en charge de cette activité que je propose pour analyse.

Il m’a été inspiré par les deux heures de parlottes dans mon open-space d’autres personnes dont j’ai fini par voir un peu marre et que j’ai donc aidées à résoudre ce problème.
Pour les fans du docteur Layton, sortez votre haut de forme !

Filtres

En Unix, il existe de nombreux programmes que l’on nomme filtre car ils n’ont d’autre usage que de “tamiser” un contenu selon nos besoins. Donc réellement leurs actions sur le système sont nulles, elles ne changent pas la résolution de l’affichage à l’écran ni ne permettent de récupérer les “cables” américains… tout au plus de les trier ;)

grep

Un des plus fameux filtres, il permet de rechercher une séquence de caractère paramétrable et de signaler son absence, sa présence, éventuellement son nombre d’occurrence, ça dépend des options souhaitées. Un exemple, si grep recherchait le mot “chat” dans le fichier fic1 donc le contenu serait le suivant:

Le chat mange des os
Le chien mange des poissons

grep me retournerait par défaut :

# grep chat fic1
Le chat mange des os

L’option ‘-v‘, qui apparait dans la seconde occurrence, permet elle de filtrer à l’envers, à savoir ne retenir que les lignes où la chaîne n’apparait. Dans le cas de fic1 et de la recherche précédente, nous aurions:


# grep -v chat fic1
Le chien mange des poissons

wc

Non pas le canard, encore un autre filtre qui signifie en fait “Word Count” et donc compte les mots, les caractères et les lignes. L’option ‘-l‘ lui demande de n’indiquer que le nombre de lignes lues. Sur notre fic1, cela donnerait :

# wc -l fic1
2 /tmp/fic1

Commandes

Là, il s’agit de programme qui ont une réelle capacité à obtenir des informations du système ou de le modifier. Ici, une seule suffit au vu du but annoncé, à savoir ce qui est en train de tourner !

ps

Là encore, cette commande a une signification à savoir “process status”. C’est un peu comme le gestionnaire des tâches de windows, on sait ce qui fonctionne, qui le fait fonctionner, etc
Les options ici sont assez simples ‘-f‘ pour full afin que la commande nous offre un listing complet et ‘-u‘ pour limiter néanmoins ce listing à l’utilisateur root, à savoir l’administrateur système1.

Shell

Bon, tous nos commandes et nos filtres, ne vivent pas dans le néant et ce qui permet de les saisir, c’est ce qu’on appelle le shell, littéralement une coquille. Dans le monde Unix IBM, par défaut, c’est un shell plaisamment appelé Korn Shell 88, vous pouvez headbanger.

Ce shell a la gentillesse d’offrir la gestion des variables, à savoir associer un identifiant à une valeur, les pipes, à savoir faire de la plomberie pour brancher une suite de programmes entre eux, et la substitution de commandes avec $(), qui permet de remplacer le contenu entre les parenthèses par les valeurs retournées par les programmes qu’elle contient. Mais développons brièvement.

Variables

C’est un incontournable en programmation impérative, on donne des noms plus ou moins clairs à ces variables et on leur assigne une valeur. Selon le langage, on peut avoir à se soucier du typage, à savoir si notre variable doit, par exemple contenir, un nombre ou un caractère mais le shell est permissif sur ce point.

L’affectation du contenu d’une variable se fait avec le signe “=“. Notre billet ici présent raconte donc l’histoire de l’affectation d’une certaine valeur à ‘nb‘.

Pipes

On ne rigole pas…

Ce concept au nom amusant est une évolution majeure du shell Unix, même si celle-ci date, et permet de faire communiquer des programmes entre eux sans devoir passer par de l’écriture sur disque. Si nous repensons à nos filtres, qui permettent donc de filtrer un contenu, il y a donc une entrée et une sortie. Le pipe fait que la source est le programme situé avant et que la sortie se fait sur le programme d’après.

La conséquence est que l’ensemble des commandes chaînées s’exécute quasi-simultanément pour profiter de ce mécanisme.

Notre chaînage assure donc que la liste des processus sera filtrée 3 fois (grep/grep/wc), et que le premier grep ne s’arrêtera pas avant que ps n’ait terminé, ni que le second grep ne s’arrêtera avant que le premier grep n’est terminé, je vous laisse déduire pour wc ;)

Substitution de commandes

Conclusion logique, nous cherchons à allouer à ‘nb‘ une valeur, à savoir celle obtenue par la succession de nos commandes que nous aurons permis les fameux pipes et cette magique substitution de commandes.

Pour réaliser cette action, les commandes destinées à être substituées sont lancées dans un sous-shell qui porte le nom du shell parent.

And now…

Bien, cette ligne apparait donc dans le script mon_programme dont nous souhaitons, rappelons le, qu’il s’assure qu’une instance de lui même ne s’exécute pas déjà.

Maintenant, le ver est déjà dans le fruit, la valeur retournée ne sera pas la bonne. Quant au pourquoi, je laisse mes amiEs geeks cogiter un peu, proposer des réponses, sous 2-3 jours je dirais tout le mal que je pense de cette façon de faire ;)

  1. tout puissant []

7 thoughts on “nb=$( ps -fu root | grep “mon_programme” | grep -v grep | wc -l ) … FAILED 1/2

  1. Alors bon, en théorie ça marche, mais le double grep me semble douteux. Y’a un énoooorme risque (sauf si ton programme s’appelle tagadatsouintsouinrev2beta4LINbin peut-être) de collisions de noms. Tiens, disons que mon truc s’appelle gameserver et qu’il prenne un paramètre –pingreply=n, et que j’appelle :

    gameserver –pingreply=1

    Cette ligne n’en trouvera jamais aucune instance. Du coup, j’aurais tendance à dire que c’est plus propre de faire un seul grep avec une vraie expression régulière, du genre et au hasard :

    ps -C “mon programme” -eo user | grep ^root$

    Voilà. Mais je pense pas que ça soit que ça.

  2. Je suis peut être pas clair sur l’exemple du gameserver :

    Le grep -v grep va éliminer toutes les instances de cette commande, parce que dans –pingreply il y’a “grep”

  3. Le double grep est douteux mais valable, et clairement pas élégant.
    Sous AIX, point de commandes évoluées, on a à disposition que du très basique, surtout pour le ps.

  4. bah moi perso, je la jouerais genre

    mycount=`ps -f -u $USER_NAME | grep “joliprogram” | grep -v “grep” | wc -l`

    pis sinon, bah t’as la bonne vieille méthode du lock…un lock file créé avec ton PID, et quand tu lances ton programme, tu check le lock file.. si il est la, hop, récupères le PID (genre ps -fp PID). Old school, mais ca fonctionne a tous les coups..

  5. Pff.. les filtres c’est pour les sissies.. on fait ça à l’ancienne :le bon vieux lock, et merci à lockfile, qui attend gentiment que le lock soit released par l’autre instance. Par défaut, lockfile va ressayer toutes les 8 ou 10 secondes (default sleeptime).. Voili.. si tu veux que ton scripts exit si il est déjà lancé (ie si le lockfile existe), suffit de faire un define retry 0.

    # On crée le lock file
    lockfile -r0 ${0}.lock &# On vire le lock file à la mano en fin
    rm -f ${0}.lock

    propre, efficace.. et puis les locks, c’est ma came, tu le sais ;-)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>