nodejs命令行执行程序

by Peter Benjamin

彼得·本杰明(Peter Benjamin)

在NodeJS中编写命令行应用程序 (Writing Command-Line Applications in NodeJS)

With the right packages, writing command-line apps in NodeJS is a breeze.

有了合适的软件包,用NodeJS编写命令行应用程序变得轻而易举。

One package in particular makes it extremely easy: Commander.

一个软件包特别容易: Commander

Let’s set the stage and walk-through how to write a command-line interface (CLI) app in NodeJS with Commander. Our goal will be to write a CLI app to list files and directories.

让我们设置阶段并逐步讲解如何使用Commander在NodeJS中编写命令行界面(CLI)应用程序。 我们的目标是编写一个CLI应用程序以列出文件和目录。

建立 (Setup)

IDEsI love online IDEs. They abstract away a lot of headaches when it comes to dev environment setup. I personally use Cloud9 for the following reasons:

IDE我喜欢在线IDE。 在开发环境设置方面,他们消除了很多麻烦。 我个人出于以下原因使用Cloud9 :

  • The layout is intuitive布局直观
  • The editor is beautiful and easy-to-use该编辑器美观且易于使用
  • Free-tier resources have recently been increased to 1GB RAM and 5GB disk space, which is more than plenty for a decent-sized NodeJS application.自由层资源最近已增加到1GB RAM和5GB磁盘空间,对于像样的NodeJS应用程序来说已经足够了。
  • Unlimited number of workstations工作站数量不限
  • It’s a perfect environment to test or experiment with new projects/ideas without fear of breaking your environment这是测试或试验新项目/想法的理想环境,而不必担心破坏您的环境

Node/NPM VersionAt the time of writing this article, Node is at version 5.3.0 and NPM is ad version 3.3.12.

节点/ NPM版本在撰写本文时,Node的版本为5.3.0,NPM的广告版本为3.3.12。

初始化 (Initialization)

We start by initializing our project, accept all the NPM defaults, and installing the commander package:

我们首先初始化项目,接受所有NPM默认值,然后安装commander软件包:

npm initnpm i --save commander

Resulting in:

导致:

Note:

注意:

  • You will have to add bin manually, which tells NodeJS what your CLI app is called and what is the entry point to your app.

    您将必须手动添加bin ,这将告诉NodeJS您的CLI应用程序是什么以及应用程序的入口点是什么。

  • Make sure you do not use a command name that already exists in your system.确保您不使用系统中已经存在的命令名称。

Index.js (Index.js)

Now that we’ve initialized our project and indicated that our entry point is index.js, let’s create index.js:

现在我们已经初始化了项目,并指出我们的入口点是index.js,让我们创建index.js:

touch index.js

Now, for the actual coding part:

现在,对于实际的编码部分:

Typically, when executing NodeJS files, we tell the system to use the appropriate interpreter by prefixing node before the file. However, we want to be able to execute our CLI app globally from anywhere in the system, and without having to specify the node interpreter every time.

通常,在执行NodeJS文件时,我们通过在文件之前添加节点前缀来告诉系统使用适当的解释器。 但是,我们希望能够从系统中的任何位置全局执行CLI应用程序,而不必每次都指定节点解释器。

Therefore, our first line is the shebang expression:

因此,我们的第一行是shebang表达式:

#!/usr/bin/env node

This not only tells our system to use the appropriate interpreter, but it also tells our system to use the appropriate version of the interpreter.

这不仅告诉我们的系统使用适当的解释器,而且还告诉我们的系统使用适当的解释器版本

From here on out, we write pure JavaScript code.Since I’ll be writing ES6-compliant code, I’ll start with the literal expression:

从现在开始,我们将编写纯JavaScript代码。由于我将编写符合ES6的代码,因此将从文字表达式开始:

'use strict';

This tells the compiler to use a stricter variant of javascript [1] and enables us to write ES6 code on Cloud9.

