Ejecución de comandos de shell con Node.js

    Introducción

    Los administradores y desarrolladores de sistemas recurren con frecuencia a la automatización para reducir su carga de trabajo y mejorar sus procesos. Cuando se trabaja con servidores, las tareas automatizadas se suelen programar con scripts de shell. Sin embargo, un desarrollador puede preferir usar un lenguaje de nivel superior más general para tareas complejas. Muchas aplicaciones también necesitan interactuar con el sistema de archivos y otros componentes de nivel de sistema operativo, lo que a menudo se hace más fácilmente con utilidades de nivel de línea de comando.

    Con Node.js, podemos ejecutar comandos de shell y procesar sus entradas y salidas usando JavaScript. Por lo tanto, podemos escribir la mayoría de estas operaciones complejas en JavaScript en lugar del lenguaje de scripts de shell, lo que potencialmente hace que el programa sea más fácil de mantener.

    En este artículo, aprenderemos las diversas formas de ejecutar comandos de shell en Node.js usando el child_process módulo.

    El módulo child_proccess

    Node.js ejecuta su ciclo de eventos principal en un solo hilo. Sin embargo, eso no significa que todo su procesamiento se realice en ese único hilo. Las tareas asincrónicas en Node.js se ejecutan en otros hilos internos. Cuando están completos, el código de la devolución de llamada, o el error, se devuelve al hilo principal único.

    Estos distintos subprocesos se ejecutan en el mismo proceso de Node.js. Sin embargo, a veces es deseable crear otro proceso para ejecutar código. Cuando se crea un nuevo proceso, el sistema operativo determina qué procesador utiliza y cómo programar sus tareas.

    los child_process El módulo crea nuevos procesos secundarios de nuestro proceso principal de Node.js. Podemos ejecutar comandos de shell con estos procesos secundarios.

    El uso de procesos externos puede mejorar el rendimiento de su aplicación si se usa correctamente. Por ejemplo, si una función de una aplicación Node.js consume mucha CPU, como Node.js es de un solo subproceso, bloquearía la ejecución de las otras tareas mientras se ejecuta.

    Sin embargo, podemos delegar ese código intensivo en recursos a un proceso hijo, digamos un programa C ++ muy eficiente. Nuestro código Node.js ejecutará ese programa C ++ en un nuevo proceso, sin bloquear sus otras actividades, y cuando se complete, procesará su salida.

    Dos funciones que usaremos para ejecutar comandos de shell son exec y spawn.

    La función ejecutiva

    los exec() La función crea un nuevo shell y ejecuta un comando dado. La salida de la ejecución se almacena en búfer, lo que significa que se mantiene en la memoria y está disponible para su uso en una devolución de llamada.

    Usemos exec() función para listar todas las carpetas y archivos en nuestro directorio actual. En un nuevo archivo Node.js llamado lsExec.js, escribe el siguiente código:

    const { exec } = require("child_process");
    
    exec("ls -la", (error, stdout, stderr) => {
        if (error) {
            console.log(`error: ${error.message}`);
            return;
        }
        if (stderr) {
            console.log(`stderr: ${stderr}`);
            return;
        }
        console.log(`stdout: ${stdout}`);
    });
    

    Primero, requerimos el child_process módulo en nuestro programa, específicamente utilizando el exec() función (a través de la desestructuración ES6). A continuación, llamamos al exec() función con dos parámetros:

    • Una cadena con el comando de shell que queremos ejecutar.
    • Una función de devolución de llamada con tres parámetros: error, stdout, stderr.

    El comando de shell que estamos ejecutando es ls -la, que debería enumerar todos los archivos y carpetas en nuestro directorio actual línea por línea, incluidos los archivos / carpetas ocultos. La función de devolución de llamada registra si obtuvimos un error al intentar ejecutar el comando o la salida en el shell stdout o stderr arroyos.

    Nota la error el objeto es diferente de stderr. los error el objeto no es nulo cuando el child_process módulo no puede ejecutar un comando. Esto podría suceder si intenta ejecutar otro script de Node.js en exec() pero no se pudo encontrar el archivo, por ejemplo. Por otro lado, si el comando se ejecuta con éxito y escribe un mensaje en el flujo de error estándar, entonces el stderr el objeto no sería nulo.

    Si ejecuta ese archivo Node.js, debería ver un resultado similar a:

    $ node lsExec.js
    stdout: total 0
    [email protected] 9 arpan arpan  0 Dec  7 00:14 .
    [email protected] 4 arpan arpan  0 Dec  7 22:09 ..
    [email protected] 1 arpan arpan  0 Dec  7 15:10 lsExec.js
    
    child process exited with code 0
    

    Ahora que hemos entendido cómo ejecutar comandos con exec(), aprendamos otra forma de ejecutar comandos con spawn().

    La función de generación

    los spawn() La función ejecuta un comando en un nuevo proceso. Esta función utiliza una API Stream, por lo que su salida del comando está disponible a través de oyentes.

    Al igual que antes, usaremos el spawn() función para listar todas las carpetas y archivos en nuestro directorio actual. Creemos un nuevo archivo Node.js, lsSpawn.jse ingrese lo siguiente:

    const { spawn } = require("child_process");
    
    const ls = spawn("ls", ["-la"]);
    
    ls.stdout.on("data", data => {
        console.log(`stdout: ${data}`);
    });
    
    ls.stderr.on("data", data => {
        console.log(`stderr: ${data}`);
    });
    
    ls.on('error', (error) => {
        console.log(`error: ${error.message}`);
    });
    
    ls.on("close", code => {
        console.log(`child process exited with code ${code}`);
    });
    

    Comenzamos por exigir el spawn() función de la child_process módulo. Luego, creamos un nuevo proceso que ejecuta el ls comando, pasando -la como argumento. Observe cómo los argumentos se mantienen en una matriz y no se incluyen en la cadena de comandos.

    Luego configuramos nuestros oyentes. los stdout objeto de ls, dispara un data evento cuando el comando escribe en esa secuencia. Del mismo modo, el stderr también dispara un data evento cuando el comando escribe en esa secuencia.

    Los errores se detectan al escucharlos directamente en el objeto que almacena la referencia del comando. Solo obtendrá un error si child_process no ejecuta el comando.

    los close El evento ocurre cuando el comando ha terminado.

    Si ejecutamos este archivo Node.js, deberíamos obtener la salida como antes con exec():

    $ node lsSpawn.js
    stdout: total 0
    [email protected] 9 arpan arpan  0 Dec  7 00:14 .
    [email protected] 4 arpan arpan  0 Dec  7 22:09 ..
    [email protected] 1 arpan arpan  0 Dec  7 15:10 lsExec.js
    [email protected] 1 arpan arpan  0 Dec  7 15:40 lsSpawn.js
    
    child process exited with code 0
    

    ¿Cuándo usar exec y spawn?

    La diferencia clave entre exec() y spawn() así es como devuelven los datos. Como exec() almacena toda la salida en un búfer, requiere más memoria que spawn(), que transmite la salida como viene.

    Generalmente, si no espera que se devuelvan grandes cantidades de datos, puede usar exec() por simplicidad. Buenos ejemplos de casos de uso son crear una carpeta u obtener el estado de un archivo. Sin embargo, si espera una gran cantidad de resultados de su comando, debe usar spawn(). Un buen ejemplo sería usar el comando para manipular datos binarios y luego cargarlos en su programa Node.js.

    Conclusión

    Node.js puede ejecutar comandos de shell utilizando el estándar child_process módulo. Si usamos el exec() , nuestro comando se ejecutará y su salida estará disponible para nosotros en una devolución de llamada. Si usamos el spawn() módulo, su salida estará disponible a través de detectores de eventos.

    Si nuestra aplicación espera una gran cantidad de resultados de nuestros comandos, deberíamos preferir spawn() encima exec(). De lo contrario, podríamos optar por usar exec() por su sencillez.

    Ahora que puede ejecutar tareas externas a Node.js, ¿qué aplicaciones crearía?

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *