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

本文大部分是参考阮一峰的文章,只是额外记录在windows下尝试的笔记。

原文地址:http://www.ruanyifeng.com/blog/2015/05/command-line-with-node.html

我在windows系统试了下,有些地方感觉和原文不一样,不知道是不是自己理解不到位,还是因为系统不同的缘故。

我的nodejs安装目录是D:\nodejs,在下面创建了个项目test。把hello.js放在D:\nodejs\test

一、可执行脚本(Window和Linux有差异)

1)步骤

首先,使用 JavaScript 语言,写一个可执行脚本 hello.js 。(#!/usr/bin/env node 用来)

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

现在,hello 就可以执行了。进入到test文件夹,输入以下命令:

(原文中写的是直接输入./hello,我尝试那样做,但是是直接在浏览器打开一个文件,把js内容直接输出。)

node hello
执行结果如下:


如果想把 hello 前面的路径去除,可以将 hello 的路径加入环境变量 PATH。但是,另一种更好的做法,是在当前目录(D:\nodejs\test)下新建 package.json ,写入下面的内容。

{
  "name": "hello",
  "bin": {
    "hello": "hello.js" //原文中写的是"hello": "hello"
  }
}

然后执行npm link,如下图所示


执行完后,会在{prefix}/node_modules下生成test文件夹的快捷方式({prefix}是指全局路径,我的全局路径是D:\nodejs\node_global)。另外也会生成hello.cmd、hello.sh放到{prefix}文件夹下,因此在任意路径输入hello都能够执行脚本,如下图所示:


package.json的bin路径原文中写的是"hello": "hello",我尝试了下,发现会有错误,错误提示如下:


2)#!/usr/bin/env node

#!/usr/bin/env node是Unix和Linux脚本语言的第一行,目的就是指出,你想要你的这个文件中的代码用什么可执行程序去运行它。除了#!/usr/bin/env node,还有!/usr/bin/node
!/usr/bin/node是告诉操作系统执行这个脚本的时候,调用/usr/bin下的node解释器;
!/usr/bin/env node这种用法是为了防止操作系统用户没有将node装在默认的/usr/bin路径里。当系统看到这一行的时候,首先会到env设置里查找node的安装路径,再调用对应路径下的解释器程序完成操作。
!/usr/bin/node相当于写死了node路径;
!/usr/bin/env node会去环境设置寻找node目录,推荐这种写法
思考:这个在Windows系统上能起作用吗?是不是就是因为这一行在Windows系统上不起作用导致我直接执行./hello时没有效果?

二、命令行参数的原始写法(Window和Linux没差异

命令行参数可以用系统变量 process.argv 获取。

下面是一个脚本 hello 。 

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

执行时,直接在脚本文件后面,加上参数即可。用法如下:

node hello tom

结果如下:

上面代码中, process.argv 是 ['node', '/path/to/hello', 'tom'] 。

三、新建进程(Window和Linux没差别)

脚本可以通过 child_process 模块新建子进程,从而执行系统命令。

#!/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);
});

用法如下:

node hello tom

结果如下:

child_process详细学习可参考:nodejs中文网的http://nodejs.cn/api/child_process.html

四、shelljs 模块(Window和Linux没差异)

shelljs 模块重新包装了 child_process,调用系统命令更加方便。它需要安装后使用。

npm install --save shelljs

shelljs会被安装到项目目录(D:\nodejs\test)下的node_moudles文件夹。

然后,改写脚本。

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

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

上面代码是 shelljs 的本地模式,即通过 exec 方法执行 shell 命令。此外还有全局模式,允许直接在脚本中写 shell 命令。

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);
}

shelljs的详细学习可参考:https://www.npmjs.com/package/shelljs
五、yargs 模块(Window和Linux没差别)

shelljs 只解决了如何调用 shell 命令,而 yargs 模块能够解决如何处理命令行参数。它也需要安装。

npm install --save yargs


yargs 模块提供 argv 对象,用来读取命令行参数。请看改写后的 hello :

#!/usr/bin/env node
var argv = require('yargs').argv;
console.log('process.argv:', process.argv);
console.log('yargs argv: ', argv);
console.log('argv.name:', argv.name);

使用时,下面两种用法都可以。

node hello --name=tom
或 node hello --name tom

