ChatGPT解决这个技术问题 Extra ChatGPT

将命令行参数发送到 npm 脚本

我的 package.jsonscripts 部分目前如下所示:

"scripts": {
    "start": "node ./script.js server"
}

...这意味着我可以运行 npm start 来启动服务器。到目前为止,一切都很好。

但是,我希望能够运行 npm start 8080 之类的东西并将参数传递给 script.js(例如 npm start 8080 => node ./script.js server 8080)。这可能吗?


j
jakub.g

npm 2 及更新版本

自 npm 2 (2014) 起,pass args to npm run 成为可能。语法如下:

npm run <command> [-- <args>]

请注意 -- 分隔符,用于分隔传递给 npm 命令本身的参数和传递给脚本的参数。

对于示例 package.json

  "scripts": {
    "grunt": "grunt",
    "server": "node server.js"
  }

以下是将参数传递给这些脚本的方法:

npm run grunt -- task:target  // invokes `grunt task:target`
npm run server -- --port=1337 // invokes `node server.js --port=1337`

注意如果您的参数不以 --- 开头,则不需要明确的 -- 分隔符;但为了清楚起见,最好还是这样做。

npm run grunt task:target     // invokes `grunt task:target`

请注意下面的行为差异(test.jsconsole.log(process.argv)):以 --- 开头的参数 被传递给 npm 而不是脚本,并且是静默的在那里吞了下去。

$ npm run test foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js',  'foobar']

$ npm run test -foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js']

$ npm run test --foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js']

$ npm run test -- foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js', 'foobar']

$ npm run test -- -foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js', '-foobar']

$ npm run test -- --foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js', '--foobar']

当您使用 npm 实际使用的参数时,差异会更加明显:

$ npm test --help      // this is disguised `npm --help test`
npm test [-- <args>]

aliases: tst, t

要获取参数值,see this question。要读取命名参数,最好使用像 yargsminimist 这样的解析库; nodejs 全局公开 process.argv,包含命令行参数值,但这是一个低级 API(空格分隔的字符串数组,由操作系统提供给节点可执行文件)。

编辑 2013.10.03: 目前无法直接进行。但是有一个相关的 GitHub issue opened on npm 可以实现您要求的行为。似乎共识是实现这一点,但这取决于之前解决的另一个问题。

原始答案(2013.01):作为某种解决方法(虽然不是很方便),您可以执行以下操作:

假设您来自 package.json 的包名称是 myPackage,并且您还有

"scripts": {
    "start": "node ./script.js server"
}

然后添加 package.json

"config": {
    "myPort": "8080"
}

在您的 script.js 中:

// defaulting to 8080 in case if script invoked not via "npm run-script" but directly
var port = process.env.npm_package_config_myPort || 8080

这样,默认情况下 npm start 将使用 8080。但是您可以对其进行配置(该值将由 npm 存储在其内部存储中):

npm config set myPackage:myPort 9090

然后,在调用 npm start 时,将使用 9090(package.json 的默认值被覆盖)。


这也与 yargs 等软件包完美配合; -- 之后的所有参数都可以在您的脚本中完美解析。
AFAIKS,这只能将参数添加到脚本的末尾。如果中间需要参数怎么办?
-- --args 天哪,这很奇怪,但没关系
@Spock您可以使用shell函数。这是我用来允许将自定义参数传递给 eslint 的 eslint+tslint 设置,例如,通过 "npm run lint -- -f unix": "lint": "f() { eslint -f codeframe $@ . && npm run tslint && echo 'lint clean!'; }; f"
设置“myPackage:myPort 9090”值的更好方法是在命令“--myPackage:myPort=9090”中使用配置标志 - keithcirkel.co.uk/how-to-use-npm-as-a-build-tool
c
cjerdonek

您要求能够运行类似 npm start 8080的东西。这是可能的,无需如下修改 script.js 或配置文件。

例如,在您的 "scripts" JSON 值中,包括--

"start": "node ./script.js server $PORT"

然后从命令行:

$ PORT=8080 npm start

