如何将命令行参数传递给从可执行脚本启动的NodeJS

时间:2018-04-11 23:17:43

标签: node.js npm

如何为启动器脚本运行的NodeJS进程设置node的命令行参数? (sh / CMD脚本npm放入node_modules/.bin。)

许多NodeJS库/框架都带有自己的跑步者脚本,例如:通常从npm脚本执行的zeit/micromoleculer。这在开发中存在问题,因为在我的情况下,我想做相同的:

node --inspect -r ts-node/register -r dotenv-safe/config src/index.ts

(当然,除了index.ts之外什么也没做什么,只是为跑步者提供了一些东西。)

是否有一些“干净的”,最好是通用的(即不是特定于给定框架的运行程序暴露那些命令行参数)我不想这样做的方式,理想情况下是一个作为npm脚本工作的方式?看起来它唯一可行的是例如micro

node-dev -r ts-node/register ./node_modules/micro-dev/bin/micro-dev.js ./src/index.ts
这是冗余部门冗余部门的一种口感,似乎避免了使用这些启动脚本。 (如果运行器生成其他Node进程,它也将无效,但这不是我实际遇到的问题。)我不想重复启动器脚本已经在做什么。我也知道npx--node-arg,但是npx是另一种蠕虫病毒。 (在Windows上,它只有5秒的启动时间和一条虚假错误消息,只是为了运行我已经安装的脚本;如果它找不到它的.cmd启动程序脚本,它也找不到已经安装的包,例如当使用Docker运行开发环境时。简而言之,我宁愿不使用npx。)

要清除注释中似乎出现的混乱:我想覆盖影响 NodeJS运行时本身执行转轮脚本的行为的命令行参数,而不是传递参数脚本本身或我的代码。也就是说,此处列出的选项为https://nodejs.org/api/cli.html

3 个答案:

答案 0 :(得分:1)

我没有明确你的问题,但作为你的问题标题,我们可以使用npm库从nodejs执行任何cmd命令,如:

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

答案 1 :(得分:1)

一个选项是编写一个使用当前进程execPath运行child_process.execFile的小包装器脚本。

所以这里的样本是能够做到的

node --expose-http2 --zero-fill-buffers -r ./some-module.js ./test.js

但实际上没有写出来,而是让wrap.js注入args:

node ./wrap.js ./test.js

我测试了在package.json中通过npm运行它,它运行正常。我通过让some-module.js在全局对象上粘贴一个值,然后将其记录在test.js中来测试它的工作情况。

涉及的文件:

wrap.js

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

some-module.js

global.testval = 2;

test.js

console.log('hi guys, did the wrap work?', global.testval)
编辑:所以经过进一步思考,这个解决方案真的只能满足包装初始跑步者的需要。但是大多数工具,比如mocha重新生成一个子进程,然后会失去这种效果。要真正完成工作,您可以代理每个子进程调用,并稍微强制执行对spawn的调用,这样也包括您的args。

我重写了代码以反映这一点。这是一个新的设置:

package.json

{
  "scripts": {
    "test": "node -r ./ensure-wrapped.js node_modules/mocha/$(npm view mocha bin.mocha) ./test.js"
  },
  "dependencies": {
    "mocha": "^5.1.0"
  }
}

ensure-wrapped.js

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

test.js

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

请勿将此类代码放入已发布的节点模块中。修改全局库函数非常糟糕。

答案 2 :(得分:0)

在您的nodejs应用程序被解析为名为process.argv的数组之后,命令行中传递的所有内容。所以......

node myapp.js foo bar hello 5000

在你的nodejs代码......

const args = process.argv;
console.log(args[0]);
console.log(args[1]);
console.log(args[2]);
console.log(args[3]);

会产生......

foo
bar
hello
5000