这告诉编译器使用javascript [ 1 ]的更严格的变体,并使我们能够在Cloud9上编写ES6代码。

Let’s start by requiring the commander package:

让我们从要求commander包开始:

const program = require('commander');

Now, writing CLI apps with commander is simple, and the documentation is great, but I struggled with a few concepts that I will attempt to clear up here.

现在,使用Commander编写CLI应用程序是 很简单,文档也很棒,但是我在尝试一些概念时遇到了麻烦,在这里我将尝试加以澄清。

There seems to be 2 designs for CLI apps. Take ls and git for example.

CLI应用程序似乎有2种设计。 以lsgit为例。

With ls, you pass one or more flags:

使用ls ,您传递一个或多个标志:

  • ls -l

    ls -l

  • ls -al

    ls -al

With git, you pass sub-commands, but you also have some flags:

使用git ,您可以传递子命令,但也有一些标志:

  • git commit -am <message>

    git commit -am <messa ge>

  • git remote add origin <repo-url>

    git remote add origin <repo-u rl>

We will explore the flexibility Commander gives us to design both types of command-line interfaces.

我们将探索Commander给我们设计两种命令行界面的灵活性。

指挥官API (Commander API)

The Commander API is straight forward and the documentation is great.

Commander API非常简单,文档也很棒。

There are 3 basic ways we can write our program:

我们可以通过3种基本方法来编写程序:

METHOD #1: Flag-only command-line application

方法1:仅标志命令行应用程序

const program = require('commander');
program  .version('0.0.1')  .option('-o, --option','option description')  .option('-m, --more','we can have as many options as we want')  .option('-i, --input [optional]','optional user input')  .option('-I, --another-input <required>','required user input')  .parse(process.argv); // end with parse to parse through the input

Note:

注意:

  • The short-hand and long-hand options are in the same string (see the bold text in the image above)简写和长写选项都在同一字符串中(请参见上图中的粗体文本)
  • -o and -m will return boolean values when users pass them because we didn’t specify any optional or required user input.

    当用户传递它们时, -o-m将返回布尔值,因为我们没有指定任何可选必需的用户输入。

  • -i and -I will capture user input and pass the values to our CLI app.

    -i-I将捕获用户输入并将值传递到我们的CLI应用程序。

  • Any value enclosed in square brackets (e.g. [ ] ) is considered optional. User may or may not provide a value.方括号内的任何值(例如[])均视为可选值。 用户可能会或可能不会提供值。
  • Any value enclosed in angled brackets (e.g. < > ) is considered required. User must provide a value.包含在尖括号中的任何值(例如<>)都被认为是必需的。 用户必须提供一个值。

The example above allows us to implement a flag-only approach to our CLI app. Users will be expected to interact with our app like so:

上面的示例使我们可以对CLI应用程序实施仅标志方法。 预计用户将与我们的应用程序交互,如下所示:

//Examples:$ cli-app -om -I hello$ cli-app --option -i optionalValue -I requiredValue

METHOD #2: Sub-command and flag-based command-line application

方法2:子命令和基于标志的命令行应用程序

