如何使用Nodejs开发交互式命令行程序
命令行程序,也就是通过文本在终端中与程序进行交互 比如我们常常使用的create-react-app命令,会生成一个最基本的react项目;执行webpack命令会给项目进行打包处理,执行npm install XXX 会安装package; 为什么只要在终端输入一行字符就可以实现这么强大的功能呢?Nodejs命令行到底是如何工作的?
解释器
在终端可以调用不同的解释器来执行你的文件,比如运用sh解释器来执行一个.sh文件,我们都知道执行npm init之后会在项目的根目录下生成一个.git文件夹,.git文件夹下的hooks目录则是一系列的.sh文件,这一系列的sh文件则会在git各生命周期被触发,我们随意改写一个文件,并运用sh解释器来执行它
给pre-commit.sh新增一行echo语句
#!/bin/sh
echo 'hello wuwenjie'
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=$(git hash-object -t tree /dev/null)
fi
在终端执行sh pre-commit.sh
则会在终端输出 hello wuwenjie
node则是用来执行js文件的解释器,当使用node来执行一个js文件的时候,程序将会去系统的环境变量中寻找node命令,找到之后便使用系统中的node命令执行文件。 新建一个hello-world.js的文件,并用node指令来执行它
// hello-world.js
console.log('hello world');
$ node hello-world.js
hello world
这样做还是比较麻烦,因为每次都得输入node hello-world.js才可以执行你的文件,有没有什么方法可以在省略node的情况下程序依然可以被执行,比如在终端输入./hello-world.js
,也可以打印出hello world
。
可执行脚本
如果想实现在终端输入./hello-world.js,也可以打印出hello world,那么方法就是在文件的第一行加上#!/usr/bin/env node
, 该行代码由两部分组成:即 Shebang 和 解释器命令。Shebang 就是开头的#!
,它告诉系统调用后面声明的解释器,而我们需要调用的解释器是node。通过/usr/bin/env node
可以寻找到系统环境变量中的node命令。env
的作用是为了解决不同系统中node命令存放的地方不一致的问题,可以通过env
动态查询。
在./hello-world.js
文件的第一行加上#!/usr/bin/env node
之后,我们再来看看终端输出的是不是我们所预期的hello world
加上#!/usr/bin/env node
之后,我们可以通过省略node来执行文件,但是这其实还是很不方便的,因为每次都要进入文件的系统目录下执行命令,有什么方法可以让程序的可执行文件全局可用,应该很容易想到一种方法,就是将其加入到系统 的环境变量中,然而另外一种更加简便的方法则是通过软链接的方式。
软链接
软链接是Linux系统下的名词,在Linux中,链接分为两种,即硬链接和软链接。
硬连接指的是一块数据有不同的名字,在读写的时候本质上是访问的同一块数据。在Linux系统中,多个文件名指向同一块数据是被允许的,其作用可以防止文件被误删。硬链接在删除的时候只是删除了一个名字,当所有的名字都被删除之后,数据才会消失。
软链接有点类似于Windows系统中的快捷方式,是一种特殊文本文件。它其实是包含了指向源文件的路径,独立于源文件存在。创建了一个文件的软链接之后,执行这个文件的软链,接,实际上会进入源文件的系统路径,执行源文件。也有点类似于C语言的指针。 删除软链接之后源文件不会受到影响,但是删除源文件之后,软链接就是一个空壳。
执行命令ln -s [源文件或目录] [目标文件或目录]
,为 [源文件或目录]创建软链接,将其链到[目标文件或目录];
我们在 /Users/wuwenjie2001/Desktop
目录下创建一个hello-world.js
文件,并写入以下代码
#!/usr/bin/env node
console.log('hello world ');
在终端执行ln -s ~/Desktop/hello-world.js /usr/local/bin/hello-world
再执行hello-world
到现在已经达到了我们想要的效果,即在系统中的任何地方打开终端,输入hello-world,控制台都会输出 hello world; 但还是有一点不好的地方就是,通过ln -s创建软链接的时候,源文件和目标文件的路径都必须是绝对路径,虽然可以通过pwd可以获取文件的绝对路径,但依然还是有点繁琐的。
那么有什么方法可以解决这个问题呢?
npm link
npm link命令可以将一个任意位置的文件链接到全局执行环境,从而在任意位置使用命令行都可以直接运行该文件 简单的讲,npm link 做了两件事情(以linux系统为例)
- 为可执行文件创建软链接,将其链接到{prefix}/bin/{name}
- 再创建之后的软链接所在目录创建软链接,将其链接到{prefix}/lib/node_modules/{package}
不过要使npm link起作用的话,必须先使用npm init 初始化项目; 我们新建一个项目,并用npm init 初始化它
$ mkdir hello-world
cd hello-world
npm init -y
执行之后,目录中生成了package.json文件,用编辑器打开它,其内容为:
{
"name": "hello-world",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
在package.json添加
"bin": {
"hello-world": "bin/index.js"
}
,表示npm会在全局环境创建一个名为hello-world的软链接,软链接的源文件为该项目下的bin/index.js
,在bin/index.js文件中输入以下内容,并执行npm link
#!/usr/bin/env node
console.log('hello world ');
此时,在任意一个位置执行以下命令都可以看到输出hello world 总结一下,npm link命令通过链接目录和可执行文件,实现npm包命令的全局可执行。
到这一步,我们已经可以写出一个简单的命令行程序了
命令行参数
在网页应用里,我们依靠 URL 来获得展现页面内容所需要的参数,而命令行程序所依靠的便是命令行参数。以hello-world为例,假设我们需要在控制台打印n次hello world,而n是用户输入的。比如我们想输出3个hello world,我们会可能这么做hello-world 3
,那么如何在命令行程序里拿到这个4呢?
我们都知道,有一个系统变量process.argv,该变量的第二个值就是命令行参数,改写bin/index.js文件
#!/usr/bin/env node
console.log(process.argv);
const times = process.argv[2] || 1;
console.log('hello world '.repeat(times));
然后在终端可以看到期望的结果:
process.argv
是实际执行的命令参数列表(数组),第一个参数是解释器命令 node所在路径,第二个是被执行文件的软链接所在路径,第三个参数则是命令行参数,也正是我们需要的。
之后你便可以灵活地通过 argv 来判断如何输出用户期望的内容了。
这篇文章的标题是如何使用Nodejs开发交互式命令行程序,除去交互式功能,我们已经可以使用Nodejs开发命令行程序了,那么如何给命令行程序添加交互式功能呢?
inquire模块
inquirer努力打造一个易于嵌入和外形美观的node命令行界面,它提供了用户界面和查询会话流程,借助inquirer这个模块就能轻松实现命令行的交互式功能,它的语法如下(来自官方网站)
npm install inquirer
var inquirer = require('inquirer');
inquirer
.prompt([
/* Pass your questions in here */
])
.then(answers => {
// Use user feedback for... whatever!!
});
inquirer功能简介
- input–输入
- validate–验证
- list–列表选项
- confirm–提示
- checkbox–复选框等等
这些功能具体要怎么实现可以移步官方网站
我们改写一下bin/index.js文件,看看列表选项功能具体要怎么实现
#!/usr/bin/env node
const inquirer = require('inquirer');
const prompt = inquirer.createPromptModule();
// 选择需要在哪个项目中创建模版文件
const choiceProject = () => {
return new Promise((resolve) =>{
prompt({
type: 'list',
message: '请选择项目',
name: 'projectName',
choices: [
"xiaomai-miniapp-show",
"xiaomai-web-b-new",
"xiaomai-web-parents",
"xiaomai-template-template",
]
}).then((answer) => {
console.log(answer.projectName)
});
});
};
choiceProject();
在终端执行命令hello-world
,可以看到
了解到inquirer模块之后,我们可以使用Nodejs开发交互式命令行程序了。最后,本文内容也就到此结束了,希望这篇文章可以帮助到你!