Umi Uyuraのブログ

プログラミング関連の作業ログ

WSLがエラーコード4294967295で起動しない

Windows TerminalでWSLのUbuntuを開こうとしたところ、このようなエラーが出て起動してくれませんでした。

ネットワーク名が見つかりません。

[process exited with code 4294967295 (0xffffffff)]

ふだんならWindowsにログインしてWindows Terminal開けばアクセスできていました。

Windows側から wsl -l --verbose で調べてみたところ、どうやらWSLが止まっているもよう。

>wsl -l --verbose
  NAME            STATE           VERSION
* Ubuntu          Stopped         2

エクスプローラ\\wsl$ を開いて見ても、ふだんなら表示されているはずのUbuntuが消えていました。

f:id:umi-uyura:20220211225353p:plain

とりあえずWSLを起動するコマンドを打ってみます。

>wsl -d Ubuntu
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.72-microsoft-standard-WSL2 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of ...

起動してくれました。

軽く調べてみたところ、WSLのネットワークまわりを変更したときにおこったりすることもあるようですが、直近で特にそういった操作はおこなっていなかったので、何かのタイミングによってこういうことがあるのかも。

Pythonを複数バージョンインストールしているときにwingetでアップデートしてみる

先日WindowsPythonの環境を作った際、wingetとPythonランチャーで複数バージョンのPythonを使えるようにしてみました。

umi-uyura.hatenablog.com

どうやら最近3.10系の新しいバージョンが出たようなので、自分の環境もアップデートしてみることにしました。

Python Insider: Python 3.10.2, 3.9.10, and 3.11.0a4 are now available

現行バージョン(3.10)のアップデート

winget list でインストール済のパッケージの一覧を表示すると、新しいバージョンがあるものについては「利用可能」列にそのバージョンが表示されます。

>winget list python
名前            ID                                     バージョン  利用可能    ソース
-------------------------------------------------------------------------------------
Python 3        Python.Python.3                        3.9.7150.0  3.10.2150.0 winget
Python Launcher {7DE12550-BE09-44DD-BDB4-0EC26BA89DAF} 3.10.7644.0
Python 3        Python.Python.3                        3.10.1150.0 3.10.2150.0 winget
Python 3        Python.Python.3                        3.8.10150.0 3.10.2150.0 winget

私の環境にはPython 3.10、3.9、3.8が入っているのですが、どれも利用可能なバージョンとして 3.10.2150.0 と、3.10系の最新バージョンが表示されていました。

一覧上はどの系統もIDが Python.Python.3 となっていますが、この状態でwingetでアップデートするとどうなるのか?

>winget upgrade --id Python.Python.3
複数のインストール済みパッケージが一致する入力条件を検出しました。入力内容を修正してください。
名前                   ID
-------------------------------------------------------------
Python 3               Python.Python.3
Python 3.9.7 (64-bit)  {0f0bf1a5-3ec1-459b-ab7c-916db941f50d}
Python 3.10.1 (64-bit) {af822d5e-759c-4e77-9696-3cc835cd54a9}
Python 3.8.10 (64-bit) {e9cd241b-9125-4624-9625-ff42d2f3647f}

指定したIDに一致するパッケージが複数あるということで、何やら別々のIDが表示されました。

>winget upgrade --id {af822d5e-759c-4e77-9696-3cc835cd54a9}
見つかりました Python 3 [Python.Python.3] バージョン 3.10.2150.0
このアプリケーションは所有者からライセンス供与されます。
Microsoft はサードパーティのパッケージに対して責任を負わず、ライセンスも付与しません。
Downloading https://www.python.org/ftp/python/3.10.2/python-3.10.2-amd64.exe
  ██████████████████████████████  26.9 MB / 26.9 MB
インストーラーハッシュが正常に検証されました
パッケージのインストールを開始しています...
インストールが完了しました

アップデートされたようです。

改めてインストール済Pythonの一覧を表示してみると、Python 3.10系のみアップデートされていました。