我已经确认这可以使用 bash 和 npm 1.4.23。请注意,此解决方法不需要解析 GitHub npm issue #3494


这真的很好用。您还可以执行 node ./script.js server ${PORT:-8080} 之类的操作以使其成为可选。
我似乎无法使用 git bash 在 Windows 中执行此操作。有人让它工作吗? (相同的命令适用于 ubuntu)
嘿@graup这对我有用NODE_PORT=${PORT=8080}(注意相等)但不是:-语法
这不适用于跨平台!例如,在 Windows 上,该命令需要为 node ./script.js server %PORT%。考虑使用 cross-varcross-env
@JeungminOh ARG1=1 ARG2=2 ARGx=x npm start
K
Kabir Sarin

你也可以这样做:

package.json 中:

"scripts": {
    "cool": "./cool.js"
}

cool.js 中:

 console.log({ myVar: process.env.npm_config_myVar });

在 CLI 中:

npm --myVar=something run-script cool

应该输出:

{ myVar: 'something' }

更新:使用 npm 3.10.3,它似乎将 process.env.npm_config_ 变量小写?我也在使用 better-npm-run,所以我不确定这是否是普通的默认行为,但这个答案 is 有效。尝试 process.env.npm_config_myvar 而不是 process.env.npm_config_myVar


谢谢这对我有用!我特别缺少的是您在命令行中指定的变量名的“npm_config_”前缀。
这是错误的。 process.env.npm_config_myVar 返回 true,而不是值。
适用于 npm 版本 6.8.0,但仅当我使用小写作为变量名时。它看起来像 npm 将其更改为小写
很好的解决方案,在 npm 6.5.0 上使用小写参数
@K-ToxicityinSOisgrowth。如果您忘记将 = 添加到参数,就会出现这种情况。例如:npm --myVar something ... 将返回 true。但是这个: npm --myVar=something ... 将返回值本身。
s
svnm

jakub.g 的回答是正确的,但是使用 grunt 的示例似乎有点复杂。

所以我更简单的答案:

- 向 npm 脚本发送命令行参数

将命令行参数发送到 npm 脚本的语法:

npm run [command] [-- <args>]

想象一下,我们的 package.json 中有一个 npm start 任务来启动 webpack 开发服务器:

"scripts": {
  "start": "webpack-dev-server --port 5000"
},

我们使用 npm start 从命令行运行它

现在,如果我们想将一个端口传递给 npm 脚本:

"scripts": {
  "start": "webpack-dev-server --port process.env.port || 8080"
},

运行它并通过命令行传递端口,例如 5000,如下所示:

npm start --port:5000

- 使用 package.json 配置:

正如 jakub.g 所提到的,您也可以在 package.json 的配置中设置参数

"config": {
  "myPort": "5000"
}

"scripts": {
  "start": "webpack-dev-server --port process.env.npm_package_config_myPort || 8080"
},

npm start 将使用您的配置中指定的端口,或者您可以覆盖它

npm config set myPackage:myPort 3000

- 在你的 npm 脚本中设置一个参数

读取 npm 脚本中的变量集的示例。在此示例中 NODE_ENV

"scripts": {
  "start:prod": "NODE_ENV=prod node server.js",
  "start:dev": "NODE_ENV=dev node server.js"
},

在 prod 或 dev 中读取 server.js 中的 NODE_ENV

var env = process.env.NODE_ENV || 'prod'

if(env === 'dev'){
    var app = require("./serverDev.js");
} else {
    var app = require("./serverProd.js");
}

请注意,package.json 中的 "start:prod": "NODE_ENV=prod node server.js" 之类的语法在 Windows 上不起作用,除非您使用 cross-env
更正?:根据我在 this tutorial 中解释的用法,"start": "webpack-dev-server --port process.env.npm_package_config_myPort || 8080" }, 应该是 "start": "webpack-dev-server --port $npm_package_config_myPort || 8080" },。进程 ref 显然可以在 javascript 中使用。
K
KyleMit

npm 2.x 开始,您可以通过使用 -- 分隔来pass args into run-scripts

终端

