如何使用node_modules中本地安装的软件包中的可执行文件?


493

如何在中使用模块的本地版本node.js。例如,在我的应用中,我安装了coffee-script:

npm install coffee-script

这会将其安装在中./node_modules,coffee命令在中./node_modules/.bin/coffee。当我位于项目的主文件夹中时,是否可以运行此命令?我想我正在寻找类似于bundle exec捆扎机中的东西。基本上,我想指定一个咖啡脚本的版本,参与该项目的每个人都应该使用。

我知道我可以添加该-g标志以在全球范围内安装它,这样咖啡在任何地方都可以正常工作,但是如果我想每个项目使用不同版本的咖啡怎么办?


9
很多指导我看了说这样的话npm install niftycommand,然后niftycommand。但是,除非您的路径中带有./node_modules/.bin,否则它将永远无法工作,对吗?
Bennett McElwee

2
这里有一个很好的文章:firstdoit.com/…—基本上,建议您将coffee命令放在该npm scripts部分中,例如"build": "coffee -co target/directory source/directoy", so you can run 之后从终端执行npm run build`。
Benny Neugebauer

@BennyNeugebauer的确如此,这就是我最近一直在做的事情,而不是弄乱PATH
typeoneerror

12
使用npx附带npm 5.2.0 medium.com/@maybekatz/...
onmyway133

Answers:


567

更新:正如Seyeong Jeong在下面的答案中指出的那样,从npm 5.2.0开始,您可以使用npx [command],这更加方便。

5.2.0之前的版本的旧答案

推杆的问题

./node_modules/.bin

进入PATH是因为它仅在当前工作目录是项目目录结构的根目录(即的位置node_modules)时才有效

与您的工作目录无关,您可以使用以下命令获取本地安装的二进制文件的路径:

npm bin

要执行coffee独立于项目目录层次结构中本地位置的本地安装二进制文件,可以使用此bash构造

PATH=$(npm bin):$PATH coffee

我将其别名为npm-exec

alias npm-exec='PATH=$(npm bin):$PATH'

所以,现在我可以

npm-exec coffee

无论我在哪里,都可以运行正确的咖啡

$ pwd
/Users/regular/project1

$ npm-exec which coffee
/Users/regular/project1/node_modules/.bin/coffee

$ cd lib/
$ npm-exec which coffee
/Users/regular/project1/node_modules/.bin/coffee

$ cd ~/project2
$ npm-exec which coffee
/Users/regular/project2/node_modules/.bin/coffee

17
您甚至可以更进一步alias coffee="npm-exec coffee"
定期

6
cd到另一个项目时,输出更改。在项目中cd时,它不会更改。npm bin在“祖先目录”链中搜索到cwd的node_modules目录。如果您特别想使用项目的package.json中列出的模块的二进制文件,则这正是所需的行为。
2013年

11
哦,天哪!为了使我的本地模块正常工作,我真的必须做类似的事情吗?向团队解释是很不现实的!没有比这更直接的了吗?
Alexian

17
您始终可以使用npm脚本,因为它们始终首先搜索本地二进制文件。您可以在那里为每个二进制文件设置别名,也可以只使用通用名称,例如“ build”。
乔·吉姆

6
@philosodad,实际上不,您不是。该PATH会回到它是命令调用之前。在运行命令之前,在同一行中设置环境变量只会影响该命令的环境。
常规

410

很好的例子

您不必再操纵$PATH了!

npm@5.2.0开始npm附带了一个npx软件包,该软件包可让您从本地node_modules/.bin或中央缓存运行命令。

只需运行:

$ npx [options] <command>[@version] [command-arg]...

默认情况下,npx将检查,或本地项目二进制文件中是否<command>存在$PATH,然后执行。

调用npx <command>when <command>尚未存在时,$PATH将为您自动从NPM注册表中安装具有该名称的软件包,然后调用它。完成后,已安装的软件包将不会放在您的全局文件中,因此您不必担心长期的污染。您可以通过提供--no-install选项来防止此行为。

对于npm < 5.2.0,您可以npx通过运行以下命令来手动安装软件包:

$ npm install -g npx

1
我不喜欢同时安装第三方全球npm软件包,npm并且package.json提供几乎相同的功能。
guneysus

如果出现“路径必须是字符串。收到未定义”消息,则表示已解决:github.com/zkat/npx/issues/144#issuecomment-391031816
Valeriy Katkov,

1
这个答案很好。但是我只想说npx是me脚。应该是npm runor npm exec或某物。
William Entriken '18

@WilliamEntriken由于某些原因,npm run [my-local-package]尽管它似乎可以在Windows设备上运行,但在我的Ubuntu上却无法使用。
发条

97

使用npm bin命令获取项目的节点模块/ bin目录

$ $(npm bin)/<binary-name> [args]

例如

$ $(npm bin)/bower install

4
我喜欢这个简单而通用的解决方案。使别名显得不必要。
马特·蒙塔格

似乎是次佳解决方案,它比做的事情更优雅,更安全export PATH="./node_modules/.bin:$PATH"
jontsai 2015年

1
@ inf3rno命令$(npm bin)/jasmine不是,node $(npm bin)/jasmine(您可能已经弄清楚了,但为其他人弄清楚了)。
jassa

5
不错的解决方案,但是它不能在带有$的标准Windows命令行上运行。我觉得将其放在package.json脚本部分中是更好的方法,因为它更具兼容性。
蒂莫西·冈萨雷斯

77

采用 npm run[-script] <script name>

使用npm将bin软件包安装到本地./node_modules目录后,进行修改package.json以添加<script name>如下内容:

$ npm install --save learnyounode
$ edit packages.json
>>> in packages.json
...
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "learnyounode": "learnyounode"
},
...
$ npm run learnyounode

如果npm install具有--add-script选项或其他功能,或者npm run在不添加到scripts块的情况下可以运行,那就很好了。


5
我发现与一个项目中的多个开发人员打交道时,这种方法更加统一-避免了在本地配置任何内容的需要……您就npm install可以访问自己的开发依赖项。唯一的不利方面是您需要npm run eslint(或其他)。您可以创建一个名为“ start”的脚本,该脚本运行gulp,因此您只需键入npm start即可启动开发服务器。很酷的东西,没有bash优点,因此您的Windows朋友仍然喜欢您。:)
jpoveda

1
添加别名以将$(npm bin)放在您的路径上是很聪明的,但这对没有本地配置的用户有效的事实
深深吸引了

12
这需要更多的投票!将args传递给脚本,--如下所示:npm run learnyounode -- --normal-switches --watch -d *.js
ptim

我也找到了最好的解决方案。这里有深入的解释:lostechies.com/derickbailey/2012/04/24/…–
adampasz

1
这通常是我的目的,但是由于某些原因,在Ubuntu设备上npm run ts-node对我不起作用。我只需要重新排序为npx。
发条

42

使用npm-run

从自述文件:

npm运行

从node_modules查找并运行本地可执行文件

可用于npm生命周期脚本的任何可执行文件都可用于npm-run

用法

$ npm install mocha # mocha installed in ./node_modules
$ npm-run mocha test/* # uses locally installed mocha executable 

安装

$ npm install -g npm-run

8
不再,请参阅上面引用的npx ... stackoverflow.com/a/45164863/3246805
tj

41

更新:出于上述安全原因,并且至少不是较新的npm bin命令,我不再推荐此方法。原始答案如下:

如您所知,任何本地安装的二进制文件都在中./node_modules/.bin。为了始终在此目录中运行二进制文件,而不是在全局可用的二进制文件中运行二进制文件(如果存在),建议您./node_modules/.bin首先输入路径:

export PATH="./node_modules/.bin:$PATH"

如果将其放在您的中~/.profilecoffee则始终./node_modules/.bin/coffee可用,否则/usr/local/bin/coffee(或要在其下安装节点模块的任何前缀)使用。


1
那可能是最好的解决方案。我还在我的项目中创建了一个名为“ watch”的bash脚本:./node_modules/.bin/coffee --output lib/ --compile --bare --watch src
typeoneerror 2012年

72
危险,威尔·鲁滨逊!在$ PATH中使用相对路径会打开一个相当于行星大小的安全漏洞,特别是如果您将它们放在最前面的话。如果你所在的目录是由每个人写(说某处/tmp),任何进程或用户可以通过将普通的命令的恶意版本(比如劫持会话lscp等等)那里。这些可能会生成“不可见”子外壳,以捕获您的密码等。
2014年

仅在根目录有效,在其他地方无效。的alias npm-exec='PATH=$(npm bin):$PATH'是雨衣。
oligofren 2015年

1
如果您不把它放在您的第一件事情中PATH,而把它放在最后(使用$(npm bin)表格),这有多糟?这样它们就不能覆盖您现有的东西,并且您将已经信任npm bin目录中的可执行文件,而与PATHvar 无关;威胁模型是:a)恶意软件可以访问您的文件系统,b)他们添加的可执行文件的名称与那些系统工具接近,并且c)您键入错误吗?考虑到使用npm安装的程序时您已经信任外部可执行文件,请尝试了解使情况变得更糟的方案。
osdiab '16

您可以使用别名进行shell技巧,也可以手动进行路径操作,这“可行”,但并不理想。
killscreen

22

PATH解决方案存在以下问题:如果将$(npm bin)放在.profile / .bashrc / etc中,则将对其进行一次评估,并永远设置为该路径首先在其中评估的目录。如果相反,则修改当前路径,则每次运行脚本时,路径都会增加。

为了解决这些问题,我创建了一个函数并使用了它。它不会修改您的环境,并且易于使用:

function npm-exec {
   $(npm bin)/$@  
}

然后可以像这样使用它,而无需对环境进行任何更改:

npm-exec r.js <args>

2
我喜欢这个!我只是将函数命名为n
jontsai 2015年

这很棒!感谢分享。我在下面添加了鱼壳版本。
LeOn-韩立

22

如果要保留npm,则npx应该做您需要的事情。


如果您可以选择切换到yarn(由npm替换为facebook),那么您可以致电:

 yarn yourCmd

package.json内的脚本优先,如果找不到脚本,则查找./node_modules/.bin/文件夹内的脚本。

它还输出运行结果:

$ yarn tsc
yarn tsc v0.27.5
$ "/home/philipp/rate-pipeline/node_modules/.bin/tsc"

因此,您不必为中的每个命令设置脚本package.json


如果你有一个脚本在定义.scripts你的里面package.json

"tsc": "tsc" // each command defined in the scripts will be executed from `./node_modules/.bin/` first

yarn tsc将等于yarn run tscnpm run tsc

 yarn tsc
 yarn tsc v0.27.5
 $ tsc

14

更新:如果您使用的是最近的npm(版本> 5.2)

您可以使用:

npx <command>

npx.bin您的目录中查找命令node_modules

旧答案:

对于Windows

将以下内容存储在一个名为的文件中npm-exec.bat,并将其添加到您的%PATH%

@echo off
set cmd="npm bin"
FOR /F "tokens=*" %%i IN (' %cmd% ') DO SET modules=%%i
"%modules%"\%*

用法

然后你可以像 npm-exec <command> <arg0> <arg1> ...

例如

要执行wdio安装在本地node_modules目录中的操作,请执行以下操作:

npm-exec wdio wdio.conf.js

即它将运行 .\node_modules\.bin\wdio wdio.conf.js


当传递多个参数时,这不起作用。例如npm-exec gulp <some_task>
OK999,2016年

@ OK9999我确定一些小的修改将允许传递参数(因为当您在此处传递参数时,它将用“”引起来);我建议将gulp文件从bin复制粘贴到您的项目根目录(需要对该文件进行一些修改,但无需编写新代码即可使用)
Dheeraj Bhaskar

是的,我最终这样做了。node_modules文件夹必须位于gulpfile所在的文件夹中
OK999,2016年

6

我宁愿不依赖Shell别名或其他程序包。

scripts您的部分添加一条简单的线package.json,您可以运行本地npm命令,例如

npm run webpack

package.json

{
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "webpack": "webpack"
  },
  "devDependencies": {
    "webpack": "^4.1.1",
    "webpack-cli": "^2.0.11"
  }
}

5

如果您希望PATH变量根据当前工作目录正确更新,请将其添加到.bashrc-evalent 的末尾(或在定义的任何内容之后PATH):

__OLD_PATH=$PATH
function updatePATHForNPM() {
  export PATH=$(npm bin):$__OLD_PATH
}

function node-mode() {
  PROMPT_COMMAND=updatePATHForNPM
}

function node-mode-off() {
  unset PROMPT_COMMAND
  PATH=$__OLD_PATH
}

# Uncomment to enable node-mode by default:
# node-mode

每次渲染bash提示时,这可能会增加一小段延迟(最有可能取决于项目的大小),因此默认情况下将其禁用。

您可以分别通过运行node-mode和来在终端中启用和禁用它node-mode-off


4

我一直使用与@guneysus相同的方法来解决此问题,该方法是在package.json文件中创建一个脚本,并使用它运行npm run script-name。

但是,在最近几个月中,我一直在使用npx,并且我喜欢它。

例如,我下载了一个Angular项目,但我不想全局安装Angular CLI。因此,在安装npx的情况下,不要像这样使用全局角度cli命令(如果已安装):

ng serve

我可以从控制台执行此操作:

npx ng serve

这是我写的有关NPX 的文章,并且深入其中。


2

zxc就像是nodejs的“ bundle exec”。它类似于使用PATH=$(npm bin):$PATH

$ npm install -g zxc
$ npm install gulp
$ zxc which gulp
/home/nathan/code/project1/node_modules/.bin/gulp

2

与@regular的解决方案相同,但具有鱼壳的味道

if not contains (npm bin) $PATH
    set PATH (npm bin) $PATH
end

1

您也可以使用direnv并仅在工作文件夹中更改$ PATH变量。

$ cat .envrc
> export PATH=$(npm bin):$PATH

1

将此脚本添加到您的中.bashrc。然后,您可以coffee在本地致电或任意呼叫。这对于您的笔记本电脑很方便,但是请不要在服务器上使用它。

DEFAULT_PATH=$PATH;

add_local_node_modules_to_path(){
  NODE_MODULES='./node_modules/.bin';
  if [ -d $NODE_MODULES ]; then
    PATH=$DEFAULT_PATH:$NODE_MODULES;
  else
    PATH=$DEFAULT_PATH;
  fi
}

cd () {
  builtin cd "$@";
  add_local_node_modules_to_path;
}

add_local_node_modules_to_path;

注意:该脚本使cd命令成为别名,并在每次调用后cd检查node_modules/.bin并将其添加到您的中$PATH

注意2:您可以将第三行更改为NODE_MODULES=$(npm bin);。但这会使cd命令太慢。


1
使用$(npm bin)而不是硬编码./node_modules/.bin
bfontaine

嗯,$(npm bin)似乎无法与每个cd命令一起使用。我已经还原了代码并为其添加了注释。
河村通

1

对于Windows,请使用以下命令:

/* cmd into "node_modules" folder */
"%CD%\.bin\grunt" --version

0

我遇到了同样的问题,我不特别喜欢使用别名(按照常规建议),如果您也不喜欢它们,那么这是我使用的另一种解决方法,您首先必须创建一个很小的可执行bash脚本,说setenv.sh

#!/bin/sh

# Add your local node_modules bin to the path
export PATH="$(npm bin):$PATH"

# execute the rest of the command
exec "$@"

然后您可以/bin使用以下命令在本地使用任何可执行文件:

./setenv.sh <command>
./setenv.sh 6to5-node server.js
./setenv.sh grunt

如果您scripts在package.json中使用,则:

...,
scripts: {
    'start': './setenv.sh <command>'
}

2
对于package.json脚本,此setenv脚本不是必需的。在执行npm run {scripts}时,npm已经为您的路径添加了本地node_modules / .bin目录。
jasonkarns

0

我想知道这是否是一个不安全/不好的主意,但是经过一番思考,我在这里看不到任何问题:

修改Linus的不安全的解决方案将其添加到年底,用npm bin找到的目录,使脚本只调用npm binpackage.json存在于父(速度),这是我想出了zsh

find-up () {
  path=$(pwd)
  while [[ "$path" != "" && ! -e "$path/$1" ]]; do
    path=${path%/*}
  done
  echo "$path"
}

precmd() {
  if [ "$(find-up package.json)" != "" ]; then
    new_bin=$(npm bin)
    if [ "$NODE_MODULES_PATH" != "$new_bin" ]; then
      export PATH=${PATH%:$NODE_MODULES_PATH}:$new_bin
      export NODE_MODULES_PATH=$new_bin
    fi
  else
    if [ "$NODE_MODULES_PATH" != "" ]; then
      export PATH=${PATH%:$NODE_MODULES_PATH}
      export NODE_MODULES_PATH=""
    fi
  fi
}

对于bashprecmd您可以使用$PROMPT_COMMAND变量(而不是使用钩子)(我没有测试过,但是您知道了):

__add-node-to-path() {
  if [ "$(find-up package.json)" != "" ]; then
    new_bin=$(npm bin)
    if [ "$NODE_MODULES_PATH" != "$new_bin" ]; then
      export PATH=${PATH%:$NODE_MODULES_PATH}:$new_bin
      export NODE_MODULES_PATH=$new_bin
    fi
  else
    if [ "$NODE_MODULES_PATH" != "" ]; then
      export PATH=${PATH%:$NODE_MODULES_PATH}
      export NODE_MODULES_PATH=""
    fi
  fi   
}

export PROMPT_COMMAND="__add-node-to-path"

添加npm bin到末尾$PATH可能无法执行用户期望的操作:基本上是另一个可执行文件,但更可能是具有另一个版本的全局安装包!
LoganMzz

0

我是Windows用户,这对我有用:

// First set some variable - i.e. replace is with "xo"
D:\project\root> set xo="./node_modules/.bin/"

// Next, work with it
D:\project\root> %xo%/bower install

祝好运。


0

如果出于安全原因,您正在使用fish shell并且不想添加$path。我们可以添加以下功能来运行本地节点可执行文件。

### run executables in node_module/.bin directory
function n 
  set -l npmbin (npm bin)   
  set -l argvCount (count $argv)
  switch $argvCount
    case 0
      echo please specify the local node executable as 1st argument
    case 1
      # for one argument, we can eval directly 
      eval $npmbin/$argv
    case '*'
      set --local executable $argv[1]
      # for 2 or more arguments we cannot append directly after the $npmbin/ since the fish will apply each array element after the the start string: $npmbin/arg1 $npmbin/arg2... 
      # This is just how fish interoperate array. 
      set --erase argv[1]
      eval $npmbin/$executable $argv 
  end
end

现在,您可以运行以下内容:

n coffee

或更多类似的参数:

n browser-sync --version

请注意,如果您是bash用户,那么@ bob9630的答案是利用bash的方法$@,该方法在中不可用fishshell


-9

在package.json中包含coffee-script以及每个项目所需的特定版本,通常是这样的:

"dependencies":{
  "coffee-script": ">= 1.2.0"

然后运行npm install在每个项目中安装依赖项。这将安装指定版本的coffee-script,每个项目都可以在本地访问它。


是的,我做到了我在问题中所说的。除了./node_modules/.bin/coffee外,我该如何在我的项目中专门调用那个?
typeoneerror

如果您已在项目的主文件夹中使用package.json运行npm install,则此文件夹中应具有./node_modules/.bin/coffee文件夹。使用./node_modules/coffee-script/bin/coffee将运行本地版本的coffee,而仅运行coffee将运行全局安装。如果您在此项目文件夹的其他路径中安装了其他版本的咖啡,则可以使用./path/to/this/installation/coffee访问它。
杏仁

这对我不起作用。我正在尝试使用“ svgo”,并且仅在全局安装时有效。我曾尝试npm install svgo以及npm install与的package.json。两种方法均“成功”安装,但“ svgo”命令仍然不可用。
Ryan Wheale

1
Grunt巧妙地使用了此功能,恕我直言,其他软件包也是如此。首先,您在grunt-cli全局范围内安装该软件包,然后在项目目录中安装该grunt软件包的任何(已修改)版本,然后在运行时grunt,它将使用此本地版本。
2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.