>winget list python
名前            ID                                     バージョン  利用可能    ソース
-------------------------------------------------------------------------------------
Python 3        Python.Python.3                        3.9.7150.0  3.10.2150.0 winget
Python Launcher {7DE12550-BE09-44DD-BDB4-0EC26BA89DAF} 3.10.7644.0
Python 3        Python.Python.3                        3.10.2150.0             winget
Python 3        Python.Python.3                        3.8.10150.0 3.10.2150.0 winget

>py --version
Python 3.10.2

ひとつ前のバージョン(3.9系)のアップデート

先のURLによると3.9系のアップデートも出ているようでしたので、試してみようと思ったのですが。

>winget show --versions python3
見つかりました Python 3 [Python.Python.3]
バージョン
-----------
3.10.2150.0
3.10.1150.0
3.10.150.0
3.9.7150.0
3.9.6150.0
3.9.5150.0
3.9.4150.0
...

2022/2/7時点では、winget用のパッケージとして 3.9.10 は出ていないようでした。

3.10系がでていることもあり、もしかすると3.9系の最新版はwingetに来ないかもしれないのかもと思い、公式サイトからインストーラーを落として入れてみることにしました。

https://www.python.org/downloads/release/python-3910/ からWindows installer (64-bit)をダウンロードして、インストールを実行。

そのあとで、あらためてインストール済のPythonの一覧を表示してみたところ、ちゃんと3.9系のバージョンがあがっていました。

>winget list python
名前            ID                                     バージョン  利用可能    ソース
-------------------------------------------------------------------------------------
Python 3        Python.Python.3                        3.9.10150.0 3.10.2150.0 winget
Python Launcher {7DE12550-BE09-44DD-BDB4-0EC26BA89DAF} 3.10.7644.0
Python 3        Python.Python.3                        3.10.2150.0             winget
Python 3        Python.Python.3                        3.8.10150.0 3.10.2150.0 winget

現行の最新バージョンであればwinget経由でアップデートしていけそうですが、それよりも前の系統については、必要に応じて手動でアップデートしないといけないのかもしれません。

Windows 10で使うランチャーアプリをFlow Launcherにした

macOSではAlfredを使っていましたが、Windows 10でも同じようなコマンド入力式のアプリケーションランチャーを使いたいと思い、いくつか試した結果、最近はFlow Launcherを使っています。

先日、初めてのプラグインを作ってみました。

umi-uyura.hatenablog.com

上の投稿にも書いたのですが、もともと使っていた Hain というアプリの開発が止まってしまったので、乗り換え先を探した結果、Flow Launcherにたどり着きました。

そこで、そのときに候補になったものと選定理由をメモしておこうと思います。

選定にあたっての条件

選定にあたって、個人的に以下のような条件をあげていました。

プラグインが作れる

ランチャー自体の機能としては備わっていなくても、あとで追加できるのであれば自分で作っても良いので。

UWPアプリを起動できる

Microsoft Storeでインストールしたアプリを起動できること。

Hainは対応していなかったので、ショートカットを作って特定のフォルダに集め、それをランチャーに呼び出せるようにしていました。

マルチプラットフォーム

ランチャーは自分の中では利用頻度高いアプリなので、WindowsmacOSで同じ操作感で使えるのであれば、その方が良いかもという考えもありました。

これはプラグインが作れることにも関係していて、せっかく作るのであればmacOSでも使えると良いなと思っていました。

候補と個人的感想

今回試してみたものは以下のような感じ。

※数値は2022/1/30時点

アプリ Star Fork 備考
Cerebro 7215 471 マルチプラットフォーム(Win/mac/Linux)
ueli 2222 175 マルチプラットフォーム(Win/mac)、プラグイン機能なし
Keypirinha 850 20 プラグイン豊富
PowerToys Run 67,345 3,790 PowerToysの機能の一つ、Woxベース
Wox 21,289 2,291 PowerToys Runのベース
Flow Launcher 756 53 Woxベース

Cerebro

Cerebro App – open-source productivity booster with a brain

実はHainを使っているころから目をつけていて、Hainが止まったのを知って、最初はこちらに乗り換えようとしました。

