How to pass command line arguments to NodeJS launched from an executable script
对于从启动程序脚本运行的NodeJS进程,如何将命令行参数设置为
大量的NodeJS库/框架都带有自己的运行脚本,例如通常从
1 | node --inspect -r ts-node/register -r dotenv-safe/config src/index.ts |
(当然,除了index.ts只是导出一些内容供跑步者使用外,它什么也不做。)
是否有某些我不希望这样做的"干净"方式,最好是通用方式(即不特定于给定框架的运行器公开那些命令行参数),而理想情况下,它可以用作npm脚本?似乎可行的唯一方法是例如
1 | node-dev -r ts-node/register ./node_modules/micro-dev/bin/micro-dev.js ./src/index.ts |
冗余部的冗余部有点令人mouth舌,并且似乎消除了拥有这些启动器脚本的意义。 (如果运行程序生成其他Node进程,它也将不起作用,但这并不是我真正遇到的问题。)我不想不必重复启动程序脚本已经在做的事情。我也知道
为了消除似乎在注释中出现的混乱:我想覆盖影响NodeJS运行时自身执行运行脚本的行为的命令行参数,而不是将参数传递给脚本本身或我的代码。即,此处列出的选项:https://nodejs.org/api/cli.html
一种选择是编写一个小的包装脚本,该脚本使用当前进程execPath来运行child_process.execFile。
所以这里的示例是能够做到的
但实际上并没有写出来,而是让wrap.js注入了args:
我测试了通过package.json中的npm运行它,它工作正常。我通过让
涉及的文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | const child_process = require('child_process'); const nodeArgs = ['--expose-http2', '--zero-fill-buffers', '-r', './some-module.js']; const runTarget = process.argv[2]; console.log('going to wrap', runTarget, 'with', nodeArgs); const finalArgs = nodeArgs.concat(runTarget).concat(process.argv.slice(2)); const child = child_process.execFile( process.execPath, finalArgs, { env: process.env, cwd: process.cwd(), stdio: 'inherit' }, (e, stdout, stderr) => { console.log('process completed'); if (e) { process.emit('uncaughtException', e); } }); child.stdout.pipe(process.stdout); child.stderr.pipe(process.stderr); |
和
1 | global.testval = 2; |
和
1 | console.log('hi guys, did the wrap work?', global.testval) |
编辑:因此,经过进一步思考,此解决方案实际上仅满足包??装初始跑步者的要求。但是大多数工具(例如,摩卡咖啡)会重新生成一个子进程,这将失去这种效果。要真正完成这项工作,您可以代理每个子流程调用,并在某种程度上强制执行对
我重写了代码以反映这一点。这是一个新设置:
1 2 3 4 5 6 7 8 | { "scripts": { "test":"node -r ./ensure-wrapped.js node_modules/mocha/$(npm view mocha bin.mocha) ./test.js" }, "dependencies": { "mocha":"^5.1.0" } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | const child_process = require('child_process'); // up here we can require code or do whatever we want; global.testvalue = 'hi there' const customParams = ['--zero-fill-buffers']; // the code below injects itself into any child process's spawn/fork/exec calls // so that it propogates const matchNodeRe = /((:?\s|^|\/)node(:?(:?\.exe)|(:?\.js)|(:?\s+)|$))/; const ensureWrappedLocation = __filename; const injectArgsAndAddToParamsIfPathMatchesNode = (cmd, args, params) => { params.unshift(...customParams); params.unshift(args); if (!Array.isArray(args)) { // all child_proc functions do [] optionally, then other params args = [] params.unshift(args); } if (!matchNodeRe.test(cmd)) { return params; } args.unshift(ensureWrappedLocation); args.unshift('-r'); return params; } child_process._exec = child_process.exec; child_process.exec = (cmd, ...params) => { // replace node.js node.exe or /path/to/node to inject -r ensure-wrapped.js ...args.. // leaves alone exec if it isn't calling node cmd = cmd.replace(matchNodeRe, '$1 -r ' + ensureWrappedLocation + ' '); return child_process._exec(cmd, ...params) } child_process._execFile = child_process.execFile; child_process.execFile = (path, args, ...params) => { params = injectArgsAndAddToParamsIfPathMatchesNode(path, args, params); return child_process._execFile(path, ...params) } child_process._execFileSync = child_process.execFileSync; child_process.execFileSync = (path, args, ...params) => { params = injectArgsAndAddToParamsIfPathMatchesNode(path, args, params); return child_process._execFileSync(path, ...params); } child_process._execSync = child_process.execSync; child_process.execSync = (cmd, ...params) => { cmd = cmd.replace(matchNodeRe, '$1 -r ' + ensureWrappedLocation + ' '); return child_process._exec(bin, ...args) } child_process._fork = child_process.fork; child_process.fork = (module, args, ...params) => { params = injectArgsAndAddToParamsIfPathMatchesNode(process.execPath, args, params); return child_process._fork(module, ...params); } child_process._spawn = child_process.spawn; child_process.spawn = (cmd, args, ...params) => { params = injectArgsAndAddToParamsIfPathMatchesNode(cmd, args, params); return child_process._spawn(cmd, ...params) } child_process._spawnSync = child_process.spawnSync; child_process.spawnSync = (cmd, args, ...params) => { params = injectArgsAndAddToParamsIfPathMatchesNode(cmd, args, params); return child_process._spawnSync(cmd, ...params); } |
1 2 3 4 5 6 7 8 | describe('test', () => { it('should have the global value pulled in by some-module.js', (done) => { if (global.testvalue !== 'hi there') { done(new Error('test value was not globally set')) } return done(); }) }) |
请不要将这样的代码放入已发布的节点模块中。修改全局库函数非常糟糕。
我没有清楚了解您的问题的情况,但是作为您的问题标题,我们可以使用npm库从nodejs执行任何cmd命令,例如:
1 2 3 4 5 6 7 8 9 10 | import Promise from 'bluebird' import cmd from 'node-cmd' const getAsync = Promise.promisify(cmd.get, { multiArgs: true, context: cmd }) getAsync('node -v').then(data => { console.log('cmd data', data) }).catch(err => { console.log('cmd err', err) }) |
在您的nodejs应用程序之后,在命令行中传递的所有内容都将解析为一个名为process.argv的数组。所以...
1 | node myapp.js foo bar hello 5000 |
在您的nodejs代码中...
1 2 3 4 5 | const args = process.argv; console.log(args[0]); console.log(args[1]); console.log(args[2]); console.log(args[3]); |
会产生...
1 2 3 4 | foo bar hello 5000 |