La facilidad de uso de un lenguaje de programación depende, en gran medida, de su capacidad para desarrollar programas de línea de comandos.

Dado que Node.js es una de las herramientas de desarrollo más populares en la actualidad, saber cómo usarlo para desarrollar programas de línea de comandos es una habilidad que los desarrolladores web deberían dominar.

Node.js 命令行程序开发教程

Recientemente, el blog de Npm tiene una serie deartículos, que me parecen muy bien escritos. A continuación, este es el tutorial que he ampliado a partir de ellos, y debería ser la mejor solución disponible actualmente.

1. Scripts ejecutables

Empecemos desde lo más simple.

Primero, escribe un script ejecutable llamado hello usando el lenguaje JavaScript.

#!/usr/bin/env node
console.log('hello world');

Luego, modifica los permisos de hello.

$ chmod 755 hello

Ahora, hello ya se puede ejecutar.

$ ./hello
hello world

Si quieres eliminar la ruta delante de hello, puedes añadir la ruta de hello a la variable de entorno PATH. Sin embargo, otra mejor manera es crear un nuevo package.json en el directorio actual y escribir el siguiente contenido.

{
  "name": "hello",
  "bin": {
    "hello": "hello"
  }
}

Luego ejecuta el comando npm link.

$ npm link

Ahora, al ejecutar hello de nuevo, no es necesario escribir la ruta.

$ hello
hello world

2. Escritura original de los argumentos de línea de comandos

Los argumentos de línea de comandos se pueden obtener con la variable de sistema process.argv.

#!/usr/bin/env node
console.log('hello ', process.argv[2]);

Al ejecutar, solo tienes que añadir los argumentos directamente después del archivo de script.

$ ./hello tom
hello tom

3. Crear nuevos procesos

El script puede crear subprocesos mediante el módulo child_process, permitiendo así ejecutar comandos del sistema Unix.

#!/usr/bin/env node
var name = process.argv[2];
var exec = require('child_process').exec;

var child = exec('echo hello ' + name, function(err, stdout, stderr) {
  if (err) throw err;
  console.log(stdout);
});

El uso es el siguiente.

$ ./hello tom
hello tom

4. Módulo shelljs

shelljsEl módulo reenvuelve child_process, lo que facilita la invocación de comandos del sistema. Es necesario instalarlo antes de usarlo.

npm install --save shelljs

Luego, reescribe el script.

#!/usr/bin/env node
var name = process.argv[2];
var shell = require("shelljs");

shell.exec("echo hello " + name);

El código anterior es el modo local de shelljs, es decir, ejecuta comandos shell mediante el método exec. También existe un modo global que permite escribir comandos shell directamente en el script.

require('shelljs/global');

if (!which('git')) {
  echo('Sorry, this script requires git');
  exit(1);
}

mkdir('-p', 'out/Release');
cp('-R', 'stuff/*', 'out/Release');

cd('lib');
ls('*.js').forEach(function(file) {
  sed('-i', 'BUILD_VERSION', 'v0.1.2', file);
  sed('-i', /.*REMOVE_THIS_LINE.*/n/, '', file);
  sed('-i', /.*REPLACE_LINE_WITH_MACRO.*/n/, cat('macro.js'), file);
});
cd('..');

if (exec('git commit -am "Auto-commit"').code !== 0) {
  echo('Error: Git commit failed');
  exit(1);
}

5. Módulo yargs

shelljs solo resuelve cómo invocar comandos shell, mientras que el módulo yargs puede resolver cómo manejar los argumentos de línea de comandos. También requiere instalación.

$ npm install --save yargs

El módulo yargs proporciona el objeto argv para leer los argumentos de línea de comandos. Observa el hello reescrito.

#!/usr/bin/env node
var argv = require('yargs').argv;

console.log('hello ', argv.name);

Al usarlo, las dos formas siguientes son válidas.

$ hello --name=tom
hello tom

$ hello --name tom
hello tom

Si cambias argv.name a argv.n, puedes usar la forma de parámetro corto de una sola letra.

$ hello -n tom
hello tom

Se puede usar el método alias para especificar que name es un alias de n.

#!/usr/bin/env node
var argv = require('yargs')
  .alias('n', 'name')
  .argv;

console.log('hello ', argv.n);

De esta manera, tanto los parámetros cortos como los largos se pueden utilizar.

$ hello -n tom
hello tom
$ hello --name tom
hello tom

El objeto argv tiene una propiedad de guion bajo (_), que permite obtener los argumentos que no comienzan con guion.

#!/usr/bin/env node
var argv = require('yargs').argv;

console.log('hello ', argv.n);
console.log(argv._);

El uso es el siguiente.

$ hello A -n tom B C
hello  tom
[ 'A', 'B', 'C' ]

6. Configuración de los argumentos de línea de comandos

El módulo yargs también proporciona 3 métodos para configurar los argumentos de línea de comandos.

  • demand: si es obligatorio
  • default: valor por defecto
  • describe: descripción