ElectronベースでマルチプラットフォームWindows/macOS/Linux)で使える点と、JavaScriptプラグインを作れる(npm経由で配布)点が魅力でした。

当時は個人でmacOSも併用していたので、プラグインを自作したときにWindowsでもmacOSでも使えると便利だなと思い、環境の共通化という観点で良さそうだなと思ったのでした。

ただ、いざ今回乗り換えようとしたところ、最新版の0.4.0は肝心のWindowsビルドに問題があるようで配布されておらず、手元でビルドしてもうまくいかなかったので、導入は断念しました。

ueli

ueli - A keystroke launcher for Windows and macOS

これもElectronベースでWindowsmacOSに対応しているランチャー。

触ってみたところ、非常に高速に動作するしUWPアプリにも対応しているので良さそうだったのですが、プラグイン機能がなかったので、残念ながら今回は見送り。

要望はあがっているようなので、いずれプラグイン作れるようになったら再度検討しても良いかもとは思っています。

Keypirinha

Keypirinha

こちらも機能豊富で、プラグインも作れるので、UWPアプリもプラグインで対応しているので良さそうかなと思ったのですが…

デフォルトのランチャー起動キーバインドCtrl + Win + K だった点は設定で変更できたのでまあ良いのですが、ランチャーに表示された機能を選択する際に Ctrl + n / p で移動ができなかった点が個人的な操作感として慣れなかったので見送りに。

PowerToys Run

microsoft/PowerToys: Windows system utilities to maximize productivity

PowerToysに付属しているランチャー機能。

そういえばそんなのあったなと思って、いろいろ迷っている間に仮で使ってみたらわりと良かったので、しばらく使っていました。

ただ、まだ公式でプラグインがサポートされていないのですが、ベースになっているWoxというランチャーアプリではプラグイン機能がサポートされているので、それならWoxでも良いかもと思って、Woxを試してみることにしました。

Wox

Wox

そんなこんなで、もうこれで決まりかなと思ってWoxをインストールしてみたのですが…

インストールした直後あたりからPCのファンが回る勢いが急激に早くなる症状が発生。

どうやら既知の問題ではあるようで、UWPアプリをインデックス化するときに問題が起きているっぽい感じ。

taking too much cpu resources · Issue #3349 · Wox-launcher/Wox

それで、このIssueのなかで、こっちでは問題でないよ、と名前が出ていたのがFlow Launcherでした。

Flow Launcher

Flow Launcher

これもWoxをフォークして開発されているもののようで、もしかしたら同じ問題が出るのではとヒヤヒヤしながらインストールしてみましたが、こちらは特にCPUがブン回ることもなく、UWPアプリも実行できました。

操作感は同じくWoxをベースにしているPowerToys Runと近く、かつこちらはプラグイン機能もサポートされていたので、

あと標準機能でゴミ箱を空にするが使えたのも個人的に気に入ったところでした(macOSのAlfredでよく使っていた機能がempty trashなので)。

結論

そんなわけで、今はFlow Launcherを使っています。

プラグインも当初はC#Pythonでの開発でしたが、最近JavaScript/TypeScriptもサポートされたので、裾野が広がってくれるといいな。

デスクトップ上のファイルを掃除するFlow Launcherプラグインを作った

Flow Launcher用のプラグインを初めて作ってみました。

umi-uyura/Flow.Launcher.Plugin.DesktopCleanup

プラグインページにも掲載してもらえました。

Flow Launcherとは

Flow LauncherはWindows用のアプリケーションランチャーです。

Macだと Alfred が有名でしょうか。

以前は Hain を使っていたのですが、いつの間にかリポジトリアーカイブ状態になっていて、開発が止まってしまっているようでした。

そこで乗り換え先を探していくつかのランチャーアプリを試してみて、個人的に気に入ったので使ってみているのがFlow Launcherです。

Flow Launcher

wingetでインストールできます。

> winget install "Flow Launcher"