npm run-script start -- --foo=3

包.json

"start": "node ./index.js"

索引.js

console.log('process.argv', process.argv);

@KyleMit:您可能是对的,尽管目前答案是多余的,即使当时可能有所不同。除了我的第一条评论,我将删除所有评论。
G
Gnucki

我过去一直在使用这种单线器,在离开 Node.js 一段时间后,最近不得不尝试重新发现它。与@francoisrv 提到的解决方案类似,它利用了 npm_config_* 变量。

创建以下最小 package.json 文件:

{
  "name": "argument",
  "version": "1.0.0",
  "scripts": {
    "argument": "echo \"The value of --foo is '${npm_config_foo}'\""
  }
}

运行以下命令:

npm run argument --foo=bar

观察以下输出:

--foo 的值为 'bar'

所有这些都很好地记录在 npm 官方文档中:

https://docs.npmjs.com/using-npm/config

注意: Environment Variables 标题说明脚本中的变量的行为与文档中定义的不同。对于区分大小写,以及参数是用空格还是等号定义,情况都是如此。

注意:如果您使用带连字符的参数,这些将在相应的环境变量中替换为下划线。例如,npm run example --foo-bar=baz 将对应于 ${npm_config_foo_bar}

注意:对于非 WSL Windows 用户,请参阅下面的 @Doctor Blue 评论... TL;DR 将 ${npm_config_foo} 替换为 %npm_config_foo%


你好。我正在尝试使用您的示例,但恐怕它对我不起作用。我复制粘贴了您的“参数”脚本,并对要运行的命令 (npm run argument --foo=bar) 执行相同操作,但未替换变量:"The value of --foo is '${npm_config_foo}'"。如果重要的话,在 Windows 10 上运行,使用 6.9.0 版的 NPM。
@DoctorBlue 啊,对,Node 和 Windows 并不总是很好......这篇文章可能会阐明 npm 脚本中的环境变量:(TL;DR 命令直接进入主机操作系统,即使从另一个 shell 启动){ 1} 我不确定您的设置,但如果您使用 Git Bash 运行 Node,您可能需要考虑通过 WSL 运行它:) docs.microsoft.com/en-us/windows/nodejs/setup-on-wsl2
我想到了。只需要改用 %npm_config_foo%。纯 Windows 命令行/powershell 在这里。 (也别无选择。)
您可以使用跨环境:npmjs.com/package/cross-env
此外,当您尝试将“v”或“版本”注入命令时要小心 - npm 认为,您正在尝试确定它自己的版本并会忽略命令输出它。但是“ver”是可以接受的,例如:)
P
Peter

在您的代码中使用 process.argv,然后只需为您的脚本值条目提供一个尾随 $*

例如,使用一个简单的脚本尝试它,该脚本只将提供的参数记录到标准输出 echoargs.js

console.log('arguments: ' + process.argv.slice(2));

包.json:

"scripts": {
    "start": "node echoargs.js $*"
}

例子:

> npm start 1 2 3
arguments: 1,2,3

process.argv[0] 是可执行文件(节点),process.argv[1] 是您的脚本。

使用 npm v5.3.0 和 node v8.4.0 测试


-- 添加到参数后不起作用,例如 - npm run demo.js --skip,如果添加额外的 --,它会起作用,例如 - npm run demo.js -- --skip
您可以在没有单独的 echoargs.js 脚本文件的情况下使用此方法吗?
@JoshuaPinter echoargs.js 仅作为示例,我将编辑我的答案以明确这一点
@Peter Right,但它必须是一个脚本文件。我正在尝试创建一个脚本,该脚本使用 adb.db 文件推送到 Android 模拟器并接受 .db 文件的本地路径的参数以推送到它,这是 { 4}。像这样:"db:push": "adb push process.argv.slice(2) /data/data/com.cntral.app/databases/database.db",我想用 npm run db:push /Users/joshuapinter/Downloads/updated.db 来调用它。有什么想法吗?
A
Aurelio

如果您想将参数传递到 npm 脚本的中间,而不是将它们附加到末尾,那么内联环境变量似乎可以很好地工作:

