使用 Node.js 进行 Shell 脚本编程
您可以购买本书的离线版本(HTML、PDF、EPUB、MOBI),并支持免费在线版本。
(广告,请不要屏蔽。)

15 通过 npm 包脚本运行跨平台任务



package.json 具有属性 "scripts",它允许我们定义*包脚本*,这些小型 Shell 脚本执行与包相关的任务,例如编译工件或运行测试。本章解释了它们,以及我们如何编写它们,以便它们在 Windows 和 Unix(macOS、Linux 等)上都能工作。

15.1 npm 包脚本

*npm 包脚本*通过 package.json 的属性 "scripts" 定义

{
  ···
  "scripts": {
    "tsc": "tsc",
    "tscwatch": "tsc --watch",
    "tscclean": "shx rm -rf ./dist/*"
  },
  ···
}

"scripts" 的值是一个对象,其中每个属性定义一个包脚本

如果我们输入

npm run <script-name>

那么 npm 会在 Shell 中执行名称为 script-name 的脚本。例如,我们可以使用

npm run tscwatch

在 Shell 中运行以下命令

tsc --watch

在本章中,我们偶尔会使用 npm 选项 -s,它是 --silent 的缩写,它告诉 npm run 减少输出

npm -s run <script-name>

此选项在关于日志记录的部分中有更详细的介绍。

15.1.1 用于运行包脚本的更短 npm 命令

某些包脚本可以通过更短的 npm 命令运行

命令 等效
npm testnpm t npm run test
npm start npm run start
npm stop npm run stop
npm restart npm run restart

15.1.2 用于运行包脚本的 Shell

默认情况下,npm 在 Windows 上通过 cmd.exe 运行包脚本,在 Unix 上通过 /bin/sh 运行包脚本。我们可以通过npm 配置设置 script-shell 更改它。

但是,这样做很少是一个好主意:许多现有的跨平台脚本都是为 shcmd.exe 编写的,并且会停止工作。

15.1.3 防止包脚本自动运行

某些脚本名称是为*生命周期脚本*保留的,每当我们执行某些 npm 命令时,npm 都会运行这些脚本。

例如,每当我们执行 npm install(不带参数)时,npm 都会运行脚本 "postinstall"生命周期脚本将在后面详细介绍。

如果配置设置 ignore-scriptstrue,则 npm 永远不会自动运行脚本,只有在我们直接调用它们时才会运行。

15.1.4 在 Unix 上获取包脚本的 Tab 自动补全

在 Unix 上,npm 通过npm completion 支持命令和包脚本名称的 Tab 自动补全。我们可以通过在 .profile / .zprofile / .bash_profile / 等文件中添加以下行来安装它。

. <(npm completion)

如果您需要非 Unix 平台的 Tab 自动补全,请执行网络搜索,例如“npm tab completion PowerShell”。

15.1.5 列出和组织包脚本

不带名称的 npm run 将列出可用的脚本。如果存在以下脚本

"scripts": {
  "tsc": "tsc",
  "tscwatch": "tsc --watch",
  "serve": "serve ./site/"
}

那么它们将像这样列出

% npm run
Scripts available via `npm run-script`:
  tsc
    tsc
  tscwatch
    tsc --watch
  serve
    serve ./site/
15.1.5.1 添加分隔符

如果有很多包脚本,我们可以将脚本名称误用作分隔符(脚本 "help" 将在下一小节中解释)

  "scripts": {
    "help": "scripts-help -w 40",
    "\n========== Building ==========": "",
    "tsc": "tsc",
    "tscwatch": "tsc --watch",
    "\n========== Serving ==========": "",
    "serve": "serve ./site/"
  },

现在脚本如下所示

% npm run
Scripts available via `npm run-script`:
  help
    scripts-help -w 40

========== Building ==========

  tsc
    tsc
  tscwatch
    tsc --watch
  
========== Serving ==========

  serve
    serve ./site/

请注意,预先添加换行符 (\n) 的技巧在 Unix 和 Windows 上都有效。

15.1.5.2 打印帮助信息

包脚本 "help" 通过@rauschma/scripts-help 中的 bin 脚本 scripts-help 打印帮助信息。我们通过 package.json 属性 "scripts-help" 提供描述("tscwatch" 的值被缩写以适应一行)

