如何使用 Node.js 开发交互式命令行应用程序 已翻译 100%

oschina 投递于 2017/04/01 16:53 (共 17 段, 翻译完成于 04-06)
阅读 3443
收藏 79
4
加载中

在过去五年中,Node.js 使软件开发统一起来。您可以用Node.js做任何你能想到的事情,无论是前端开发,服务器端脚本,跨平台桌面应用程序,跨平台移动应用程序,物联网等。编写命令行工具也比以往任何时候都更容易,因为Node.js —— 不仅是一个命令行工具,它还具有交互式,易用和开发耗时少等特点。

如果您是前端开发人员,那么您肯定听说过或者用过 GulpAngular CLICordovaYeoman 等。你有没有想过他们如何工作的?例如,就Angular CLI来说,通过运行诸如ng new <project-name>命令就能创建一个具有基本配置的Angular新项目。而 Yeoman 等工具也要求运行时输入最后帮助您自定义项目的配置。Yeoman中的一些生成器可帮助你将项目发布到生产环境中。这就是我们今天要学习的。

总长
总长
翻译于 2017/04/01 19:12
1

在 SMASHINGMAG 上的相关阅读:

我们会在本教程中开发一个命令行程序,它会接收一个包含客户信息的 CSV 文件,然后使用 SendGrid API 向这些客户发送电子邮件。下面是这篇教程的目录:

  1. “Hello, World”

  2. 处理命令行参数

  3. 运行时用户输入

  4. 异步网络通信

  5. 美化 CLI 输出

  6. 使其成为 shell 命令

  7. JavaScript 之外

Viyi
Viyi
翻译于 2017/04/02 10:20
0

“Hello, World”

本教程假设你已经安装了 Node.js。如果你还没有安装,请先安装。Node.js 带有一个叫 npm 的包管理器。使用 npm,你可以安装很多开源的包。在npm 的官方网站可以看到这些包的完整列表。我们这个项目中会用到许多开源模块(稍后会用到更多)。现在先使用 npm 创建一个 Node.js 项目。

$ npm init
name: broadcast
version: 0.0.1
description: CLI utility to broadcast emails
entry point: broadcast.js

这里我创建了一个名为 broadcast 的目录,并在其中运行了 npm init 命令。然后提供了一些项目的基本信息,比如名称(name)、说明(description)、版本(version)和入口(entry point)。入口是指执行脚本时最开始执行的 JavaScript 文件。默认情况下,Node.js 把 index.js 作为入口;不过在这个示例中,我们把入口改为 broadcast.js 了。运行 npm init 命令的时候,你会看到更多选项,比如 Git 库、许可证和作者。你可以输入某些值,也可以留空不填。

Viyi
Viyi
翻译于 2017/04/02 10:29
0

成功执行 npm init 之后你会发现当前目录下出现了一个 package.json 文件。这是我们的配置文件。现在,它保存着我们创建项目的时候输入的信息。如果你想更详细的了解 package.json,可以看看 npm 的文档

现在我们已经建立了项目,接下来开始写 “Hello World” 程序。先在项目中创建 broadcast.js 文件,它会是你的主文件。在文件中输入如下这段代码:

console.log('hello world');

运行这段代码。

$ node broadcast
hello world

你可以看到控制台输出 “hello world”。你可以通过 node broadcast.js 来运行,也可以通过 node broadcast 来运行。Node.js 能够识别这两种方式。

在 package.json 的文档中,提到了一个名为 dependencies 的选项。在这个选项中你可以申明想用于本项目的所有第三方模块以及它们的版本号。我曾提到我们会在开发这个工具的过程中使用很多第三方开源模块,因此,我们的 package.json 看起来是这样的:

{
  "name": "broadcast",
  "version": "0.0.1",
  "description": "CLI utility to broadcast emails",
  "main": "broadcast.js",
  "license": "MIT",
  "dependencies": {
    "async": "^2.1.4",
    "chalk": "^1.1.3",
    "commander": "^2.9.0",
    "csv": "^1.1.0",
    "inquirer": "^2.0.0",
    "sendgrid": "^4.7.1"
  }}