自分でプラグインを作ることができるものを探していて、Flow Launcherは.NET(C#) / Pythonで書くことができます。

※私が2,3ヶ月前に着手したときには上記2つがメインでしたが、最近になってJavaScript/TypeScriptでの開発方法もドキュメントに追加されました。

About Flow's TypeScript/JavaScript plugins

Desktop Cleanupプラグイン

今回作ったのは、デスクトップにあるファイルやフォルダを削除する、もしくはゴミ箱に移動するプラグインです。

私はデスクトップにはゴミ箱以外のファイル等を置かない派なのですが、wingetやChocolateyを使ってアプリケーションをインストールしたりアップデートをするたびにショートカットが作られてしまったりするので、それをサクッと削除したいときに使います。

Flow Launcherを使っている方であれば、ランチャーに pm install desktop cleanup と入れて実行するか、もしくは設定画面のPlugin StoreからDesktop CleanupのInstallボタンを押すことでもインストールできます。

dc clean で削除、 dc trash だとゴミ箱に移します。

ちなみにゴミ箱に入れた場合でも、Flow Launcherで Empty Recycle Bin を実行するとゴミ箱を空にできるので、いきなり削除するのが怖い人でも安心です。

今回のハマりどころ

Publicユーザーのデスクトップ

単純にデスクトップ上にあるファイル(ショートカット)を削除するだけなので大したことないと思っていたのですが、いざやってみたら削除されない、というかそもそもプログラムからも見えないファイルがあって、その解明に時間を使ってしまいました。

見えないと思っていたのは、実際には全てのユーザー向けにインストールされたアプリのショートカットでした。

これは実体としてはPublicというユーザーのデスクトップ上に存在しており、それが個々のユーザーのデスクトップ上にも表示されるというWindowsの仕組みでした。(たしかXPあたりのころはAll Usersという名前だった気がする)

インストーラですべてのユーザー向けにインストールした場合など、実際にはこのような仕組みになっていたんですね。

このため、自分のデスクトップ上では見えているのに、コマンドプロンプトなどでデスクトップフォルダの中を見た場合には(当然)存在していないということになります。

当初、プログラム上から自分のデスクトップのパス( %USERPROFILE\Desktop )にあるファイルを削除するようにしていましたが、そうするとPublicユーザーのデスクトップ( %PUBLIC%\Desktop )にあるものは当然パスに含まれないために消えせん。

最初はなぜ見えないのかわからず、権限の問題?シンボリックリンクの種類?など見当違いの方向を調べていましたが、消せないショートカットのプロパティの場所に表示されているパスを見てようやく気づいた次第です。

基本的にPCは一人でしか使わないですし、デスクトップに何か置かれたらすぐに削除するかどこかに移してしまうこともあって今まで気にしたことがありませんでしたが、今更ながらWindowsのマルチユーザーの仕組みの一端を知りました。

Windows 10でPython環境構築 (wingetとPythonランチャー)

基本的にはPython触るならWSL側にHomebrewとasdfで作った環境を使おうと思いますが、Windows側で何かをするときのために、Windows側にもPython環境を作ることにしました。

umi-uyura.hatenablog.com

まだ何もインストールしていないときの環境。

>where python
C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\python.exe

>python --version
Python

デフォルトで python.exe が存在していますが、バージョン情報を表示しようとしても python としか表示されません。

これを実行するとMicrosoft Storeが起動して、ストア版のPythonのページに飛びます。

そこからインストールすることもできますが、ストア版のPythonには制限もあるようなので、Python公式サイトで配布されるインストーラ版を使うことにしました。

参考)

Pythonをインストールする

公式サイトからダウンロードしても良いですが、wingetでもインストールできます。

> winget install -s winget -e --id Python.Python.3

refreshenv コマンドで環境変数を読み直すと、Python関連のパスが2つ追加されていました。

>refreshenv
Refreshing environment variables from registry for cmd.exe. Please wait...Finished..

>set | findstr /i python
Path=(中略)C:\Users\<user>\AppData\Local\Programs\Python\Python310\Scripts\;C:\Users\<user>\AppData\Local\Programs\Python\Python310\;(以降、略)