从上图中可以看到process.argv 的原始返回值是一个数组。而yargs将结果改为对象,每个参数项就是一个键值对。因此可以直接通过require('yargs').argv.name来访问name参数。

如果将 argv.name 改成 argv.n,就可以使用一个字母的短参数形式了。用法如下:
node hello -n tom

可以使用 alias 方法,指定 name 是 n 的别名。

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

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

这样一来,短参数和长参数就都可以使用了。(短参数-n或--n,长参数-name或--name好像都是可以访问到对应变量。但--后面一般跟长的完整名字,-后面一般跟简写大多数是一个字母)。效果如下图:


argv 对象有一个下划线(_)属性,可以获取非连词线开头的参数。

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

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

用法如下:

node hello A -n tom B C

效果如下图:


六、命令行参数的配置(Window和Linux没差别)

yargs 模块还提供3个方法,用来配置命令行参数

demand:是否必选
default:默认值
describe:提示
#!/usr/bin/env node
var argv = require('yargs')
  .demand(['n'])
  .default({n: 'tom'})
  .describe({n: 'your name'})
  .argv;

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

上面代码指定 n 参数不可省略,默认值为 tom,并给出一行提示。输入node hello --help时,会出现这些信息:


options 方法允许将所有这些配置写进一个对象。

有时,某些参数不需要值,只起到一个开关作用,这时可以用 boolean 方法指定这些参数返回布尔值。

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

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

上面代码中,参数 n 总是返回一个布尔值,用法如下:


boolean 方法也可以作为属性,写入 option 对象。

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

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

七、帮助信息(Window和Linux没差别)

yargs 模块提供以下方法,生成帮助信息。

usage:用法格式
example:提供例子
help:显示帮助信息
epilog:出现在帮助信息的结尾
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);

执行结果如下:


八、子命令(Window和Linux没差别)

yargs 模块还允许通过 command 方法,设置 Git 风格的子命令。

#!/usr/bin/env node
var yargs = require('yargs')
  .command("morning", "good morning", function (yargs) {
    console.log("Good Morning");
  })
  .command("evening", "good evening", function (yargs) {
    console.log("Good Evening");
  })
var argv = yargs.argv; //必须要访问argv属性,或通过parse()解析参数,前面的命令才会得到执行
console.log('hello ', argv.n);
用法如下:


可以将这个功能与 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;
echo("hello", argv.n);

每个子命令往往有自己的参数,这时就需要在回调函数中单独指定。回调函数中,要先用 reset 方法重置 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;

echo(argv.m); //和morning命令内部获取的值一样

用法如下:

node hello morning -m "Are you hungry?"

注意-m后面的字符串要用双引号,如果是单引号或不加,只能获取到Are。

效果如下图:


虽然在命令morning内部和外部都能获取m参数,但是加上morning命令时,对命令的解析会有区别。


九、其他事项

1)重定向

Unix 允许程序之间使用管道重定向数据(Windows也可以)。

重定向描述:
假设您想要一张 images 目录中所有以 .png 结尾的文件[6]列表。该列表非常长,因此您会想把它先放到一个文件中,然后在有空的时候查看。您可以输入下述命令:
$ ls images/*.png 1>file_list
这表示把该命令的标准输出(1)重定向到(>)file_list 文件。其中的 > 操作符是输出重定向符。如果要重定向到的文件不存在,它将被创建;不过如果它已经存在,那么它先前的内容将被覆盖。不过,该操作符默认的描述符就是标准输出,因此就不用在命令行上特意指出。所以,上述命令可以简化为:

管道描述:

管道是指把一个命令的输出作为下一个命令的输入。管道在某种程度上是输入和输出重定向的结合。其原理同物理管道类似:一个进程向管道的一端发送数据,而另一个进程从该管道的另一端读取数据。

脚本可以通过监听标准输入的data 事件,获取重定向的数据。

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

下面是用法。

echo Tom Zhang | node hello

2)系统信号

操作系统可以向执行中的进程发送信号,process 对象能够监听信号事件。

process.on('SIGINT', function() {
    console.log('收到 SIGINT 信号。');
});

console.log('试着按下 ctrl + C');
/*为了避免执行完后退出当前脚本,加了个定时任务*/
setTimeout(function() {
    console.log('end');
}, 50000);

输入node hello后,效果如下:


我在命令行窗口按下了Ctrl + C,收到信息“收到SIGINT信号”。如下图:



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值