#!/usr/bin/env node
var argv = require('yargs')
  .demand(['n'])
  .default({n: 'tom'})
  .describe({n: 'your name'})
  .argv;

console.log('hello ', argv.n);

El código anterior especifica que el parámetro n es obligatorio, con valor por defecto tom, y muestra una línea de descripción.

El método options permite escribir toda esta configuración en un objeto.

#!/usr/bin/env node
var argv = require('yargs')
  .option('n', {
    alias : 'name',
    demand: true,
    default: 'tom',
    describe: 'your name',
    type: 'string'
  })
  .argv;

console.log('hello ', argv.n);

A veces, algunos parámetros no necesitan un valor y solo actúan como un interruptor; en este caso, se puede usar el método boolean para especificar que estos parámetros devuelvan un valor booleano.

#!/usr/bin/env node
var argv = require('yargs')
  .boolean(['n'])
  .argv;

console.log('hello ', argv.n);

En el código anterior, el parámetro n siempre devuelve un valor booleano; el uso es el siguiente.

$ hello
hello  false
$ hello -n
hello  true
$ hello -n tom
hello  true

El método boolean también se puede escribir como una propiedad dentro del objeto option.

#!/usr/bin/env node
var argv = require('yargs')
  .option('n', {
    boolean: true
  })
  .argv;

console.log('hello ', argv.n);

7. Información de ayuda

El módulo yargs proporciona los siguientes métodos para generar información de ayuda.

  • usage: formato de uso
  • example: proporciona ejemplos
  • help: muestra información de ayuda
  • epilog: aparece al final de la información de ayuda
#!/usr/bin/env node
var argv = require('yargs')
  .option('f', {
    alias : 'name',
    demand: true,
    default: 'tom',
    describe: 'your name',
    type: 'string'
  })
  .usage('Usage: hello [options]')
  .example('hello -n tom', 'say hello to Tom')
  .help('h')
  .alias('h', 'help')
  .epilog('copyright 2015')
  .argv;

console.log('hello ', argv.n);

El resultado de la ejecución es el siguiente.

$ hello -h

Usage: hello [options]

Options:
  -f, --name  your name [string] [required] [default: "tom"]
  -h, --help  Show help [boolean]

Examples:
  hello -n tom  say hello to Tom

copyright 2015

8. Subcomandos

El módulo yargs también permite configurar subcomandos al estilo Git mediante el método command.

#!/usr/bin/env node
var argv = require('yargs')
  .command("morning", "good morning", function (yargs) {
    console.log("Good Morning");
  })
  .command("evening", "good evening", function (yargs) {
    console.log("Good Evening");
  })
  .argv;

console.log('hello ', argv.n);

El uso es el siguiente.

$ hello morning -n tom
Good Morning
hello tom

Se puede combinar esta funcionalidad con el módulo shellojs.

#!/usr/bin/env node
require('shelljs/global');
var argv = require('yargs')
  .command("morning", "good morning", function (yargs) {
    echo("Good Morning");
  })
  .command("evening", "good evening", function (yargs) {
    echo("Good Evening");
  })
  .argv;

console.log('hello ', argv.n);

Cada subcomando suele tener sus propios parámetros; en este caso, es necesario especificarlos por separado en la función de devolución de llamada. Dentro de la función de devolución de llamada, primero se debe usar el método reset para restablecer el objeto yargs.

#!/usr/bin/env node
require('shelljs/global');
var argv = require('yargs')
  .command("morning", "good morning", function (yargs) {  
    echo("Good Morning");
    var argv = yargs.reset()
      .option("m", {
        alias: "message",
        description: "provide any sentence"
      })
      .help("h")
      .alias("h", "help")
      .argv;

    echo(argv.m);
  })
  .argv;

El uso es el siguiente.

$ hello morning -m "Are you hungry?"
Good Morning
Are you hungry?

9. Otros asuntos

(1) Valor de retorno

Según la tradición de Unix, un programa devuelve 0 si se ejecuta correctamente; de lo contrario, devuelve 1.

if (err) {
  process.exit(1);
} else {
  process.exit(0);
}

(2) Redirección

Unix permite redirigir datos entre programas mediante tuberías.

$ ps aux | grep 'node'

El script puede obtener los datos redirigidos escuchando el evento data de la entrada estándar.

process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(data) {
  process.stdout.write(data);
});

El uso es el siguiente:

$ echo 'foo' | ./hello
hello foo

(3) Señales del sistema

El sistema operativo puede enviar señales a los procesos en ejecución; el objeto process puede escuchar los eventos de señal.

process.on('SIGINT', function () {
  console.log('Got a SIGINT');
  process.exit(0);
});

El método para enviar señales es el siguiente.

$ kill -s SIGINT [process_id]

Fuente original: http://www.ruanyifeng.com/blog/2015/05/command-line-with-node.html