"scripts": {
  "dev": "BABEL_ARGS=-w npm run build && cd lib/server && nodemon index.js",
  "start": "npm run build && node lib/server/index.js",
  "build": "mkdir -p lib && babel $BABEL_ARGS -s inline --stage 0 src -d lib",
},

在这里,npm run dev-w 监视标志传递给 babel,但 npm run start 只运行一次常规构建。


这是如何从 CLI 调用的?
@dresdin npm run devnpm start
需要使用 cross-env 在 Windows 上使用它。
有什么方法可以将第一个 npm 脚本的结果存储在某个变量中并在第二个 npm 脚本中使用该变量?
A
Alexandr Priezzhev

上面的大多数答案都只是将参数传递到由 npm 调用的 NodeJS 脚本中。我的解决方案是通用的。

只需使用 shell 解释器(例如 sh)调用包装 npm 脚本并像往常一样传递参数。唯一的例外是第一个参数编号是 0

例如,您想添加 npm 脚本 someprogram --env=<argument_1>,其中 someprogram 只打印 env 参数的值:

包.json

"scripts": {
  "command": "sh -c 'someprogram --env=$0'"
}

当你运行它时:

% npm run -s command my-environment
my-environment

谢谢!这是完美的!
m
mxt3

对于 Windows 上的 PowerShell 用户

接受的答案对我来说不适用于 npm 6.14。既不添加 -- 也不包括它一次也不起作用。但是,将 -- 两次或将 "--" 一次放在参数之前就可以了。例子:

npm run <my_script> -- -- <my arguments like --this>

怀疑原因

与 bash 中一样,-- 指示 PowerShell 将所有后续参数视为文字字符串,而不是选项 (E.g see this answer)。问题似乎是该命令的解释比预期的多一次,丢失了 '--'。例如,通过做

npm run <my_script> -- --option value

npm 将运行

<my_script> value

然而,做

npm run <my_script> "--" --option value

结果是

<my_script> "--option" "value"

效果很好。


哦,天哪,你为我节省了这么多时间……这么多时间……还有头发……还有理智……还有幸福……还有理智……疯狂……自我坚持……发疯了。 ..啊啊啊啊
另一种方法是使用 --- 三斜杠
D
DrunkenBeetle

这并不能真正回答您的问题,但您始终可以使用环境变量:

"scripts": {
    "start": "PORT=3000 node server.js"
}

然后在你的 server.js 文件中:

var port = process.env.PORT || 3000;

只要您在 Unix 平台上,这很好。不幸的是,它不适用于 Windows,因为它有自己的约定。
S
Sergiy Seletskyy

当我试图通过运行 sequelize seed:generate cli 命令来解决我的问题时,我发现了这个问题:

node_modules/.bin/sequelize seed:generate --name=user

让我进入正题。我想在我的 package.json 文件中有一个简短的脚本命令并同时提供 --name 参数

经过一些实验后得到了答案。这是我在 package.json 中的命令

"scripts: {
  "seed:generate":"NODE_ENV=development node_modules/.bin/sequelize seed:generate"
}

...这是在终端中运行它以为用户生成种子文件的示例

> yarn seed:generate --name=user

> npm run seed:generate -- --name=user

供参考

yarn -v
1.6.0

npm -v
5.6.0

这是否与 2013 年接受的答案中解释的技术相同,通过 -- --arg1, ...
好的,那为什么要重复答案?
如果我想分享另一个已经在不同答案中解释过的技术示例,我会将我的示例添加为对该答案的评论。
gotcha,下次会这样做
这个答案归结为“如果你想在使用标志时避免--,请使用纱线”+1
A
Ashish Ranjan

注意:此方法会即时修改您的 package.json,如果您别无选择,请使用它。

我必须将命令行参数传递给我的脚本,例如:

"scripts": {
    "start": "npm run build && npm run watch",
    "watch": "concurrently  \"npm run watch-ts\" \"npm run watch-node\"",
    ...
}

因此,这意味着我使用 npm run start 启动我的应用程序。

