コマンドが見つかりません(command not found)の対処法

npmのプログラム(パッケージ)をインストールしても、コマンドを叩いてうまくいかないことがあります。

うまくいかなかったケース

私がハマってしまったのは、ProgateのNode.jsの「Expressのアプリケーションを作成しよう」というコラムを実践していたときです。

コラムはこちら

Node.jsの新規アプリケーションを作ろう! | プログラミングの入門なら基礎から学べるProgate[プロゲート]

npmのパッケージ、「nodemon」をインストールしても nodemon app.js でサーバー起動ができないというものでした。

インストール
・グローバル

npm install -g nodemon

・ローカル

npm install nodemon

ローカルやグローバルにインストールしたり、1回アンインストールして再度インストールしてみてもだめ。

実行

nodemon app.js

とすると、
zsh: command not found: nodemon と返ってきます。

☆後ほどグローバルでインストールして実行したところ、うまくいきました。スペルミスしていたのか、何だったのでしょう...。
めでたしめでたしですが、ここはローカルにインストールした場合で進めます。

環境

  • シェルはzsh
  • nodenvからnodeとnpmはインストール済み

原因と対処法

原因はPATHが通っていないケースが大半なのではないでしょうか。

実際、パスを付けてnodemon という実行ファイルを直接叩けば実行されました。

node_modulesフォルダのあるところまで移動して

./node_modules/.bin/nodemon app.js

です。
(ローカルにインストールしたnodemonの実行です)

毎回ここまで打つのは大変なので、その他の対処法を考えてみました。

対処法その1:PATHの設定をする

PATHの設定をすれば nodemon コマンド一つで実行できます。

Macで「zsh: command not found」が出る原因と対処法

環境変数のPATHには、実行したいプログラムのあるフォルダのパスを複数登録することができるので、そこに追加する、といったものです。

今回ローカルにインストールしたプログラム(nodemon)なので、できれば汎用的なパスだけの登録にしておきたいです。

env コマンドで環境変数(PATHなど)を確認できるということは覚えておきたいです。

以下はこちらを参考にしながらまとめていきます。
どうもありがとうございます。

知らないのは損!npmに同梱されているnpxがすごい便利なコマンドだった | Developers.IO

対処法その2:npm binを使って直接叩く

npm binはカレントプロジェクトのローカルパッケージPATHを返してくれるコマンドです。

実際にプロジェクトのディレクトリ内で npm bin と叩くと、ルートディレクトリから node_module/.bin までのパスが返ってきました。

$(npm bin)/nodemon app.js

でうまくいきました。

$() の書き方は初めて知りました。

Linuxでのドル記号「$(...)」の意味と使い方 | UX MILK

対処法3:package.jsonにscriptを追加する

nodemon のコマンドをpackage.jsonのscriptに登録して使う方法です。

npmのrun-scriptというしくみを利用します。
こちら↓には npm-script とあります。
「npmのrun-script=npm-script」なのでしょうか。

Node.jsユーザーなら押さえておきたいnpm-scriptsのタスク実行方法まとめ - ICS MEDIA

npm-scriptsとは、package.jsonファイルに記述可能なシェルスクリプトエイリアスです。エイリアスとはコマンド名を別のコマンド名に置き換えることです。

gulpを使わなくても、scriptフィールドでもりもりタスクを設定できることが分かりました。

package.json

 "scripts": {
    "nodemon": "nodemon"
  },

ターミナル

npm run nodemon app.js

で動きました。

また
package.json

 "scripts": {
    "nodemon": "nodemon app.js"
  },

ターミナル

npm run nodemon

でもOKです。

毎回サーバーを起動させるので、できる限りコマンドを短くしたい、となれば
package.json

 "scripts": {
    "start": "nodemon app.js"
  },

ターミナル

npm start

でしょうか。

ところでなぜコマンド名だけのシェルスクリプトの登録でうまく実行できるのでしょうか。

scriptsフィールドではローカルインストールしたパッケージのコマンドを直接実行できるテクニックを使用したものです。

npmのパッケージの構成ファイルであるpackage.jsonに書いたものなので、そう言われたらそうですね。

対処法4:npxをつかう

npx とは

npxはnpmパッケージを簡単に実行できるコマンドです。具体的には次のようなことができます。

  • run-scriptを使用せずにローカルインストールしたコマンドを実行する
  • グローバルインストールせずに一度だけコマンドを実行する
  • GitHubやGistで公開されているスクリプトを実行する

他の記事でも出てきた、遊び心いっぱいのパッケージで私も試してみました。

npx yosay Hello!

f:id:yokoyoko_115:20201017011821p:plain

npxでは、事前インストールせずに一度だけコマンド実行でき、実行後には削除されます。

便利便利と書かれているので便利なのでしょうが、残念ながらまだ実感できず...。パッケージをお手軽に使いたくなる日が来ますように。

気になったこと

ここまで書いていて追加で気になったことをまとめました。

パッケージとモジュールの違いについて

未だに「パッケージ」と「モジュール」の差がよくわかっていません。同じだと思っていました。

どうやら違うようなので、こちらの記事を見て自分の言葉に替えてみました。

【npm】パッケージとモジュールの違いって何? - Qiita

パッケージ

パッケージとはpackage.jsonによって記述されるファイルもしくはディレクトリのことを指します。

なので、package.jsonがある、node_moduleディレクトリ内のディレクトリたちは各パッケージなのだと思います。

モジュール

モジュールとは、Node.jsの require 機能によって呼び出すことができる node_modules 内のファイルまたはディレクトリのことを指します。

この「Node.jsの require 機能によって呼び出す」というのは、今のところgulpfile.jsで見たことがあります。

モジュールとして require 機能によって読み込まれる為には、次のいずれかである必要があります。

  • package.json を含むフォルダーであり、mainフィールドを含むファイルであること。
  • index.js というファイルが入っているフォルダであること。
  • JavaScript ファイルであること。

package.jsonname フィールドはパッケージ名、 main フィールドはメインエントリー(?)といいjsファイルやディレクトリ名が入るようです。

package.jsonの内容をまとめてみました - Qiita

これらからnodemonはパッケージでありモジュールであることが分かりました。

パッケージを特にrequire 機能によって読み込まれるものをモジュールという
考え方でいいかと思いました。

自分たちもパッケージを作っているんだ、という意識を持ちました。
いろいろなパッケージを組み合わせてオリジナルのパッケージを作っているって改めてすごい。

パッケージにはコマンドを提供されているものとされていないものがある

コマンドが提供されている場合、node_modules/.bin/ 配下にコマンドが配置されます。

このことを覚えておけば、のちのち腑に落ちそうです。

実行ファイルに拡張子がついていないのは「コマンド」として打ちやすいからなのでしょうか...。中身はjsファイルですね。 そもそも .bin とは?ってなりますが、別の機会に調べます。

最後に

今までこのような「ローカルでインストールしたのにコマンドが見つからない」、といった問題にぶつかったことないのはなぜなのだろう、と考えたところ、直接コマンドを叩く機会がなかったからだと気づきました。

Node.jsのrequireでインストールしたモジュールを読み込んで、処理のコマンドをpackage.jsonでnpm-scriptとして実行していたからです。

npm-scriptで gulp sass と何の疑問を持たずに書いてきましたが、 ここにきて gulp コマンドって何でしょうか...気になります。

なにはともあれNode.jsの理解が深まってよかったです。

これまでNode.jsは単にgulpを使うために必要なものものぐらいにしか思っていなかったので、Progateのスライドも勉強になりました。

もう少し詳しく知りたいです!

おしまい。