我们会使用 AsyncChalkCommanderCSVInquirer.js 和 SendGrid,在教程进行的过程中,我们会逐步讲解这些模块的用法。

Viyi
Viyi
翻译于 2017/04/02 10:38
0

处理命令行参数

获取命令行参数并不困难,只需要使用 process.argv 就可以了。但解析这些参数的值和选项可是件累人的活。所以,与其重复发明轮子,不如直接用 Commander 模块。Commander 是个开源的 Node.js 模块,用于帮助我们写可交互的命令行工具。它带来了很多用于解析命令行选项的特性,而且还支持 git 风格的子命令。我最喜欢的 Commander 特性是自动生成到屏幕的帮助信息。你不需要写多余的代码 —— 只需要解析 --help 或 -h 选项就可以了。你在定义各种命令行选项的时候,它会自动生成 --help 帮助信息。让我们用一下看看:

$ npm install commander --save

这行命令会在你的 Node.js 项目中安装 Commander 模块。在 npm 命令中使用 --save 选项会自动将 Commander 包含在项目的依赖项(dependencies)中,上面曾提到它在 package.json 中定义。在我们这个示例中,所有依赖项都已经申明了,所以我们不需要运行这个命令。

Viyi
Viyi
翻译于 2017/04/02 10:51
0
var program = require('commander');
program
  .version('0.0.1')
  .option('-l, --list [list]', 'list of customers in CSV file')
  .parse(process.argv)
console.log(program.list);

可以看到,处理命令行参数很简单。我们已经完成了对 --list 选项的定义。现在只要是在 --list 选项之后的值都会保存在括号里申明的变量中——在本例中,叫 list。你可以通过 program 命令来访问它,这个 program 是 Commander 的实例。目前这个程序只是通过 --list 来接收一个文件路径然后打印到控制台。

$ node broadcast --list input/employees.csv
input/employees.csv

你一定也注意到了我们调用方法链中的一个方法,version。如果我们在运行命令的时候使用 --version 或 -V 作为参数,就会打印出作为参数转入到这个方法的值。

$ node broadcast --version
0.0.1
Viyi
Viyi
翻译于 2017/04/02 10:59
0

同样,如果运行命令的时候给出 --help 选项,它会打印出所有定义好的选项和子命令。本例中的输出是这样的:

$ node broadcast --help

  Usage: broadcast [options]

  Options:

    -h, --help                 output usage information
    -V, --version              output the version number
    -l, --list <list>          list of customers in CSV file

现在我们已经可以从命令行参数中接收到文件路径,接下来开始使用 CSV 模块读取 CSV 文件。CSV 模块提供处理 CSV 文件的全面解决方案。从创建 CSV 文件到进行解析,都可以用这个模块来实现。

因为我们打算使用 SendGrid API 来发送电子邮件,所以用下面这个文档来作为 CSV 文件的示例。我们会使用 CSV 模块来读取数据,并显示出相应行中的姓名和电子邮件地址。

First nameLast name                Email
DwightSchrutedwight.schrute@dundermifflin.com
JimHalpert jim.halpert@dundermifflin.com
PamBeeslypam.beesly@dundermifflin.com
RyanHowardryan.howard@dundermifflin.com
StanleyHudsonstanley.hudson@dundermifflin.com
Viyi
Viyi
翻译于 2017/04/02 11:37
0

现在让我们来写一段程序,从 CSV 文件中读取数据并打印到控制台。

const program = require('commander');
const csv = require('csv');
const fs = require('fs');
program
  .version('0.0.1')
  .option('-l, --list [list]', 'List of customers in CSV')
  .parse(process.argv)
let parse = csv.parse;
let stream = fs.createReadStream(program.list)
    .pipe(parse({ delimiter : ',' }));