Pythonコマンドの実体を調べてみます。

>where python
C:\Users\<user>\AppData\Local\Programs\Python\Python310\python.exe
C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\python.exe

>where pip
C:\Users\<user>\AppData\Local\Programs\Python\Python310\Scripts\pip.exe

>where pip3
C:\Users\<user>\AppData\Local\Programs\Python\Python310\Scripts\pip3.exe

>where py
C:\Windows\py.exe

もともと存在していた python.exe に加えて、追加されたPATHの方にも python.exe が見つかりました。

また、複数のPythonのバージョンを切り替えて使えるPythonランチャー(py.exe)もインストールされていることを確認できました。

python でも py でも、バージョン情報が出力されることを確認。

>python --version
Python 3.10.1

>py --version
Python 3.10.1

参考)

PATHは必ずしも通っていなくて良いらしい。

確かに今後別のバージョンを追加したときに混乱しそうなので、いったん以下は環境変数から消して使ってみることにしました。

C:\Users\<user>\AppData\Local\Programs\Python\Python310\
C:\Users\<user>\AppData\Local\Programs\Python\Python310\Scripts\

※このあと3.9を追加インストールする際に、PATHを追加せずにインストールする方法を記載しています。

Pythonランチャー(py.exe)で複数バージョンを切り替えてみる

Pythonランチャーで別のバージョンを使いたい場合は、 py <version> と使いたいバージョンを指定して実行します。

>py <version> <script>      # 最新バージョンで実行
>py -3 <script>             # 3系の最新バージョンで実行
>py -3.9 <script>           # 3.9で実行

指定したバージョンがインストールされていない場合は、その旨表示されます。

>py -3.9 --version
Python 3.9 not found!
Installed Pythons found by py Launcher for Windows
 -3.10-64 *

Requested Python version (3.9) not installed, use -0 for available pythons

試しに -3.9 を指定してみると上記のようなメッセージが表示されましたので、追加で3.9をインストールしてみます。

wingetでインストールできる過去のバージョンは winget show --versions <query> で取得できます。

>winget show --versions --id Python.Python.3
見つかりました Python 3 [Python.Python.3]
バージョン
-----------
3.10.1150.0
3.10.150.0
3.9.7150.0
3.9.6150.0
3.9.5150.0
...

3.9系の最新である 3.9.7150.0 をインストールするため、wingetに --version オプションを付けてバージョンを指定して実行します。

>winget install -s winget -e --id Python.Python.3 --version 3.9.7150.0 --override PrependPath=0

また --override PrependPath=0 を付けることで、環境変数PATHへの追加されずにインストールすることができます。

このときインストーラの画面が開くので、 Add Python 3.9 to PATH のチェックが外れていることを確認して、「Install Now」を選べばOKでした。

f:id:umi-uyura:20211214233022p:plain

インストール後に python の所在を確認してみます。

>where python
C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\python.exe

>py --version
Python 3.10.1

>py -3.9 --version
Python 3.9.7

PATHは追加されていませんが、 py -3.9 で3.9版のバージョンが表示されました。

現在の環境にインストールされているバージョンを一覧表示するには --list もしくは --list-paths を使います。

>py --list
Installed Pythons found by py Launcher for Windows
 -3.10-64 *
 -3.9-64

>py --list-paths
Installed Pythons found by py Launcher for Windows
 -3.10-64       C:\Users\<user>\AppData\Local\Programs\Python\Python310\python.exe *
 -3.9-64        C:\Users\<user>\AppData\Local\Programs\Python\Python39\python.exe

* が付いているバージョンがデフォルトで使われるもので、これは設定で変更することができます。

参考)

Pythonランチャー+venv

Pythonランチャーでvenvが使えるかを確認します。

>py --version
Python 3.10.1

>py -3.9 -m venv .venv

デフォルトが 3.10 の状態で、 3.9 を指定してvenv環境を作ります。

>.venv\Scripts\activate

(.venv) >py --version
Python 3.9.7

activate すると、何も指定しないで実行した py が返すバージョンが 3.9 になりました。