const program = require('commander');
program  .version('0.0.1')  .command('command <req> [optional]')  .description('command description')  .option('-o, --option','we can still have add'l options')  .action(function(req,optional){    console.log('.action() allows us to implement the command');    console.log('User passed %s', req);    if (optional) {      optional.forEach(function(opt){        console.log("User passed optional arguments: %s", opt);      });    }  });
program.parse(process.argv); // notice that we have to parse in a new statement.

Note:

注意:

  • If we utilize .command(‘command…’).description(‘description…’), we must utilize .action() to pass a function and execute our code based on the user’s input. (I point this out because there is an alternative method to utilize .command() that we’ll explore next.)

    如果我们使用.command('command ...')。description('description ...') ,则必须利用.action()传递函数并根据用户输入执行代码。 (我指出这一点是因为我们接下来将探讨利用.command()的另一种方法。)

  • If we utilize .command(‘command…’), we can no longer just tack on .parse(process.argv) at the end like we did in the previous example. We have to pass parse() in a new statement

    如果我们使用.command('command ...') ,那么我们将不再像前面的示例那样最后仅附加.parse(process.argv) 。 我们必须在新语句中传递parse()

Users are expected to interact with our CLI app like so:

希望用户与我们的CLI应用程序进行交互,如下所示:

//Example: $ cli-app command requiredValue -o

METHOD #3: Same as METHOD #2 above, but allows for modularized code

方法3:与上述方法2相同,但允许使用模块化代码

Finally, we don’t have to bloat our one JavaScript file with all the .command().description().action() logic. We can modularize our CLI project like so:

最后,我们不必使用所有.command()。description()。action()逻辑来膨胀一个JavaScript文件。 我们可以像这样对CLI项目进行模块化:

// file: ./cli-app/index.jsconst program = require('commander');
program.version('0.0.1').command('command <req> [optional]','command description').command('command2','command2 description').command('command3','command3 description').parse(process.argv);

Note:

注意:

  • If we utilize .command(‘command’, ‘description’) to pass in the command and the description, we can no longer have .action(). Commander will imply that we have separate files with a specific naming convention where we can handle each command. The naming convention is index-command.js, index-command2.js, index-command3.js. See examples of this on Github (specifically: pm, pm-install, pm-publish files).

    如果我们利用.command('command','description')传递命令和描述,我们将不再拥有.action()。 Commander表示我们有单独的文件,具有特定的命名约定,可以在其中处理每个命令。 命名约定为index-command.jsindex-command2.jsindex-command3.js 。 在Github上查看此示例 (具体是: pmpm-installpm-publish文件)。

  • If we take this route, we can just tack on .parse() at the end.

    如果采用这种方式,则只需在最后添加.parse()即可

回到我们的项目场景… (Back to our project scenario…)

Now that we’ve covered the basics, it’s all downhill from here. We can take a deep breath.

既然我们已经介绍了基础知识,那么一切都从这里开始。 我们可以深吸一口气。

*** SIGH ***

*** SIGH ***

All right, now the fun begins.

好吧,现在开始乐趣了。

If we recall our project scenario, we want to write a CLI app to list files and directories. So let’s start writing the code.

如果我们回想起我们的项目场景,我们想编写一个CLI应用程序以列出文件和目录。 因此,让我们开始编写代码。

We want to give the user the ability to decide if they want to see “all” files (including hidden ones) and/or if they want to see the long listing format (including the rights/permissions of the files/folders).

我们希望使用户能够决定是否要查看“所有”文件(包括隐藏文件)和/或是否要查看长列表格式(包括文件/文件夹的权限/许可)。

So, we start by writing the basic shell of our program to see our incremental progress (we will follow signature of Method #2 for the sake of the demo) :

因此,我们首先编写程序的基本外壳以查看增量进度(为了演示,我们将遵循方法2的签名):

#!/usr/bin/env node'use strict';
const program = require('commander');
program  .version('')  .command('')  .description('')  .option('','')  .option('','')  .action('');
program.parse(process.argv);

Let’s start filling the blanks:

让我们开始填补空白:

#!/usr/bin/env node'use strict';
const program = require('commander');
program  .version('0.0.1')  .command('list [directory]')  .description('List files and folders')  .option('-a, --all','List all files and folders')  .option('-l, --long','')  .action();
program.parse(process.argv);

Note:

注意:

  • We decided to name our command list.

    我们决定命名命令列表

  • Directoryargument is optional, so user can simply ignore to pass a directory, in which case we will list files/folders in current directory.

    目录 参数是可选的,因此用户可以忽略传递目录,在这种情况下,我们将在当前目录中列出文件/文件夹。

So, in our scenario, the following calls are valid:

因此,在我们的方案中,以下调用是有效的:

$ cli-app list $ cli-app list -al$ cli-app list --all$ cli-app list --long$ cli-app list /home/user -al

Now, let’s start writing code for our .action().

现在,让我们开始为.action()编写代码。

#!/usr/bin/env node'use strict';
const program = require('commander');
let listFunction = (directory,options) => {  //some code here}
program  .version('0.0.1')  ...  .action(listFunction);
program.parse(process.argv);

We are going to cheat here by using the built-in ls command that’s available in all unix-like operating systems.

我们将使用所有类unix操作系统中可用的内置ls命令来作弊。

#!/usr/bin/env node'use strict';
const program = require('commander'),      exec = require('child_process').exec;
let listFunction = (directory,options) => {const cmd = 'ls';let params = [];if (options.all) params.push('a');if (options.long) params.push('l');let fullCommand = params.length                   ? cmd + ' -' + params.join('')                  : cmdif (directory) fullCommand += ' ' + directory;
};
program  .version('0.0.1')  ...  .action(listFunction);
program.parse(process.argv);

Let’s talk reason about this code.

让我们谈谈这段代码的原因。

  1. First, we require the child_process library to execute shell commands* (*this opens up a big security risk that I will discuss later)

    首先,我们要求child_process库执行shell命令* ( * 这带来了很大的安全风险,我将在后面讨论 )

  2. We declare a constant variable that holds the root of our command我们声明一个常量变量,该变量保存命令的根
  3. We declare an array that will hold any parameters passed by the user (e.g. -a, -l)

    我们声明一个数组,该数组将保存用户传递的所有参数(例如-a-l )

  4. We check to see whether the user passed short-hand (-a) or long-hand ( — all) flags. If so, then options.all and/or options.long will evaluate to true, in which case we will push the respective command flag to our array. Our goal is to build the shell command that we will pass later to child_process.

    我们检查用户是否通过了短手( -a )或长手(— all )标志。 如果是这样,则options.all和/或options.long的评估结果为true ,在这种情况下,我们会将相应的命令标志压入数组。 我们的目标是构建将在以后传递给child_process的shell命令。

  5. We declare a new variable to hold the full shell command. If the param array contains any flags, we concatenate the flags to each other and to the root command. Otherwise, if param array is empty, then we use the root command as is.我们声明一个新变量来保存完整的shell命令。 如果param数组包含任何标志,我们将这些标志相互连接并与root命令连接。 否则,如果param数组为空,则按原样使用root命令。
  6. Finally, we check if user passed any optional directory values. If so, we concatenate it to the fully constructed command.

    最后,我们检查用户是否传递了任何可选的目录值。 如果是这样,我们将其连接到完全构造的命令。

Now, we want to execute the fully constructed command in the shell. Child_Process.exec() gives us the ability to do so and NodeJS docs give us the signature:

现在,我们要在shell中执行完全构造的命令。 Child_Process.exec()使我们能够这样做,而NodeJS文档给我们签名:

child_process.exec(command, callback(error, stdout, stderr){  //"error" will be returned if exec encountered an error.  //"stdout" will be returned if exec is successful and data is returned.  //"stderr" will be returned if the shell command encountered an error.})

So, let’s use this:

所以,让我们使用这个:

#!/usr/bin/env node'use strict';
const program = require('commander'),      exec = require('child_process').exec;
let listFunction = (directory,options) => {  const cmd = 'ls';  let params = [];  if (options.all) params.push('a');  if (options.long) params.push('l');  let fullCommand = params.length                   ? cmd + ' -' + params.join('')                  : cmd  if (directory) fullCommand += ' ' + directory;
let execCallback = (error, stdout, stderr) => {    if (error) console.log("exec error: " + error);    if (stdout) console.log("Result: " + stdout);    if (stderr) console.log("shell error: " + stderr);  };
exec(fullCommand, execCallback);
};
program  .version('0.0.1')  ...  .action(listFunction);
program.parse(process.argv);

That’s it!

而已!

Here is the gist of my sample CLI app.

这是我的示例CLI应用程序的要点 。

Of course, we can add a few niceties, like:

当然,我们可以添加一些细节,例如:

  • Coloring the output (I use the chalk library below)

    着色输出(我使用下面的粉笔库)

  • Modern CLI apps are smart enough to show the help text when a user calls the program with no parameters or arguments (much like git), so I added that functionality at the very bottom.

    现代的CLI应用程序足够聪明,可以在用户不带任何参数或参数(类似于git )的情况下调用该程序时显示帮助文本,因此我在最底部添加了该功能。

#!/usr/bin/env node'use strict';/** * Require dependencies * */const program = require('commander'),    chalk = require("chalk"),    exec = require('child_process').exec,    pkg = require('./package.json');/** * list function definition * */let list = (directory,options)  => {    const cmd = 'ls';    let params = [];        if (options.all) params.push("a");    if (options.long) params.push("l");    let parameterizedCommand = params.length                                 ? cmd + ' -' + params.join('')                                 : cmd ;    if (directory) parameterizedCommand += ' ' + directory ;        let output = (error, stdout, stderr) => {        if (error) console.log(chalk.red.bold.underline("exec error:") + error);        if (stdout) console.log(chalk.green.bold.underline("Result:") + stdout);        if (stderr) console.log(chalk.red("Error: ") + stderr);    };        exec(parameterizedCommand,output);    };program    .version(pkg.version)    .command('list [directory]')    .option('-a, --all', 'List all')    .option('-l, --long','Long list format')    .action(list);program.parse(process.argv);// if program was called with no arguments, show help.if (program.args.length === 0) program.help();

Finally, we can take advantage of NPM to symbolic link our CLI application so we can use it globally in our system. Simply, in the terminal, cd into the root of our CLI app and type:

最后,我们可以利用NPM来符号链接CLI应用程序,以便可以在系统中全局使用它。 只需在终端中,将cd插入我们的CLI应用程序的根目录,然后键入:

npm link

最后的想法和考虑 (Final thoughts & Considerations)

The code in this project is by no means the best code. I am fully aware that there is always room for improvement, so feedback is welcome!

这个项目中的代码绝不是最好的代码。 我完全意识到,总会有改进的余地,因此欢迎反馈!

Also, I want to point out a security flaw in our app. Our code does not sanitize or validate the users’ input. This violates security best practices. Consider the following scenarios where users can pass un-desired input:

另外,我想指出我们应用程序中的安全漏洞。 我们的代码不清除验证用户的输入。 这违反了安全性最佳做法。 请考虑以下情况,用户可以传递不需要的输入:

$ cli-app -al ; rm -rf /$ cli-app -al ; :(){ :|: & };:

If you want to write some code that handles this issue, or fixes any other potential issues, be sure to show us your code by leaving a comment.

如果您想编写一些处理此问题的代码,或者解决任何其他潜在问题,请确保通过评论向我们展示您的代码。

翻译自: https://www.freecodecamp.org/news/writing-command-line-applications-in-nodejs-2cf8327eee2/

nodejs命令行执行程序

nodejs命令行执行程序_在NodeJS中编写命令行应用程序相关推荐

  1. 命令逐行显示_在LoadRunner中执行命令行程序之:popen()取代system()

    >>>推荐阅读<<< 1.性能测试学习笔记-场景设计 2.性能测试的重要意义 3.性能分析流程及方法 4.应用系统性能调优之性能分析 在LoadRunner中执行命 ...

  2. sqlite多行插入_在SQLite中插入多行

    我试图在SQLite(最新版本)表中插入多行,但是会抛出一个错误 从HERE得到这个想法,这里是我的sql查询: INSERT INTO "Track" SELECT " ...

  3. python中如何输入多行字符_在python中输入多行字符串有哪些方法

    在python中输入多行字符串有哪些方法 发布时间:2020-04-11 13:46:24 来源:亿速云 阅读:39 作者:小新 今天小编给大家分享的是在python中输入多行字符串有哪些方法,很多人 ...

  4. python显示行数_在idle中如何显示行号

    展开全部 其实IDLE提供了一个显32313133353236313431303231363533e78988e69d8331333365663438示所有行和所有字符的功能. 我们打开IDLE sh ...

  5. excel 多行插入_在Excel中插入多行

    excel 多行插入 If you've used Excel for a while, you have lots of skills that you might assume everyone ...

  6. 如何在Go中编写多行字符串?

    本文翻译自:How do you write multiline strings in Go? Does Go have anything similar to Python's multiline ...

  7. 图形界限命令在命令行输入_设置图形界限的命令为在命令行输入

    [填空题]CAD 的工作界面主要包括标题栏.下拉菜单. . . .状态栏以及属性栏. [判断题]当图形界限检查功能设置为 ON 时,若输入或拾取的点坐标超出绘图界限,则操作将无法进行. [单选题]患儿 ...

  8. linux命令行运行c程序,如何在Linux中编写和运行C程序

    Linux正在成为开发人员的编程天堂,成为开源和免费操作系统. Turbo C编译器已经是一种编译程序的旧方法,所以让程序员转向Linux以获得新的编程环境. 在本文中,我们将解释如何编写,编译和运行 ...

  9. nodejs 获取cpu核心数量_用 NodeJS 充分利用多核 CPU 的资源[每日前端夜话0xCB]

    每日前端夜话0xCA 每日前端夜话,陪你聊前端. 每天晚上18:00准时推送. 正文共:1558 字 预计阅读时间:7 分钟 作者:Nick Major 翻译:疯狂的技术宅 来源:coderrocke ...

最新文章

  1. P1502 窗口的星星 离散化+扫描线
  2. json数组 js html标签,js定义json对象数组 json 数组也是数组 //
  3. ReactOS 0.0.21从开机启动,到加载驱动的过程
  4. 最小二乘拟合,L1、L2正则化约束--转
  5. 【计算机网络】Session机制
  6. 反思代码优化点:充分使用错误处理机制
  7. js 获取浏览器高度和宽度值
  8. 使用Jupter Notebook实现简单的神经网络
  9. centos java 路径_CentOS JAVA安装及查看路径方法
  10. aix curl 安装_命令行下载工具(Curl)
  11. 台式计算机配置单,最新台式电脑组装配置推荐
  12. Marlin固件学习总结(一)
  13. 高性能分布式游戏服务器框架
  14. 常见设计模式笔试面试题
  15. 怎么把线稿提取出来_ps怎么把彩色图片提取线稿出来,就黑白线稿的那种 要详细!...
  16. html文档注释多行,css注释的写法(单行和多行)
  17. 无线传感器网络(期末重点)
  18. 机器学习基础、sklearn数据集、转换器与预估器
  19. 无线蓝牙模块在汽车DSP的应用
  20. 关于点进Steam页面白屏解决办法

热门文章

  1. gcc是java的什么意思_为什么gcc支持Java而不是C#
  2. 基于ssm框架和freemarker的商品销售系统
  3. CentOS6 下Samba服务器的安装与配置
  4. 数据库的数据类型及运算符
  5. 用过C#的朋友可能认为它是一种十分安全的语言,其实C#也可以做到经典的缓冲区溢出。 本文章将用一个实例来描述C#究竟是如何发生缓冲区溢出的! 首先建立一个C# Console工程,并开启工程的“允许
  6. activiti 部署流程图后中文乱码
  7. 内置的常用协议实现模版
  8. 如何使用HTTP压缩优化服务器
  9. linux安装lrzsz,并使用rz sz 命令
  10. Mongodb 安装和启动