{"id":26235978,"url":"https://github.com/helti/node-cli","last_synced_at":"2025-03-13T03:19:12.615Z","repository":{"id":43481831,"uuid":"188694932","full_name":"HelTi/node-cli","owner":"HelTi","description":"node-cli,use commander.js,commander.js example,inquirer.js example","archived":false,"fork":false,"pushed_at":"2022-12-07T08:55:45.000Z","size":171,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-14T07:47:27.986Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HelTi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-26T14:41:58.000Z","updated_at":"2024-04-14T07:47:27.987Z","dependencies_parsed_at":"2023-01-24T15:31:02.390Z","dependency_job_id":null,"html_url":"https://github.com/HelTi/node-cli","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelTi%2Fnode-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelTi%2Fnode-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelTi%2Fnode-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelTi%2Fnode-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HelTi","download_url":"https://codeload.github.com/HelTi/node-cli/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243330720,"owners_count":20274112,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-13T03:19:12.163Z","updated_at":"2025-03-13T03:19:12.608Z","avatar_url":"https://github.com/HelTi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# node 命令行教程\n\n本文先介绍原生的node.js实现命令行交互，了解原生的api,然后通过commander.js和inquirer.js实现一个完整的交互命令行工具。\n[项目地址](https://github.com/HelTi/node-cli)\n## process （进程）\nprocess对象是一个全局变量，它提供了当前node.js进程的信息并对其控制。因为其是一个全局变量所以无需在文件中引入。\n\n**需要用到的几个api**\n\n- process.argv\n- process.cwd()\n- process.stdin\n- process.stdout\n- process.stdin.resume()\n\n### process.argv\nprocess.argv属性返回一个数组。数组的第一个值是process.execPath,第二个是正在执行的JavaScript的文件路径，其余参数为其它命令参数，这是我们来自定义命令的关键。\n\n#### 示例\n\n**新建argv.js**\n\n```js\n//  argv.js\nconsole.log(process.argv)\n\n```\n\n**执行node命令 node argv.js**\n\n```shell\nnode argv.js --name zhu\n## 输出\n[ '/usr/local/bin/node', ## 执行当前脚本的Node二进制文件的绝对路径\n  '/Users/zhuhuilong/Node/Book/argv.js', ## 文件的绝对路径\n  '--name', ## 其余参数\n  'zhu' ]\n```\n\n**接收自定义的命令参数进行处理输出**\n\n```js\n//  argv.js\nconsole.log(process.argv)\n\nlet argvs = process.argv\n\nlet param = argvs.splice(2)\nif(param[0] \u0026\u0026 param[0] == '--name'){\n  if(param[1]){\n    console.log(`hello ${param[1]}`)\n  }else{\n    console.log('请输入name')\n  }\n}\n```\n\n**\u003e运行argv.js**\n\n```shell\nnode argv.js --name zhu\n## 输出\n[ '/usr/local/bin/node',\n  '/Users/zhuhuilong/Node/Book/argv.js',\n  '--name',\n  'zhu' ]\nhello zhu\nparam [ '--name', 'zhu' ]\n```\n\n### process.stdin与process.stdout\n\n**process.stdin**(标准输入)\n\nprocess.stdin 属性返回连接到 stdin (fd 0) 的流。 它是一个 net.Socket 流（也就是双工流），除非 fd 0 指向一个文件，在这种情况下它是一个可读流。\n\n**process.stdout**(标准输出)\n\nprocess.stdout 属性返回连接到 stdout (fd 1) 的流。 它是一个 net.Socket 流（也就是双工流），除非 fd 1 指向一个文件，在这种情况下它是一个可写流。\n\n### process.stdin.resume()\n- 一个指向 标准输入流(stdin) 的可读流(Readable Stream)。标准输入流默认是暂停 (pause) 的，所以必须要调用 process.stdin.resume() 来恢复 (resume) 接收。\n\n- 作为流，process.stdin可以在旧模式下使用。为了兼容node v0.10以前的版本。在旧模式喜爱使用stdin必须调用process.stdin.resume()。注意如果调用了process.stdin.resume() stdin将转为旧模式。\n\n通俗来讲就是控制台等待我们输入内容不退出进程，对输入输出进行交互。\n\n**新建inputout.js**\n\n```js\n// inputout.js\nprocess.stdin.setEncoding('utf8')\n\nlet argvs = process.argv\n\nlet param = argvs.splice(2)\nif (param[0] \u0026\u0026 param[0] == '--name') {\n  if (param[1]) {\n    console.log(`hello ${param[1]}`)\n  } else {\n    process.stdout.write(`请输入name:`)\n    process.stdin.resume()\n    process.stdin.on('data', chunk =\u003e {\n      if (!!chunk.replace(/[\\r\\n]/g, '')) {\n        process.stdout.write(`你输入的name是: ${chunk}`)\n        process.stdin.emit('end')\n      } else {\n        process.stdout.write(`请输入name:`)\n      }\n    })\n  }\n}\n\nprocess.stdin.on('end', () =\u003e {\n  process.stdout.write('结束\\n')\n})\n\n```\n\n**\u003e执行node inputout.js --name**\n![](20190508223732.jpg)\n![](20190508223850.jpg)\n\n#### 备注\n在新版本node模式下可以使用process.stdin.on(\"readable\",()=\u003e{})代替process.stdin.resume()恢复输入流接收。\n\n示例：\n\n```js\nprocess.stdin.on(\"readable\", () =\u003e {\n  var chunk = process.stdin.read();\n  console.log(typeof(chunk))\n  if (chunk !==null) {\n    process.stdout.write(`data: ${chunk}`);\n    process.stdin.emit(\"end\");\n  }\n});\n\nprocess.stdin.on(\"end\", () =\u003e {\n  process.stdout.write(\"end\");\n});\n```\n\n从上面的示例我们可以拿到process.argv参数对其进行处理交互，但如果要实现更复杂的命令交互，使用上面的方法会很吃力。下面我们使用commander.js和inquirer来实现一个完整的node命令行工具(创建项目模版)。\n\n## commander.js\n\nnode.js命令行界面的完整解决方案，受Ruby Commander启发。\n\n### commander.js的API简述\n\n**program.version() 声明版本**\n\n```js\nconst program = require('commander')\nconst pkg = require('../package.json')\nprogram.version(pkg.version)\n```\n**Options 解析**\n\n使用.option()方法定义commander的选项options，也可以作为选项的文档。\n\n```js\nvar program = require('commander');\n \nprogram\n  .version('0.1.0')\n  .option('-p, --peppers', 'Add peppers')\n  .option('-P, --pineapple', 'Add pineapple')\n  .option('-b, --bbq-sauce', 'Add bbq sauce')\n  .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')\n  .parse(process.argv);\n \nconsole.log('you ordered a pizza with:');\nif (program.peppers) console.log('  - peppers');\nif (program.pineapple) console.log('  - pineapple');\nif (program.bbqSauce) console.log('  - bbq');\nconsole.log('  - %s cheese', program.cheese);\n```\n\n**添加自定义命令program.command()**\n\n```js\nvar program = require('commander');\n \nprogram\n  .command('rm \u003cdir\u003e') //\u003c\u003e必选参数，如果是[]则是可选参数\n  .option('-r, --recursive', 'Remove recursively')\n  .action(function (dir, cmd) {\n    console.log('remove ' + dir + (cmd.recursive ? ' recursively' : ''))\n  })\n \nprogram.parse(process.argv)\n\n// command()可变参数\n/** 命令command有且只有最后一个参数可变不固定的。 要使参数变量可变，必须将...附加到参数名称。**/\nprogram\n  .version('0.1.0')\n  .command('rmdir \u003cdir\u003e [otherDirs...]')\n  .action(function (dir, otherDirs) {\n    console.log('rmdir %s', dir);\n    if (otherDirs) {\n      otherDirs.forEach(function (oDir) {\n        console.log('rmdir %s', oDir);\n      });\n    }\n  });\n \nprogram.parse(process.argv);\n\n```\n\n**program.action() 定义命令的回调函数**\n\n```js\nvar program = require(\"commander\");\n\nprogram\n  .command(\"rm \u003cdir\u003e\")\n  .option(\"-r, --recursive\", \"Remove recursively\")\n  .option(\"-f, --force\", \"remove force\")\n  .action(function(dir, cmd) {\n    // cmd为option参数选项\n    //console.log('cmd',cmd)\n    if (cmd.recursive) {\n      console.log(\"remove \" + dir + \" recursively\");\n    }\n    if (cmd.force) {\n      console.log(\"remove \" + dir + \" forcefully\");\n    }\n  });\n\nprogram.parse(process.argv);\n```\n\n## inquirer.js\n[Inquirer.js](https://www.npmjs.com/package/inquirer)使用NodeJs做的一个通用交互式命令行用户界面的集合。具有常用的控制台交互操作。\n\n![](https://github.com/LitoMore/inquirer-chalk-pipe/raw/master/screenshot.gif)\n\n由于交互的问题种类不同，inquirer为每个问题提供很多参数：\n\n- type：表示提问的类型，包括：input, confirm, list, rawlist, expand, checkbox, password, editor；\n- name: 存储当前问题回答的变量；\n- message：问题的描述；\n- default：默认值；\n- choices：列表选项，在某些type下可用，并且包含一个分隔符(separator)；\n- validate：对用户的回答进行校验；\n- filter：对用户的回答进行过滤处理，返回处理后的值；\n- transformer：对用户回答的显示效果进行处理(如：修改回答的字体或背景颜色)，但不会影响最终的答案的内容；\n- when：根据前面问题的回答，判断当前问题是否需要被回答；\n- pageSize：修改某些type类型下的渲染行数；\n- prefix：修改message默认前缀；\n- suffix：修改message默认后缀。\n\n### 创建cli.js\n\n```js\nconst program = require('commander')\nconst inquirer = require('inquirer')\nconst fs = require('fs')\nconst path = require('path')\nconst pkg = require('../package.json')\n\nconst CWD = process.cwd()\n\nconst promptList = [\n  {\n    type: 'list',\n    message: '请选择一种模版',\n    name: 'template',\n    choices: ['vue', 'angular', 'webpack-m-pages'],\n    filter: function(val) {\n      return val.toLowerCase()\n    }\n  }\n]\n\nprogram\n  .version(pkg.version)\n  .command('create \u003cdir\u003e')\n  .description('create project template')\n  .action(function(dir, cmd) {\n    const TEMPLATE_PATH = path.join(CWD, dir)\n    if (fs.existsSync(TEMPLATE_PATH)) {\n    } else {\n      fs.mkdirSync(TEMPLATE_PATH)\n    }\n    if (dir) {\n      inquirer.prompt(promptList).then(anwsers =\u003e {\n        console.log(anwsers)\n      })\n    }\n  })\n\nprogram.parse(process.argv)\n```\n运行 node cli/cli.js create vue\n![](WX20190526-221811@2x.png)\n\n已经可以运行了，我们自定义一个命名替代每次都执行node\n\n命令为：test-cli create \u003cdir\u003e\n\n1、创建bin文件夹，在bin文件夹下创建index.js文件\n\n```js\n#!/usr/bin/env node\n\nrequire('../cli/cli')\n```\n\n2、修改package.json文件\n添加bin选项\n```json\n\"bin\": {\n    \"test-cli\": \"./bin/index.js\"\n  },\n```\n\n3、执行npm link (如果没有权限，执行sudo npm link)\n\n4、测试\n![](WX20190526-223440@2x.png)\n\n5、发布 npm publish (如果未登录需先 npm login登录)\n\n6、发布完毕，需npm unlink解除本地的命令映射\nnpm install -g XXX\n\n\n\n\n\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhelti%2Fnode-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhelti%2Fnode-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhelti%2Fnode-cli/lists"}