(.venv) >where python
C:\Users\<user>\workspace\python\pyexe-test\.venv\Scripts\python.exe
C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\python.exe

作成したvenv環境の python が優先されるようになっていました。

(.venv) >py --list-paths
Installed Pythons found by py Launcher for Windows
 (venv)         C:\Users\<user>\workspace\python\pyexe-test\.venv\Scripts\python.exe *
 -3.10-64       C:\Users\<user>\AppData\Local\Programs\Python\Python310\python.exe
 -3.9-64        C:\Users\<user>\AppData\Local\Programs\Python\Python39\python.exe

py --list-paths してみても、venv環境が追加されていました。


最新版以外を使う場合は、都度使うバージョンを指定する必要がありますが、いちいち切り替えなくて済むという点では、pyenv方式よりPythonランチャーの方が使い勝手は良いのかも。

とりあえず、Windows側でPython使うときはPythonランチャーを意識して使ってみます。

参考

Chocolateyをアップデートしたらexe.oldへのアクセス拒否が出た

少し前ですが、Chocolateyにアップデートが来ていたのでアップデートしたのですが、その後使おうとしたときに、何やらアクセス拒否されたというエラーが出ました。

>choco
Chocolatey v0.11.3
Please run 'choco -?' or 'choco <command> -?' for help menu.
This is try 1/3. Retrying after 300 milliseconds.
 Error converted to warning:
 パス 'C:\ProgramData\chocolatey\choco.exe.old' へのアクセスが拒否されました。
This is try 2/3. Retrying after 400 milliseconds.
 Error converted to warning:
 パス 'C:\ProgramData\chocolatey\choco.exe.old' へのアクセスが拒否されました。
Maximum tries of 3 reached. Throwing error.

このエラー自体は gsudo 付きで choco コマンド単発で呼び出すだけで解消されました。

>gsudo choco
Chocolatey v0.11.3
Please run 'choco -?' or 'choco <command> -?' for help menu.

>choco
Chocolatey v0.11.3
Please run 'choco -?' or 'choco <command> -?' for help menu.

原因については想像ですが、以前は choco コマンドを使うときは管理者権限のコマンドプロンプトを起動して使っていましたが、最近は gerardog/gsudo を使って gusodo choco upgrade chocolatey のように管理者権限付きで実行するようにしていたのですが、もしかするとその違いによって、アップデート処理の中で、権限が適用されなくなってしまう処理などがあるのかもと推測しています。

実際にアップデートしてから、このエラーに遭遇するまで間があいていたこともあるので、またChocolateyのアップデートが降ってきたら確認してみようと思い、とりあえずメモしておきます。

WindowsのNode.jsバージョン管理をNodistからfnmに変えてみる

これまでWindowsでNode.jsのバージョン管理はNodistを使っていましたが、改めて環境構築を構築をしていくにあたってMicrosoftのドキュメントを眺めていたところ、Nodist以外にもそういったツールがあることを知りました。

WSL2(Linux)向けの方で掲載されているパッケージ管理ツールのほうが多いですが、その中のいくつかはマルチプラットフォームで、ネイティブWindowsでも使えそうなものもあります。

2020 年ではもう使えない Nodist はアンインストールする (Windows)

この記事に触発されたというわけでもないのですが、最近WSLを触り始めたこともあり、Windows/Mac/Linux(WSL)で同じように使えるツールにしておけると迷うことも少ないかなと思い、Nodistから乗り換えてみることにしました。

マルチプラットフォームなNode.jsバージョン管理ツール

Windowsをサポートしているマルチプラットフォームなものをピックアップしてみました。

それぞれのリポジトリや公式サイトを眺めてみての個人的に気になった点を挙げてみました。

