macOS で npm install -g 実行時にエラーが出るときの対処法
macOS で npm install -g
実行時にエラーが出るときの対処法について説明します。ここでは、
EACCES: permission denied, access '/usr/local/lib/node_modules/...'
といったエラーが出る場合が対象です。このエラーへの対処法にはいくつか方法があります。ここでは、sudo
コマンドを使って解決する方法と使わないで解決する方法を紹介します。
sudo
コマンドを使う方法
sudo
コマンドを使って解決する方法には2つ方法があります。
- a) インストールするときに
sudo
コマンドを使う - b) ファイルのパーミッションを root からユーザーに変更する
sudo
コマンドを使う
インストールするときに 一般ユーザーで NPM 用のパッケージをグローバルにインストールしようとするからエラーになるので、これを解決すればインストールができるようになります。
ここで、一般ユーザーへ一時的に root 権限を付与する sudo
コマンドというものがあります。このコマンドを使うと、一般ユーザーでも「root 権限が必要な NPM 用のパッケージをグローバルにインストールする」ということができるようになります。
これが一番素直な解決策でしょう。例えば、express-generator パッケージをインストールするとしたら下記のようにします。
sudo npm install -g express-generator
ファイルのパーミッションを root からユーザーに変更する
自分しか使わない macOS マシンの場合は、NPM 用のパッケージをグローバルにインストールするときに、わざわざ sudo
コマンドを使わないといけないのは面倒です。
単純には、/usr/local/lib/node_modules
に対するユーザーのファイルパーミッションが足りないことが原因なので、これをユーザー所有とすれば解決します。つまり、node 関連のファイルをユーザー所有とすれば良いので、その方法を説明します。
先に whoami
と npm config get prefix
の実行結果を確認しておきます。
$ whoami
user001
$ npm config get prefix
/usr/local/
コマンドの実行結果を別のコマンドに含めるには、コマンドを $(
と )
とで囲んで使います。whoami
と npm config get prefix
の実行結果を使いたい場合は、それぞれ $(whoami)
と $(npm config get prefix)
とします。コマンドの実行結果である user001
や /usr/local/
をそのまま使ったほうがわかりやすく確実なのですが、環境依存の値を使う場合に、$(コマンド)
を使ってコマンド実行例を示すことはよくあります。
少し話が長くなりました。以上のことを考慮すると、node 関連のファイルをユーザー所有とするには、一般的には下記のようなコマンドを実行するのが良いでしょう。
sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}
sudo
コマンドを使わない方法
sudo
コマンドが使えない環境で対応したい場合もあるでしょう。ここでは、その方法について説明します。
Node.js の環境では、ツールのバージョン管理が煩雑になるので、個人的にはパッケージのグローバルインストールは推奨しません。ですから、自分は次のいずれかの方法を使うようにしています。
- a)
npm exec
コマンド (npx
コマンド) を使って実行する - b) 環境変数
NPM_CONFIG_PREFIX
を設定してインストール先を変更して使う - c) プロジェクトの開発用依存パッケージとしてインストールして使う
- d) 開発用ツールとして用意
npm exec
コマンド (npx
コマンド) を使って実行
npm exec
コマンドを使うと、指定したコマンドを含むパッケージがユーザーのホームディレクトリーにある ~/.npm/_npx
にダウンロードされて使えるようになります。npm exec
コマンドの短縮形が npx
コマンドなので、そちらを使っても良いです。
npm exec
コマンドと npx
コマンドでのコマンド実行方法は npm
のバージョンによって微妙に違ったりするので、自分が使っているものでの使い方は --help
で確認して使うようにしましょう。
例えば、手元では次のように npm
コマンドのヘルプを表示させると npm exec
コマンドのヘルプが表示されます。
$ npx --help
Run a command from a local or remote npm package
Usage:
npm exec -- <pkg>[@<version>] [args...]
(略)
これを使うと、例えば express-generator
コマンドを使う場合は次のようにコマンドを実行します。初めて実行するときはインストールして良いか確認する Ok to proceed? (y)
のプロンプトが表示されます。y
を入力すると、インストールされます。
$ npm exec -- express-generator --help
Need to install the following packages:
express-generator@4.16.1
Ok to proceed? (y) y
(略)
Usage: express [options] [dir]
(略)
この後も、コマンドを実行するには、npm exec --
をつけて実行する必要がありますが、グローバル用のディレクトリーを使わずにコマンド実行ができます。
npm exec
コマンドで実行するコマンドのパッケージは一時的なキャッシュなので、クリアすることができます。キャッシュクリアには clear-npx-cache
コマンドを使うのが良いでしょう。
$ npm exec -- clear-npx-cache
Need to install the following packages:
clear-npx-cache@1.0.1
Ok to proceed? (y) y
NPM_CONFIG_PREFIX
を設定
環境変数 環境変数 NPM_CONFIG_PREFIX
を使うことで、npm
コマンドが使うディレクトリーを変更することができます。たとえば、${HOME}/.npm-global
(~/.npm-global
と同じ)を作成して、ここを使うようにすれば、ファイルパーミッションの問題は解決します。
具体的な手順について説明します。
最初に NPM_CONFIG_PREFIX
へ指定するディレクトリーの用意をします。すでにインストールされているものを使えるようにするため、cp
コマンドでそれらのコピーもします。
mkdir -p ${HOME}/.npm-global/lib
cp -r $(npm config get prefix)/lib/node_modules ${HOME}/.npm-global/lib/
環境変数 NPM_CONFIG_PREFIX
と PATH
を設定してから npm install -g
コマンドを実行してパッケージをインストールします。次の例では、express-generator パッケージをインストールしています。
$ export NPM_CONFIG_PREFIX=${HOME}/.npm-global
$ export PATH=${NPM_CONFIG_PREFIX}/bin:$PATH
$ npm install -g express-generator
これで、express-generator コマンドが使えるようになります。
次に念の為に npm config
コマンドで NPM_CONFIG_PREFIX
環境変数と同じディレクトリーを prefix
に指定しておきます。
npm config set prefix '${HOME}/.npm-global'
これを実行すると、${HOME}/.npmrc
ファイルに設定が保存されます。
$ cat ${HOME}/.npmrc
prefix=/home/node/${HOME}/.npm-global
もしデフォルト値に戻したい場合は prefix
の行を消します。この行しかないなら、${HOME}/.npmrc
ファイルを削除しても良いです。
さて、ここで、環境変数を毎回指定するのは大変です。bash を使っている場合は ${HOME}/.profile
か ${HOME}/.bash_profile
に指定しておくと良いでしょう。
export NPM_CONFIG_PREFIX=${HOME}/.npm-global
export PATH=${NPM_CONFIG_PREFIX}/bin:$PATH
zsh を使っている場合は、${HOME}/.zprofile
か ${HOME}/.zshrc
へ同じ内容で指定しておくと良いでしょう。
これらの環境変数を ${HOME}/.profile
などへ追加したら、一度ログアウトしてから再ログインします。すると、指定が反映されます。
プロジェクトの開発用依存パッケージとしてインストール
プロジェクトの開発用依存パッケージとしてインストールすると、プロジェクト用の node_modules
ディレクトリーにインストールされるので、グローバル用のディレクトリーで発生するパーミッションの問題は、そもそも起きません。
そのプロジェクトで使うツールのバージョンも固定して継続して使いやすくなります。プロジェクトが変わると同じバージョンのものであっても別途インストールすることになるので、その点がグローバルにインストールするのよりも不利になります。
プロジェクト用ディレクトリーの project001 があったとして、express-generator をプロジェクトの開発用依存パッケージとしてインストールして使うには、次のようにします。
$ cd project001
$ npm install -D express-generator
$ ./node_modules/.bin/express-generator --help
project001/node_modules/.bin
ディレクトリーにあるコマンドは、このようにして実行できます。
カレントディレクトリーを project001 としている場合は、npm exec
で project001/node_modules/.bin
ディレクトリーにあるコマンドを実行することもできます。
$ cd project001
$ npx express-generator --help
Usage: express [options] [dir]
(略)
project001/package.json
ファイルの scripts:
にコマンドを指定して使えるようにすることもできます。
project001/package.json
ファイルを編集して次のようにします。
{
"scripts": {
"express": "express"
},
"devDependencies": {
"express-generator": "^4.16.1"
}
}
express-generator
パッケージのデフォルトで実行されるコマンドは express
なので、それを指定しています。
利用するには、npm run
コマンドを使います。コマンドへパラメーターを指定するには --
を間に入れます。
$ npm run express -- --help
> express
> express --help
Usage: express [options] [dir]
(略)
開発用ツールとして用意
「プロジェクトの開発用依存パッケージとしてインストール」の場合の不利な点に「プロジェクトが変わると同じバージョンのものであっても別途インストールすることになるので、その点がグローバルにインストールするのよりも不利になる」というものがありました。これを改善する方法のひとつに、開発用ツールとして用意する方法があります。npm exec
で実行するという方法で良い気がするのですが、知っておいて損はないので紹介しておきます。
例えば、npm のバージョンが 9 のときにグローバルへインストールして使いたくなったコマンドを ${HOME}/npm-tools/npm9
へ入れておくというルールで運用するとします。その場合は、次のようにすれば良いでしょう。
mkdir -p ${HOME}/npm-tools/npm9
cd ${HOME}/npm-tools/npm9
npm install express-generator
使うときは次のようにします。
$ ${HOME}/npm-tools/npm9/node_modules/.bin/express --help
> express
> express --help
Usage: express [options] [dir]
(略)
こうしておけば、npm
コマンドがバージョン9の間に作業する場合は、${HOME}/npm-tools/npm9
にインストールされているものを共通で使えば良いということになります。将来、npm
コマンドがバージョン10になったら、${HOME}/npm-tools/npm10
を用意して、そちらを使うようにします。もし、バージョン10を普段使っている時に、バージョン9の頃に使っていたものを使いたい場面があったら、${HOME}/npm-tools/npm9
にインストールされているものを使えば良いということになります。
NPM やシェルの基本を知っていると当たり前のことなのですが、知らない人はわからないことが多いと思い、整理してみました。
Discussion