现在,如果我想传递一些论点,我可能会从以下开始:

npm run start -- --config=someConfig

它的作用是:npm run build && npm run watch -- --config=someConfig。问题在于,它总是将参数附加到脚本的末尾。这意味着所有链接的脚本都没有得到这些参数(Args 可能是也可能不是所有人都需要,但那是另一回事。)。此外,当调用链接的脚本时,这些脚本将不会获得传递的参数。即watch 脚本不会得到传递的参数。

我的应用程序的生产用途是 .exe,因此在 exe 中传递参数可以正常工作,但如果想在开发过程中这样做,它会变得有问题。

我找不到任何合适的方法来实现这一点,所以这就是我尝试过的。

我创建了一个 javascript 文件:start-script.js 在应用程序的父级,我有一个“default.package.json”,而不是维护“package.json”,我维护“default.package.json”。 start-script.json 的目的是读取 default.package.json,提取 scripts 并查找 npm run scriptname,然后将传递的参数附加到这些脚本。之后,它将创建一个新的 package.json 并使用修改后的脚本从 default.package.json 复制数据,然后调用 npm run start

const fs = require('fs');
const { spawn } = require('child_process');

// open default.package.json
const defaultPackage = fs.readFileSync('./default.package.json');
try {
    const packageOb = JSON.parse(defaultPackage);
    // loop over the scripts present in this object, edit them with flags
    if ('scripts' in packageOb && process.argv.length > 2) {

        const passedFlags = ` -- ${process.argv.slice(2).join(' ')}`;
        // assuming the script names have words, : or -, modify the regex if required.
        const regexPattern = /(npm run [\w:-]*)/g;
        const scriptsWithFlags = Object.entries(packageOb.scripts).reduce((acc, [key, value]) => {
            const patternMatches = value.match(regexPattern);
            // loop over all the matched strings and attach the desired flags.
            if (patternMatches) {
                for (let eachMatchedPattern of patternMatches) {
                    const startIndex = value.indexOf(eachMatchedPattern);
                    const endIndex = startIndex + eachMatchedPattern.length;
                    // save the string which doen't fall in this matched pattern range.
                    value = value.slice(0, startIndex) + eachMatchedPattern + passedFlags + value.slice(endIndex);
                }
            }
            acc[key] = value;
            return acc;
        }, {});
        packageOb.scripts = scriptsWithFlags;
    }

    const modifiedJSON = JSON.stringify(packageOb, null, 4);
    fs.writeFileSync('./package.json', modifiedJSON);

    // now run your npm start script
    let cmd = 'npm';
    // check if this works in your OS
    if (process.platform === 'win32') {
        cmd = 'npm.cmd';    // https://github.com/nodejs/node/issues/3675
    }
    spawn(cmd, ['run', 'start'], { stdio: 'inherit' });

} catch(e) {
    console.log('Error while parsing default.package.json', e);
}

现在,我不做npm run start,而是做node start-script.js --c=somethis --r=somethingElse

初始运行看起来不错,但尚未彻底测试。如果您喜欢应用程序开发,请使用它。


Z
Zach Smith

我发现可以像向 Node.js 一样传递变量:

// index.js
console.log(process.env.TEST_ENV_VAR)
// package.json
...
"scripts": { "start": "node index.js" },
...
TEST_ENV_VAR=hello npm start

打印出“你好”


K
Kiddo

据我所知,当人们想以更简单的方式运行脚本时,他们会使用 package.json 脚本。例如,要使用安装在本地 node_modules 中的 nodemon,我们不能直接从 cli 调用 nodemon,但可以使用 ./node_modules/nodemon/nodemon.js 调用它。所以,为了简化这个冗长的输入,我们可以把这个...

...

    scripts: {
      'start': 'nodemon app.js'
    }

    ...

...然后调用 npm start 以使用以 app.js 作为第一个参数的“nodemon”。

我想说的是,如果您只想使用 node 命令启动服务器,我认为您不需要使用 scripts。键入 npm startnode app.js 具有相同的效果。

但如果您确实想要使用 nodemon,并且想要传递动态参数,也不要使用 script。尝试改用符号链接。