名前 対応プラットフォーム ポイント
nvm-windows Windows nvmWindows版、なのかと思いきやが互換性があるわけではないみたい
・Go製
・自動バージョン切り替えはない
nvs Windows/Mac/Linux VSCode連携あり
コマンドプロンプトのサポートが弱い(PowerShellだと自動バージョン切り替えができる)
・wingetでインストールできる
Volta Windows/Mac/Linux ・Rust製、速度重視
・他のツールと若干使い方が違う印象
・package.jsonに使うNodeのバージョンなどを記録する
・wingetでインストールできる
fnm Windows/Mac/Linux ・Rust製、こちらも速度重視
・自動バージョン切り替えに対応
(番外)asdf-nodejs Mac/Linux asdf公式
・自動バージョン切り替えに対応

Nodistも備えていましたが、プロジェクトディレクトリにある .node-version.nvmrc などを参照して使うNodeのバージョンを切り替えてくれる機能は欲しいところ。

Linux(WSL)とMacだけで考えれば asdf公式のNode.jsプラグイン が自動切り替えにも対応していて良さそうではありますが、Windowsでも使えるものからとなると、nvs、Volta、fnmあたりが選択肢になりそうです。

(nvm-windowsは紛らわしいですが、本家nvmのクローンということではないらしい。コマンドも微妙に使い方が違っていたりと今回の目的に合わないため選外)

その中でnvsとVoltaについては以下のような点が自分的に気になったので、今回はfnmを使ってみることにしました。

  • nvsコマンドプロンプト主体で考えると切り替え機能がサポートされない
  • Voltaは他のツールと違ってpackage.jsonにバージョンを記録する(業務利用を考えたときに、自分の会社ではそのあたりのツールが統一されているわけではないため、自分のためだけに編集はしにくい)

Nodistをアンインストールする

まずは既存環境のNodistを削除する手順で、この点については先のNodistアンインストールの記事がとても参考になりました。

2020 年ではもう使えない Nodist はアンインストールする (Windows)

以下、その記事の手順をなぞったときのメモ。

  • Nodistのアンインストール
    • 記事ではコントロールパネルから実施していましたが、私はChocolateyでインストールしていたので、 choco uninstall nodist で削除しました
  • Nodistインストール先フォルダの削除
    • 確かに残っていて、npmの各バージョンが残っていたと1GB近くありました
  • npm-cacheフォルダの削除
    • これも残っていて、3.5GBもありました…
  • .npmrcの削除
    • これについては、私の環境ではNodistに関するもの以外の設定もあったので、該当行のみ削除してファイル自体は残しました

fnm (Fast Node Manager)をインストールする

インストールは簡単で、 Releases · Schniz/fnm から利用しているプラットフォーム向けのバイナリをダウンロードして、PATHが通っているところに置くだけでも使えます。

またWindows向けにはChocolateyからもインストールすることもできるので、私はこちらの方法を使いました。

# 要管理者権限
> choco install fnm -y

or

# Use gsudo
> gsudo choco install fnm -y

gerardog/gsudo を使うと、わざわざ管理者権限でターミナルを開き直さなくても良いので便利です。

コマンドプロンプト向けシェルセットアップ

さてインストールが完了したら、使っているシェルに合わせてfnmを使うための初期化処理を設定する必要があります。

Bash向けなどは .bashrceval "$(fnm env)" を追記すれば良いという感じで非常に簡単なのですが、Windows特にコマンドプロンプト向けのセットアップは一筋縄ではいきませんでした。

Windows Command Prompt aka Batch aka WinCMD

コマンドプロンプトを起動したときに何か処理をおこないたい場合、レジストリHKEY_CURRENT_USER\SOFTWARE\Microsoft\Command ProcessorAutoRun キーに設定することで実行させることができます。

fnmのREADMEによれば、そこに以下のコマンドを登録することでセットアップがおこなわれるようです。

FOR /f "tokens=*" %i IN ('fnm env --use-on-cd') DO CALL %i

ただ自分の場合は、他にも初期化処理でやりたいことがあったので、バッチファイルを登録して、その中で上記の処理を呼び出すことにしました。

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Command Processor]
"AutoRun"="C:\Users\<user>\mystartup.cmd"

ところがこれがうまくいかず、コマンドプロンプトを起動すると i was unexpected at this time. というメッセージが表示されました。