"scripts-help": {
  "tsc": "Compile the TypeScript to JavaScript.",
  "tscwatch": "Watch the TypeScript source code [...]",
  "serve": "Serve the generated website via a local server."
}

这就是帮助信息的样子

% npm -s run help
Package “demo”

╔══════╤══════════════════════════╗
║ help │ scripts-help -w 40       ║
╚══════╧══════════════════════════╝

Building

╔══════════╤══════════════════════════════════════════╗
║ tsc      │ Compile the TypeScript to JavaScript.    ║
╟──────────┼──────────────────────────────────────────╢
║ tscwatch │ Watch the TypeScript source code and     ║
║          │ compile it incrementally when and if     ║
║          │ there are changes.                       ║
╚══════════╧══════════════════════════════════════════╝

Serving

╔═══════╤══════════════════════════════════════════╗
║ serve │ Serve the generated website via a local  ║
║       │ server.                                  ║
╚═══════╧══════════════════════════════════════════╝

15.2 包脚本的种类

如果某些名称用于脚本,则它们会在某些情况下自动运行

所有其他脚本都称为*直接运行脚本*。

15.2.1 前置和后置脚本

每当 npm 运行包脚本 PS 时,它都会自动运行以下脚本(如果它们存在)

以下脚本包含前置脚本 prehello 和后置脚本 posthello

"scripts": {
  "hello": "echo hello",
  "prehello": "echo BEFORE",
  "posthello": "echo AFTER"
},

如果我们运行 hello,就会发生这种情况

% npm -s run hello
BEFORE
hello
AFTER

15.2.2 生命周期脚本

npm 在 npm 命令期间运行*生命周期脚本*,例如

如果任何生命周期脚本失败,则整个命令将立即停止并报错。

生命周期脚本的用例是什么?

