package.json 属性"bin" 让一个npm包指定它提供哪些shell脚本(更多信息,见"用Node.js为Unix和Windows创建基于ESM的shell脚本")。如果我们安装了这样的包,Node.js会确保我们可以从命令行访问这些shell脚本(所谓的bin脚本)。在这篇博文中,我们将探讨两种用bin脚本安装软件包的方法。
-
本地安装带有 bin 脚本的软件包意味着将其作为一个软件包的依赖项来安装。脚本只能在该包内访问。
-
全局安装带有 bin 脚本的软件包是指将其安装在一个 "全局位置",这样脚本就可以在任何地方被访问--无论是当前用户还是系统的所有用户(取决于 npm 的设置方式)。
我们将探讨这一切意味着什么,以及在安装后如何运行bin脚本。
全局安装npm注册表包
包cowsay,其属性如下:package.json 。
"bin": {
"cowsay": "./cli.js",
"cowthink": "./cli.js"
},
要在全球范围内安装这个包,我们使用npm install -g 。
npm install -g cowsay
注意事项:在Unix上,我们可能要使用sudo (我们很快就会知道如何避免这种情况)。
sudo npm install -g cowsay
之后,我们可以在命令行中使用cowsay 和cowthink 命令。
注意,只有bin脚本是全局可用的。当Node.js在node_modules 目录中查找裸模块指定符时,这些包会被忽略。
哪些包是全局安装的? npm ls -g
我们可以检查哪些包是全局安装的,以及安装在哪里。
% npm ls -g
/usr/local/lib
├── corepack@0.12.1
├── cowsay@1.5.0
└── npm@8.15.0
在Windows上,安装路径是%AppData%\npm ,例如。
>echo %AppData%\npm
C:\Users\jane\AppData\Roaming\npm
全局安装的软件包在哪里? npm root -g
macOS上的结果。
% npm root -g
/usr/local/lib/node_modules
Windows上的结果。
>npm root -g
C:\Users\jane\AppData\Roaming\npm\node_modules
全局性的shell脚本安装在哪里? npm bin -g
npm bin -g 告诉我们npm把shell脚本安装在全局的什么地方。它还确保该目录在shell的PATH中是可用的。
macOS上的结果。
% npm bin -g
/usr/local/bin
% which cowsay
/usr/local/bin/cowsay
在Windows命令壳上的结果。
>npm bin -g
C:\Users\jane\AppData\Roaming\npm
>where cowsay
C:\Users\jane\AppData\Roaming\npm\cowsay
C:\Users\jane\AppData\Roaming\npm\cowsay.cmd
没有文件名扩展的可执行cowsay ,用于基于Unix的Windows环境,如Cygwin、MinGW和MSYS。
Windows PowerShell为gcm cowsay ,返回这个路径。
C:\Users\jane\AppData\Roaming\npm\cowsay.ps1
包在全球范围内安装在哪里?npm的安装前缀
npm的安装前缀决定了包和bin脚本在全球范围内的安装位置。
这就是macOS上的安装前缀。
% npm config get prefix
/usr/local
相应地。
- 软件包被安装在
/usr/local/lib/node_modules - bin 脚本安装在
/usr/local/bin
这是Windows上的安装前缀。
>npm config get prefix
C:\Users\jane\AppData\Roaming\npm
相应地。
- 软件包被安装在
C:\Users\jane\AppData\Roaming\npm\node_modules - Bin 脚本安装在
C:\Users\jane\AppData\Roaming\npm
改变软件包的全局安装位置
在这一节中,我们将研究两种改变软件包全局安装位置的方法。
- 改变npm的安装前缀
- 使用 Node.js 版本管理器
改变npm的安装前缀
改变软件包的全局安装位置的一种方法是改变npm的安装前缀。
Unix。
mkdir ~/npm-global
npm config set prefix '~/npm-global'
Windows Command shell。
mkdir "%UserProfile%\npm-global"
npm config set prefix "%UserProfile%\npm-global"
Windows PowerShell。
mkdir "$env:UserProfile\npm-global"
npm config set prefix "$env:UserProfile\npm-global"
配置数据被保存到主目录下的一个文件.npmrc 。
从现在开始,全局安装将被添加到我们刚才指定的目录中。
之后,我们仍然要把npm bin -g 目录添加到我们的shell PATH中,这样我们的shell才能找到我们全局安装的bin脚本。
**改变npm前缀的一个坏处是:**如果我们告诉它自己升级,npm现在也会被安装在新的位置。
使用Node.js版本管理器
Node.js版本管理器可以让我们同时安装多个Node.js版本并在它们之间切换。流行的有:。
在本地安装npm注册表包
要在本地安装一个npm注册表包,如cowsay (放入一个包中),我们要做以下工作。
cd my-package/
npm install cowsay
这将在package.json 中添加以下数据。
"dependencies": {
"cowsay": "^1.5.0",
···
}
此外,该包会被下载到以下目录中。
my-package/node_modules/cowsay/
在Unix上,npm为bin脚本添加这些符号链接。
my-package/node_modules/.bin/cowsay -> ../cowsay/cli.js
my-package/node_modules/.bin/cowthink -> ../cowsay/cli.js
在Windows上,npm将这些文件添加到my-package\node_modules\.bin\ 。
cowsay
cowsay.cmd
cowsay.ps1
cowthink
cowthink.cmd
cowthink.ps1
没有扩展名的文件是基于Unix的Windows环境的脚本,如Cygwin、MinGW和MSYS。
npm bin 告诉我们本地安装的bin脚本的位置--例如。
% npm bin
/Users/john/my-package/node_modules/.bin
注意:在本地,软件包总是安装在一个目录node_modules ,旁边是一个package.json 文件。如果后者在当前目录中不存在,npm会在一个祖先目录中搜索它并在那里安装包。要检查npm会在本地安装软件包的位置,我们可以使用命令npm root - 例如(Unix)。
% cd $HOME
% npm root
/Users/john/node_modules
在John的主目录中没有package.json ,但npm不能在祖先目录中安装任何东西,这就是为什么npm root 显示这个目录。在当前位置本地安装一个包,会导致package.json 被创建,并且安装工作照常进行。
运行本地安装的bin脚本
(本小节中的所有命令都是在目录my-package 内执行的。)
直接运行bin脚本
我们可以在shell中运行cowsay ,如下所示。
./node_modules/.bin/cowsay Hello
在Unix上,我们可以设置一个帮助器。
alias npm-exec='PATH=$(npm bin):$PATH'
那么下面的命令就可以起作用了。
npm-exec cowsay Hello
通过包脚本运行bin脚本
我们还可以在package.json 中添加一个包脚本。
{
···
"scripts": {
"cowsay": "cowsay"
},
···
}
现在我们可以在shell中执行这个命令。
npm run cowsay Hello
这就可以了,因为在Unix上npm会临时添加以下条目到$PATH 。
/Users/john/my-package/node_modules/.bin
/Users/john/node_modules/.bin
/Users/node_modules/.bin
/node_modules/.bin
在Windows上,类似的条目被添加到%Path% 或$env:Path 。
C:\Users\jane\my-package\node_modules\.bin
C:\Users\jane\node_modules\.bin
C:\Users\node_modules\.bin
C:\node_modules\.bin
下面的命令列出了在包脚本运行时存在的环境变量和它们的值。
npm run env
通过npx运行bin脚本
在一个软件包中,npx可以用来访问bin脚本。
npx cowsay Hello
npx cowthink Hello
以后会有更多关于npx的内容。
安装未发布的软件包
有时,我们有一个还没有发布或永远不会发布的软件包,想安装它。
npm link:在全局范围内安装一个未发布的包
让我们假设我们有一个未发布的软件包,它的名字是@my-scope/unpublished-package ,存放在一个目录下/tmp/unpublished-package/ 。我们可以让它在全局范围内可用,方法如下。
cd /tmp/unpublished-package/
npm link
如果我们这样做。
-
npm为全局的
node_modules(由npm root -g返回)添加一个符号链接--例如。/usr/local/lib/node_modules/@my-scope/unpublished-package -> ../../../../../tmp/unpublished-package -
在Unix上,npm也会在每个bin脚本中添加一个来自全局bin目录的符号链接(如由
npm bin -g返回)。这个链接不是直接的,它要经过全局的node_modules目录。/usr/local/bin/my-command -> ../lib/node_modules/@my-scope/unpublished-package/src/my-command.js -
在Windows上,它添加了通常的3个脚本(这些脚本通过相对路径引用链接的软件包到全局
node_modules)。C:\Users\jane\AppData\Roaming\npm\my-command C:\Users\jane\AppData\Roaming\npm\my-command.cmd C:\Users\jane\AppData\Roaming\npm\my-command.ps1
由于链接的包是如何被提及的,它的任何变化都会立即生效。当它发生变化时,没有必要重新链接它。
为了检查全局安装是否成功,我们可以使用npm ls -g 来列出所有全局安装的软件包。
npm link:在本地安装一个全局链接的软件包
当我们在全局安装了我们的upublished包(见上一小节)后,我们可以选择在本地将其安装在我们的一个包中(可以是已发布的或未发布的)。
cd /tmp/other-package/
npm link @my-scope/unpublished-package
这就创建了以下链接。
/tmp/other-package/node_modules/@my-scope/unpublished-package
-> ../../../unpublished-package
默认情况下,未发布的包不会被添加为package.json 的依赖关系。这背后的原理是,npm link 经常被用来暂时与一个未发布的注册表包版本一起工作--它不应该出现在依赖关系中。
npm link:撤消链接
撤销本地链接。
cd /tmp/other-package/
npm uninstall @my-scope/unpublished-package
撤销全局链接。
cd /tmp/unpublished-package/
npm uninstall -g
通过本地路径安装未发布的包
另一种在本地安装未发布软件包的方法,是使用npm install 并通过本地路径(而不是通过其软件包名称)来引用它。
cd /tmp/other-package/
npm install ../unpublished-package
这有两个效果。
首先,下面的符号链接被创建。
/tmp/other-package/node_modules/@my-scope/unpublished-package
-> ../../../unpublished-package
第二,一个依赖关系被添加到package.json 。
"dependencies": {
"@my-scope/unpublished-package": "file:../unpublished-package",
···
}
这种安装未发布软件包的方式也适用于全球。
cd /tmp/unpublished-package/
npm install -g .
安装未发布包的其他方式
-
Yalc允许我们将软件包发布到本地的 "Yalc仓库"(想想本地注册表)。从该仓库中,我们可以安装软件包作为依赖,例如,一个软件包
my-package/。它们被复制到目录my-package/.yalc,file:或link:的依赖关系被添加到package.json。 -
relative-deps支持"relativeDependencies",在package.json,这(如果它们存在)覆盖了正常的依赖关系。与npm link和本地路径安装相反。- 正常的依赖关系不需要被改变。
- 相对依赖关系的安装就像它们来自npm注册表一样(不是通过符号链接)。
relative-deps这也有助于保持本地安装的相对依赖项和它们的原件同步。 -
npx link是一个更安全的npm link,不需要全局安装,还有其他好处。
npx运行npm包中的bin脚本而不安装它们
npx是一个用于运行bin脚本的shell命令,与npm捆绑在一起。
它最常见的用法是。
npx arg1 arg2 ...
这个命令在npx缓存中安装名称为package-name 的包,并运行与该包同名的bin脚本--比如说。
npx cowsay Hello
这意味着我们可以在不安装bin脚本的情况下运行它们。npx对bin脚本的一次性调用最有用--例如,许多框架为设置新项目提供了bin脚本,这些脚本经常通过npx运行。
在npx第一次使用一个软件包后,它在其缓存中是可用的,随后的调用会快得多。然而,我们不能确定一个软件包在缓存中停留了多长时间。因此,npx并不能替代全局或局部安装bin脚本的做法。
如果一个软件包附带的bin脚本的名称与它的软件包名称不同,我们可以像这样访问它们。
npx --package= arg1 arg2 ...
比如说。
npx --package=cowsay cowthink Hello
npx 缓存
npx的缓存在哪里?
在Unix上,我们可以通过下面的命令找到它。
npx --package=cowsay node -p \
"process.env.PATH.split(':').find(p => p.includes('_npx'))"
这将返回一个类似于这个的路径。
/Users/john/.npm/_npx/8f497369b2d6166e/node_modules/.bin
在Windows上,我们可以用(一行分成两行)。
npx --package=cowsay node -p
"process.env.Path.split(';').find(p => p.includes('_npx'))"
它返回的路径与此相似(单行路径分成两行)。
C:\Users\jane\AppData\Local\npm-cache\_npx\
8f497369b2d6166e\node_modules\.bin
注意,npx的缓存与npm为其安装的模块使用的缓存不同。
-
Unix。
- npm cache。
$HOME/.npm/_cacache/ - npx cache。
$HOME/.npm/_npx/
- npm cache。
-
Windows(PowerShell)。
- npm cache:
$env:UserProfile\AppData\Local\npm-cache\_npx\ - npx cache。
$env:UserProfile\AppData\Local\npm-cache\_cacache\
- npm cache:
两个缓存的父目录可以通过确定。
npm config get cache
关于npm缓存的更多信息,请参见npm文档。
与npx缓存不同的是,数据永远不会从npm缓存中删除,只是添加。我们可以在Unix上按如下方式检查其大小。
du -sh $(npm config get cache)/_cacache/
以及在Windows PowerShell上。
DiskUsage /d:0 "$(npm config get cache)\_cacache"
进一步阅读
这篇博文是关于Node.js shell脚本系列的一部分。
你可能还对以下内容感兴趣 我即将出版的书 "Shell scripting with Node.js"。.