stream
  .on('data', function (data) {
    let firstname = data[0];
    let lastname = data[1];
    let email = data[2];
    console.log(firstname, lastname, email);
  });

我们可以使用原生的文件系统(File System)模块读取从命令行参数获取的文件。文件系统模块中预定义了事件,其中 data 事件会在读取到一块数据时触发。CSV 模块提供的 parse 方法能将 CSV 文件拆分行,并多次触发 data 事件。每个 data 事件都会发送一个数组,包含每一列的数据。那么,在本例中会按下面的格式打印数据:

$ node broadcast --list input/employees.csv
Dwight Schrute dwight.schrute@dundermifflin.com
Jim Halpert jim.halpert@dundermifflin.com
Pam Beesly pam.beesly@dundermifflin.com
Ryan Howard ryan.howard@dundermifflin.com
Stanley Hudson stanley.hudson@dundermifflin.com
Viyi
Viyi
翻译于 2017/04/02 12:10
0

运行时用户输入

我们已经知道如何接收命令行参数并解析它们。但是如果想在运行时获取输入该怎么办呢?Inquirer.js 模块能让我们接收不同类型的输入,包括纯文本、密码和可多选项的列表等。

在这个示例中,我们会在运行时接收用户输入的电子邮件地址和姓名。

…

let questions = [
  {
    type : "input",
    name : "sender.email",
    message : "Sender's email address - "
  },
  {
    type : "input",
    name : "sender.name",
    message : "Sender's name - "
  },
  {
    type : "input",
    name : "subject",
    message : "Subject - "
  }];

let contactList = [];
let parse = csv.parse;
let stream = fs.createReadStream(program.list)
    .pipe(parse({ delimiter : "," }));
stream
  .on("error", function (err) {
    return console.error(err.message);
  })
  .on("data", function (data) {
    let name = data[0] + " " + data[1];
    let email = data[2];
    contactList.push({ name : name, email : email });
  })
  .on("end", function () {
    inquirer.prompt(questions).then(function (answers) {
      console.log(answers);
    });
  });

首先,注意上例中我们创建了一个名为 contactList的数组,我们用它保存来自 CSV 文件的数据。

Viyi
Viyi
翻译于 2017/04/02 12:14
0

Inquirer.js 有一个叫做 prompt的方法,它的参数是一组我们想在运行时提出的问题。在本例中就是我们想知道的发送者的姓名、电子邮件地址和电子邮件的主题。我们创建了一个叫 questions 的数组,用来保存这些问题。这个数组的元素是一个包含像类型(type)这类属性的对象,类型用于标识输入的是密码还是列表,或者其它。你可以在官方文档看到完整的类型列表。这里的 name 保存了用户输入所需要保存的信息的名称。 prompt 方法返回一个 Promise 对象,根据成功或者失败会调用不同的回调函数,当然这会在用户回答完所有问题之后进行。用户的回答通过 then 回调的参数 answers  来访问。执行上述代码会得到如下结果:

$ node broadcast -l input/employees.csv
? Sender's email address -  michael.scott@dundermifflin.com
? Sender's name -  Micheal Scott
? Subject - Greetings from Dunder Mifflin
{ sender:
   { email: 'michael.scott@dundermifflin.com',
     name: 'Michael Scott' },
  subject: 'Greetings from Dunder Mifflin' }
Viyi
Viyi
翻译于 2017/04/02 13:56
0
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(3)

肖滔
肖滔
java这么多年没有做到的,js做到了
手握华为赛神仙
手握华为赛神仙

引用来自“shijunti”的评论

打听下,js怎么和系统底层交互,例如Windows10上,js启动应用程序,js监听Windows的API
监听Windows API只能用C之类的底层语言写HOOK。Nodejs可以用C写扩展,所以你懂的
shijunti
shijunti
打听下,js怎么和系统底层交互,例如Windows10上,js启动应用程序,js监听Windows的API
返回顶部
顶部