Comment exécuter une commande système ou un programme externe en NodeJS ?

Si en voyant la librairie "node-ffmpeg" vous pensiez que ffmpeg avait été réécrite en JS, alors lisez cet article jusqu'à la fin !

Article publié le 21/07/2020, dernière mise à jour le 19/09/2023

Il arrive parfois que les APIs fournies par NodeJS pour dialoguer avec le système ne suffisent pas à nos besoin, imaginons par exemple que vous ayez besoin que votre programme éteigne la machine après s'être exécuté.

Mieux encore, et si vous aviez besoin de lancer un programme externe et de contrôler son exécution depuis NodeJS ! Comment faire ?

La réponse à ce problème est simple : nous allons utiliser le module child_process.

Les processus enfants

Comme vous le savez sûrement, le système d'exploitation gère de nombreux processus simultanément, dont certains sont dépendants du processus qui les a créé : on appelle ça des "processus enfants" ou "child process".

Par exemple pour chaque nouvel onglet, Chrome lance un processus enfant. On peut fermer ces processus indépendamment, mais si l'on ferme le processus parent tous les autres sont aussi détruits !

Cette mécanique évite de laisser des processus fantômes qui consommeraient des ressources inutilement.

Lancer un nouveau processus

Lancer un nouveau processus est très simple en NodeJS, il suffit de deux lignes de code :


const child_process = require('child_process');
let ls_process = child_process.exec("ls -a");

exec() vs spawn()

Il existe deux méthodes différentes pour créer un nouveau processus, chacune ayant ses particularités, voici le même bout de code que précédemment avec la méthode spawn.


const child_process = require('child_process');
let ls_process = child_process.spawn("ls",["-a"]);

Les différences sont :

  • exec() créé un shell dans lequel il va passer la commande à exécuter
  • spawn() streame en continu les données retournées par le processus, tandis qu'exec() les retourne à la fin de l'exécution
  • exec() est limité à un transfert de données de 200kb par défaut

En définitive, si vous voulez exécuter une simple commande, utilisez plutôt exec() tandis que pour lancer un programme externe avec lequel vous allez communiquer, on va privilégier spawn(), comme dans la suite de ce tutoriel !

Contrôler un processus

Ce n'est pas le tout de lancer un processus, encore faut-il pouvoir lui envoyer des informations et recevoir celles qu'il nous envoie, que ce soit lors d'une exécution réussie ou en cas d'erreur.

stdin, stdout, stderr

Si vous n'en avez jamais entendu parlé, voici les trois flux de données standard (std) d'un programme: l'entrée (in), la sortie (out), et les erreurs (err).

Lorsqu'un programme vous demande de rentrer des informations au clavier après son lancement, vous envoyez des informations dans l'entrée standard, et lorsqu'il affiche du texte dans le terminal, il redirige simplement sa sortie standard vers le terminal (même chose pour les erreurs).

Ecouter les flux de données

Les flux de données sont gérés par des évènements auxquels il faut souscrire comme dans l'exemple ci-dessous :



ls_process.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls_process.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

Il faut garder en tête que le format des données renvoyées est spécifique au programme externe invoqué et parfois à la commande du programme en question.

Ecrire dans l'entrée standard

Au lieu d'invoquer un programme qui va s'exécuter grâce aux paramètre passés à l'initialisation et se fermer ensuite, il est parfois judicieux d'utiliser des programmes qui vont tourner en tâche de fond et prendre des commandes à la volée.

Cette méthode est notamment utile pour des programmes prenant un certain temps à démarrer, voici un exemple d'envoi de données à un programme en cours d'exécution :



ls_process.stdin.setEncoding('utf-8');
ls_process.stdin.write("play");
//il est parfois nécessaire d'ajouter un \n à la fin de la commande pour valider l'envoi
ls_process.stdin.end();

Pour le reste, je vous laisse vous familiariser avec toutes les nombreuses fonctionnalités du module child_process directement sur la documentation officielle disponible ici : https://nodejs.org/api/child_process.html

A noter

Lorsque vous voyez passer des librairies de gestion de vidéo, d'image ou de son en NodeJS (comme node-ffmpeg par exemple), vous devez être conscient que ces librairies ne sont pas codées en Javascript (la plupart du temps), mais ces librairies sont seulement des wrappers qui exécutent un programme externe écrit dans un langage plus bas niveau.

Maintenant vous aussi vous pouvez donc écrire vos propres wrappers !

Attention : Lorsque vous faites appel à des commandes systèmes, votre programme devient dépendant de ce système en particulier et risque de ne pas s'exécuter sur un autre OS (à moins de prévoir tous les cas possibles), il faut donc le faire en connaissance de l'environnement d'exécution !


Brett Jordan sur Unsplash

Vous avez terminé l'article ?

Commentaires (0)

pour laisser un commentaire

Aucun commentaire pour l'instant