这些是最重要的生命周期脚本(有关所有生命周期脚本的详细信息,请参阅npm 文档

下表总结了这些生命周期脚本的运行时间

prepublishOnly prepack prepare install
npm publish
npm pack
npm install
全局安装
通过 git、路径安装

**注意:**自动执行操作总是有点棘手。我通常遵循以下规则

15.3 运行包脚本的 Shell 环境

在本节中,我们将偶尔使用

node -p <expr>

它运行 expr 中的 JavaScript 代码并将结果打印到终端 - 例如

% node -p "'hello everyone!'.toUpperCase()" 
HELLO EVERYONE!

15.3.1 当前目录

当包脚本运行时,当前目录始终是包目录,与我们在其根目录树中的位置无关。我们可以通过将以下脚本添加到 package.json 来确认这一点

"cwd": "node -p \"process.cwd()\""

让我们在 Unix 上试用 cwd

% cd /Users/robin/new-package/src/util 
% npm -s run cwd
/Users/robin/new-package

以这种方式更改当前目录有助于编写包脚本,因为我们可以使用相对于包目录的路径。

15.3.2 Shell PATH

当一个模块 M 从一个以包 P 的名称开头的说明符导入模块时,Node.js 会遍历 node_modules 目录,直到找到 P 的目录。

也就是说,M 继承了其祖先目录的 node_modules 目录。

类似的继承也发生在 bin 脚本上,当我们安装一个包时,这些脚本存储在 node_modules/.bin 中。npm run 会临时将条目添加到 shell PATH 变量中(Unix 上为 $PATH,Windows 上为 %Path%)。

要查看这些添加的内容,我们可以使用以下包脚本:

"bin-dirs": "node -p \"JS\""

JS 代表包含以下 JavaScript 代码的单行:

(process.env.PATH ?? process.env.Path)
.split(path.delimiter)
.filter(p => p.includes('.bin'))

在 Unix 上,如果我们运行 bin-dirs,我们会得到以下输出:

% npm -s run bin-dirs
[
  '/Users/robin/new-package/node_modules/.bin',
  '/Users/robin/node_modules/.bin',
  '/Users/node_modules/.bin',
  '/node_modules/.bin'
]

在 Windows 上,我们会得到:

>npm -s run bin-dirs
[
  'C:\\Users\\charlie\\new-package\\node_modules\\.bin',
  'C:\\Users\\charlie\\node_modules\\.bin',
  'C:\\Users\\node_modules\\.bin',
  'C:\\node_modules\\.bin'
]

15.4 在包脚本中使用环境变量

在 Make、Grunt 和 Gulp 等任务运行器中,变量很重要,因为它们有助于减少冗余。然而,虽然包脚本没有自己的变量,但我们可以使用_环境变量_(也称为_shell 变量_)来解决这个问题。

我们可以使用以下命令列出特定于平台的环境变量:

在 macOS 上,结果如下所示:

TERM_PROGRAM=Apple_Terminal
SHELL=/bin/zsh
TMPDIR=/var/folders/ph/sz0384m11vxf5byk12fzjms40000gn/T/
USER=robin
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
PWD=/Users/robin/new-package
HOME=/Users/robin
LOGNAME=robin
···

在 Windows 命令行中,结果如下所示:

Path=C:\Windows;C:\Users\charlie\AppData\Roaming\npm;···
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
PROMPT=$P$G
TEMP=C:\Users\charlie\AppData\Local\Temp
TMP=C:\Users\charlie\AppData\Local\Temp
USERNAME=charlie
USERPROFILE=C:\Users\charlie
···

此外,npm 会在运行包脚本之前临时添加更多环境变量。要查看最终结果,我们可以使用以下命令:

npm run env

此命令调用内置的包脚本。让我们用这个 package.json 来试试:

{
  "name": "@my-scope/new-package",
  "version": "1.0.0",
  "bin": {
    "hello": "./hello.mjs"
  },
  "config": {
    "stringProp": "yes",
    "arrayProp": ["a", "b", "c"],
    "objectProp": {
      "one": 1,
      "two": 2
    }
  }
}

所有 npm 临时变量的名称都以 npm_ 开头。让我们按字母顺序只打印这些变量:

npm run env | grep npm_ | sort

npm_ 变量具有层次结构。在 npm_lifecycle_ 下,我们可以找到当前正在运行的包脚本的名称和定义:

npm_lifecycle_event: 'env',
npm_lifecycle_script: 'env',

在 Windows 上,npm_lifecycle_script 在这种情况下将是 SET

npm_config_ 前缀下,我们可以看到一些 npm 的配置设置(在 npm 文档中有描述)。以下是一些示例:

npm_config_cache: '/Users/robin/.npm',
npm_config_global_prefix: '/usr/local',
npm_config_globalconfig: '/usr/local/etc/npmrc',
npm_config_local_prefix: '/Users/robin/new-package',
npm_config_prefix: '/usr/local'
npm_config_user_agent: 'npm/8.15.0 node/v18.7.0 darwin arm64 workspaces/false',
npm_config_userconfig: '/Users/robin/.npmrc',

前缀 npm_package_ 允许我们访问 package.json 的内容。它的顶层如下所示:

npm_package_json: '/Users/robin/new-package/package.json',
npm_package_name: '@my-scope/new-package',
npm_package_version: '1.0.0',

npm_package_bin_ 下,我们可以找到 package.json 属性 "bin" 的属性:

npm_package_bin_hello: 'hello.mjs',

npm_package_config_ 条目允许我们访问 "config" 的属性:

npm_package_config_arrayProp: 'a\n\nb\n\nc',
npm_package_config_objectProp_one: '1',
npm_package_config_objectProp_two: '2',
npm_package_config_stringProp: 'yes',

这意味着 "config" 允许我们设置可以在包脚本中使用的变量。下一小节将对此进行更深入的探讨。

请注意,对象已转换为“嵌套”条目(第 2 行和第 3 行),而数组(第 1 行)和数字(第 2 行和第 3 行)已转换为字符串。

以下是剩余的 npm_ 环境变量:

npm_command: 'run-script',
npm_execpath: '/usr/local/lib/node_modules/npm/bin/npm-cli.js',
npm_node_execpath: '/usr/local/bin/node',

15.4.1 获取和设置环境变量

以下 package.json 演示了如何在包脚本中访问通过 "config" 定义的变量:

{
  "scripts": {
    "hi:unix": "echo $​npm_package_config_hi",
    "hi:windows": "echo %​npm_package_config_hi%"
  },
  "config": {
    "hi": "HELLO"
  }
}

遗憾的是,没有内置的跨平台方法可以从包脚本访问环境变量。

但是,有一些带有 bin 脚本的包可以帮助我们。

env-var 允许我们获取环境变量。

"scripts": {
  "hi": "env-var echo {{npm_package_config_hi}}"
}

cross-env 允许我们设置环境变量。

"scripts": {
  "build": "cross-env FIRST=one SECOND=two node ./build.mjs"
}

15.4.2 通过 .env 文件设置环境变量

还有一些包允许我们通过 .env 文件设置环境变量。这些文件具有以下格式:

# Comment
SECRET_HOST="https://example.com"
SECRET_KEY="123456789" # another comment

使用与 package.json 分开的文 件可以让我们将这些数据保留在版本控制之外。

以下是一些支持 .env 文件的包:

15.5 包脚本的参数

让我们探讨如何将参数传递给通过包脚本调用的 shell 命令。我们将使用以下 package.json

{
  ···
  "scripts": {
    "args": "log-args"
  },
  "dependencies": {
    "log-args": "^1.0.0"
  }
}

bin 脚本 log-args 如下所示:

for (const [key,value] of Object.entries(process.env)) {
  if (key.startsWith('npm_config_arg')) {
    console.log(`${key}=${JSON.stringify(value)}`);
  }
}
console.log(process.argv.slice(2));

位置参数按预期工作:

% npm -s run args three positional arguments
[ 'three', 'positional', 'arguments' ]

npm run 会消耗选项并为其创建环境变量。它们不会添加到 process.argv 中。

% npm -s run args --arg1='first arg' --arg2='second arg'
npm_config_arg2="second arg"
npm_config_arg1="first arg"
[]

如果我们希望选项出现在 process.argv 中,则必须使用_选项终止符_ --。该终止符通常插入在包脚本名称之后:

% npm -s run args -- --arg1='first arg' --arg2='second arg' 
[ '--arg1=first arg', '--arg2=second arg' ]

但我们也可以将其插入在该名称之前:

% npm -s run -- args --arg1='first arg' --arg2='second arg' 
[ '--arg1=first arg', '--arg2=second arg' ]

15.6 npm 日志级别(产生多少输出)

npm 支持以下日志级别:

日志级别 npm 选项 别名
silent --loglevel silent -s --silent
error --loglevel error
warn --loglevel warn -q --quiet
notice --loglevel notice
http --loglevel http
timing --loglevel timing
info --loglevel info -d
verbose --loglevel verbose -dd --verbose
silly --loglevel silly -ddd

日志记录指的是两种活动:

以下小节描述了:

15.6.1 日志级别和打印到终端的信息

默认情况下,包脚本在终端输出方面相对详细。以以下 package.json 文件为例:

{
  "name": "@my-scope/new-package",
  "version": "1.0.0",
  "scripts": {
    "hello": "echo Hello",
    "err": "more does-not-exist.txt"
  },
  ···
}

如果日志级别高于 silent 并且包脚本退出时没有错误,则会发生以下情况:

% npm run hello

> @my-scope/[email protected] hello
> echo Hello

Hello

如果日志级别高于 silent 并且包脚本失败,则会发生以下情况:

% npm run err      

> @my-scope/[email protected] err
> more does-not-exist.txt

does-not-exist.txt: No such file or directory

使用日志级别 silent,输出会变得不那么混乱:

% npm -s run hello
Hello

% npm -s run err
does-not-exist.txt: No such file or directory

某些错误会被 -s 吞没。

% npm -s run abc
%

我们需要至少 error 日志级别才能看到它们:

% npm --loglevel error run abc
npm ERR! Missing script: "abc"
npm ERR! 
npm ERR! To see a list of scripts, run:
npm ERR!   npm run

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/robin/.npm/_logs/2072-08-30T14_59_40_474Z-debug-0.log

遗憾的是,日志级别 silent 也会抑制 npm run(不带参数)的输出。

% npm -s run
%

15.6.2 日志级别和写入 npm 日志的信息

默认情况下,日志会写入 npm 缓存目录,我们可以通过 npm config 获取其路径:

% npm config get cache
/Users/robin/.npm

日志目录的内容如下所示:

% ls -1 /Users/robin/.npm/_logs
2072-08-28T11_44_38_499Z-debug-0.log
2072-08-28T11_45_45_703Z-debug-0.log
2072-08-28T11_52_04_345Z-debug-0.log

日志中的每一行都以行索引和日志级别开头。这是一个使用日志级别 notice 编写的日志示例。有趣的是,即使是比 notice“更详细”的日志级别(例如 silly)也会出现在其中:

0 verbose cli /usr/local/bin/node /usr/local/bin/npm
1 info using [email protected]
···
33 silly logfile done cleaning log files
34 timing command:run Completed in 9ms
···

如果 npm run 返回错误,则相应的日志将如下所示结尾:

34 timing command:run Completed in 7ms
35 verbose exit 1
36 timing npm Completed in 28ms
37 verbose code 1

如果没有错误,则相应的日志将如下所示结尾:

34 timing command:run Completed in 7ms
35 verbose exit 0
36 timing npm Completed in 26ms
37 info ok

15.6.3 配置日志记录

npm config list --long 会打印各种设置的默认值。以下是与日志记录相关的设置的默认值:

% npm config list --long | grep log
loglevel = "notice"
logs-dir = null
logs-max = 10

如果 logs-dir 的值为 null,则 npm 将使用 npm 缓存目录中的 _logs 目录(如前所述)。

要永久更改这些设置,我们也使用 npm config,例如:

我们也可以通过命令行选项临时更改设置,例如:

npm --loglevel silent run build

npm 文档 解释了更改设置的其他方法(例如使用环境变量)。

15.6.4 在 npm install 期间运行的生命周期脚本的输出

npm install(不带参数)期间运行的生命周期脚本的输出是隐藏的。我们可以通过(临时或永久)将 foreground-scripts 设置为 true 来更改此设置。

15.6.5 对 npm 日志记录工作方式的观察

15.7 跨平台 shell 脚本

最常用于包脚本的两个 shell 是:

在本节中,我们将研究在两个 shell 中都有效的结构。

15.7.1 路径和引号

提示:

15.7.2 命令链

有两种方法可以链接在两个平台上都有效的命令:

忽略退出代码的链接在不同平台上有所不同:

以下交互演示了 &&|| 在 Unix 上的工作方式(在 Windows 上,我们将使用 dir 而不是 ls):

% ls unknown && echo "SUCCESS" || echo "FAILURE"
ls: unknown: No such file or directory
FAILURE

% ls package.json && echo "SUCCESS" || echo "FAILURE"
package.json
SUCCESS

15.7.3 包脚本的退出代码

可以通过 shell 变量访问退出代码:

npm run 返回的退出代码与最后执行的 shell 脚本的退出代码相同:

{
  ···
  "scripts": {
    "hello": "echo Hello",
    "err": "more does-not-exist.txt"
  }
}

以下交互发生在 Unix 上:

% npm -s run hello ; echo $?
Hello
0
% npm -s run err ; echo $?
does-not-exist.txt: No such file or directory
1

15.7.4 管道和重定向输入和输出

15.7.5 在两个平台上都有效的命令

以下命令在两个平台上都存在(但在选项方面有所不同):

15.7.6 运行 bin 脚本和包内部模块

以下 package.json 演示了三种调用依赖项中的 bin 脚本的方法:

{
  "scripts": {
    "hi1": "./node_modules/.bin/cowsay Hello",
    "hi2": "cowsay Hello",
    "hi3": "npx cowsay Hello"
  },
  "dependencies": {
    "cowsay": "^1.5.0"
  }
}

说明:

在 Unix 上,我们可以直接调用包本地脚本,前提是它们具有 hashbang 并且是可执行的。但是,这在 Windows 上不起作用,这就是为什么最好通过 node 调用它们的原因。

"build": "node ./build.mjs"

15.7.7 node --evalnode --print

当包脚本的功能变得过于复杂时,通常最好通过 Node.js 模块来实现它,这使得编写跨平台代码变得容易。

但是,我们也可以使用 node 命令来运行小的 JavaScript 代码片段,这对于以跨平台的方式执行小任务非常有用。相关的选项有:

以下命令在 Unix 和 Windows 上均可使用(只有注释是特定于 Unix 的)

# Print a string to the terminal (cross-platform echo)
node -p "'How are you?'"

# Print the value of an environment variable
# (Alas, we can’t change variables via `process.env`)
node -p process.env.USER # only Unix
node -p process.env.USERNAME # only Windows
node -p "process.env.USER ?? process.env.USERNAME"

# Print all environment variables
node -p process.env

# Print the current working directory
node -p "process.cwd()"

# Print the path of the current home directory
node -p "os.homedir()"

# Print the path of the current temporary directory
node -p "os.tmpdir()"

# Print the contents of a text file
node -p "fs.readFileSync('package.json', 'utf-8')"

# Write a string to a file
node -e "fs.writeFileSync('file.txt', 'Text content', 'utf-8')"

如果我们需要特定于平台的行终止符,我们可以使用 os.EOL——例如,我们可以将前面命令中的 'Text content' 替换为

`line 1${os.EOL}line2${os.EOL}`

注意事项

15.8 常见操作的辅助包

15.8.1 从命令行运行包脚本

npm-quick-run 提供了一个 bin 脚本 nr,它允许我们使用缩写来运行包脚本——例如

15.8.2 并发或顺序运行多个脚本

并发运行 shell 脚本

以下两个包为我们提供了跨平台的选择,用于实现该功能和相关功能

15.8.3 文件系统操作

shx 允许我们使用“Unix 语法”来运行各种文件系统操作。它所做的一切都可以在 Unix 和 Windows 上运行。

创建目录

"create-asset-dir": "shx mkdir ./assets"

删除目录

"remove-asset-dir": "shx rm -rf ./assets"

清空目录(使用双引号以确保通配符 * 的安全)

"tscclean": "shx rm -rf \"./dist/*\""

复制文件

"copy-index": "shx cp ./html/index.html ./out/index.html"

删除文件

"remove-index": "shx rm ./out/index.html"

shx 基于 JavaScript 库 ShellJS,其存储库列出了 所有受支持的命令。除了我们已经看到的 Unix 命令之外,它还模拟:catchmodechofindgrepheadlnlsmvpwdsedsorttailtouchuniq 等。

15.8.4 将文件或目录放入回收站

trash-cli 适用于 macOS(10.12+)、Linux 和 Windows(8+)。它将文件和目录放入回收站,并支持路径和 glob 模式。以下是使用它的示例

trash tmp-file.txt
trash tmp-dir
trash "*.jpg"

15.8.5 复制文件树

copyfiles 允许我们复制文件树。

以下是 copyfiles 的一个用例:在 TypeScript 中,我们可以导入非代码资产,例如 CSS 和图像。TypeScript 编译器将代码编译到“dist”(输出)目录,但会忽略非代码资产。此跨平台 shell 命令将它们复制到 dist 目录

copyfiles --up 1 "./ts/**/*.{css,png,svg,gif}" ./dist

TypeScript 编译

my-pkg/ts/client/picker.ts  -> my-pkg/dist/client/picker.js

copy-assets 复制

my-pkg/ts/client/picker.css -> my-pkg/dist/client/picker.css
my-pkg/ts/client/icon.svg   -> my-pkg/dist/client/icon.svg

15.8.6 监视文件

onchange 监视文件并在每次文件更改时运行 shell 命令——例如

onchange 'app/**/*.js' 'test/**/*.js' -- npm test

一个常见的替代方案(还有许多其他方案)

15.8.7 其他功能

15.8.8 HTTP 服务器

在开发过程中,拥有一个 HTTP 服务器通常很有用。以下软件包(以及许多其他软件包)可以提供帮助

15.9 扩展包脚本的功能

15.9.1 per-env:根据 $NODE_ENV 切换脚本

bin 脚本 per-env 允许我们运行包脚本 SCRIPT,并根据环境变量 NODE_ENV 的值自动在(例如)SCRIPT:developmentSCRIPT:stagingSCRIPT:production 之间切换

{
  "scripts": {
    // If NODE_ENV is missing, the default is "development"
    "build": "per-env",

    "build:development": "webpack -d --watch",
    "build:staging": "webpack -p",
    "build:production": "webpack -p"
  },
  // Processes spawned by `per-env` inherit environment-specific
  // variables, if defined.
  "per-env": {
    "production": {
      "DOCKER_USER": "my",
      "DOCKER_REPO": "project"
    }
  }
}

15.9.2 定义特定于操作系统的脚本

bin 脚本 cross-os 根据当前操作系统在脚本之间切换。

{
  "scripts": {
    "user": "cross-os user"
  },
  "cross-os": {
    "user": {
      "darwin": "echo $USER",
      "win32": "echo %USERNAME%",
      "linux": "echo $USER"
    }
  },
  ···
}

支持的属性值有:darwinfreebsdlinuxsunoswin32

15.10 本章来源