これはfnmのREADMEにも対処法が書かれていますが、原因としてはバッチファイルの中で FOR を使う場合の変数の書き方を %i ではなく %%i としなければいけないからでした。

for | Microsoft Docs

よって以下のように書き換えます。

FOR /f "tokens=*" %%i IN ('fnm env --use-on-cd') DO CALL %%i

これでうまくいくかと思いきや、確かに先のメッセージは出なくなったものの、今度は処理が戻ってこない状態になりました。

いろいろ調べた結果、次のことがわかりました。

  • for /f はサブプロセス実行時に cmd.exe を起動するため、そこでまたAutoRunが走ってしまうことで無限ループに陥る可能性がある
  • cmd /d を指定することでAutoRunを無効にできるが、 for の中では効かないっぽい

参考:

fnmのREADMEにはCmderというターミナルアプリの起動スクリプト機能を使うことで回避する方法が載っていますが、これだとWindows Terminalが使えないので個人的にはイマイチ。

Usage with Cmder

そこで起動バッチのなかで多重実行を防ぐような仕組みを入れたところ、無限ループにならずに初期化処理が実行できるようになりました。

具体的には初回実行時にある環境変数に値を入れるようにして、実行時にその環境変数に値がある場合はそこで処理を終了するというもので、以下のような実装です。

mystartup.cmd

@ECHO OFF

IF "%MYSTARTUP_INIT%"=="OK" (
    EXIT /b
)

ECHO ... Running startup script
SET MYSTARTUP_INIT=OK

REM Setup fnm
FOR /f "tokens=*" %%z IN ('fnm env --use-on-cd') DO CALL %%z

ECHO ... Complete startup script

これでfnmを使うことができるようになりました。

基本的な使い方

# インストール可能なバージョンの一覧
> fnm ls-remote
v0.1.14
v0.1.15
v0.1.16
... # 省略
v16.8.0
v16.9.0
v16.9.1
v16.10.0

# 指定したバージョンをインストール
> fnm install v16.10.0
Installing Node v16.10.0 (x64)

# インストール済のバージョン一覧
> fnm ls
* v14.17.6
* v16.10.0 default
* system

# 現在のデフォルトで使われるバージョン
> fnm current
v16.10.0

# デフォルトバージョンの設定
> fnm default v14.17.6
> node -v
v14.17.6

自動バージョン切り替えを試してみる

あらかじめ、16系と14系をインストールしておきます。現在のデフォルトは16。

> fnm install 14
Installing Node v14.17.6 (x64)

> fnm list
* v14.17.6
* v16.10.0 default
* system

わかりやすく node14node16 フォルダを作り、それぞれに対応するバージョンを書いた .node-version ファイルを配置します。

> type node14\.node-version
v14
> type node16\.node-version
v16.10.0

それぞれのフォルダに移動してみます。

> cd node14
Using Node v14.17.6

> fnm current
v14.17.6

> node -v
v14.17.6

> cd ..\node16
Using Node v16.10.0

> fnm current
v16.10.0

> node -v
v16.10.0

node14 フォルダに入るとカレントバージョンが14に、 node16 フォルダに移動するとカレントバージョンは16になりました。

.node-version ファイルの内容に合わせて使うNodeバージョンが切り替わってくれることが確認できました。

※2021/10/06追記

プロジェクトのバージョン指定に使うファイルは .node-version だけでなく、nvm由来の .nvmrc を使うツールもあります。

fnmは .nvmrc にも対応しているようだったので試してみたところ、同じようにバージョンが切り替わってくれました。

> type node14\.nvmrc
14

> type node16\.nvmrc
v16.10.0

> node -v
v14.17.6

> cd node16
Using Node v16.10.0

node16> fnm current
v16.10.0

node16> node -v
v16.10.0

node16> cd ..\node14
Using Node v14.17.6

node14> fnm current
v14.17.6

node14> node -v
v14.17.6

おわり

Rust製で売りの一つにスピードとあるだけに、たしかに速い気がします。

Linux(WSL)やMacにもHomebrewで入れられるので、そちらにもセットアップしていこうと思います。