例如,通过 sequelize 使用迁移。我创建了一个符号链接...

ln -s node_modules/sequelize/bin/sequelize sequelize

...而且我可以通过任何争论当我称之为...

./sequlize -h /* show help */

./sequelize -m /* upgrade migration */

./sequelize -m -u /* downgrade migration */

ETC...

在这一点上,使用符号链接是我能想到的最好方法,但我并不认为这是最好的做法。

我也希望您对我的回答提出意见。


这根本不能回答问题。我不知道它是如何获得 6 票的,但恭喜 :)
f
fullstacklife

我知道已经有一个批准的答案,但我有点喜欢这种 JSON 方法。

npm start '{"PROJECT_NAME_STR":"my amazing stuff", "CRAZY_ARR":[0,7,"hungry"], "MAGICAL_NUMBER_INT": 42, "THING_BOO":true}';

通常我有 1 个我需要的 var,例如项目名称,所以我觉得这很简单。

我的 package.json 中也经常有这样的东西

"scripts": {
    "start": "NODE_ENV=development node local.js"
}

并且贪婪我想要“全部”,NODE_ENV 和 CMD 行 arg 的东西。

您只需在文件中访问这些内容(在我的情况下为 local.js)

console.log(process.env.NODE_ENV, starter_obj.CRAZY_ARR, starter_obj.PROJECT_NAME_STR, starter_obj.MAGICAL_NUMBER_INT, starter_obj.THING_BOO);

你只需要在它上面放一点(我正在运行 v10.16.0 btw)

var starter_obj = JSON.parse(JSON.parse(process.env.npm_config_argv).remain[0]);

Anyhoo,问题已经回答了。想我会分享,因为我经常使用这种方法。


从 npm v7 github.com/npm/cli/issues/1995 起,npm_config_argv 已被删除
我刚进去检查了一下,我现在碰到了 v14.9.0。它仍然对我有用。 process.env.npm_config_argv 只是未定义的“直到”您使用所有传递的信息运行 npm start。
请注意,nodejs 版本是一回事,npm 版本是另一回事。请参阅此处的表格 nodejs.org/en/download/releases 以检查哪些 nodejs 版本包含哪些 npm 版本。 Nodejs v14 仍然有 npm v6,所以这就是它适合你的原因(到目前为止,直到使用 npm v7 升级到 nodejs v15)
G
Gilbert

我解决了这样的问题,看看 test-watch 脚本:

"scripts": {
    "dev": "tsc-watch --onSuccess \"node ./dist/server.js\"",
    "test": "tsc && cross-env NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 jest",
    "test-watch": "cross-env NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 tsc-watch --onSuccess",
  },

您可以像这样调用 test-watch 脚本:

// Run all tests with odata in their name
npm run test-watch "jest odata"

S
Sunny Goel

npm run script_target -- 基本上这是传递命令行参数的方式,但它只在脚本只有一个命令运行的情况下才有效,就像我正在运行一个命令,即 npm run start -- 4200

"script":{
       "start" : "ng serve --port="
 }

这将运行以传递命令行参数,但是如果我们一起运行多个命令,例如 npm run build c:/workspace/file

"script":{
       "build" : "copy c:/file <arg> && ng build"
 } 

但它会在运行 copy c:/file && ng build c:/work space/file 时像这样解释,我们应该像这样 copy c:/file c:/work space/file && ng build

注意:-因此命令行参数仅在脚本中只有一个命令的情况下才起作用。

我读了上面的一些答案,其中一些人正在写你可以使用 $ 符号访问命令行参数,但这不会起作用


J
Junior Tour

试试 cross-env NPM package

便于使用。易于安装。跨平台。

例子:

为命令设置参数

// package.json
"scripts": {
  “test”: “node test.js”,
  “test-with-env-arg”: “cross-env YourEnvVarName=strValue yarn test,
}

从 process.env 获取参数

// test.js
const getCommandLineArg = Boolean(process.env.YourEnvVarName === 'true')  // Attention: value of process.env.* is String type, not number || boolean