本連載では、ここ数回に渡り、Windowsで「MSYS2」を使ってLinuxコマンドを使う環境を整えている。前々回までである程度は整ったものの、そのままだとWindowsとMSYS2とでいくつかのコマンドがぶつかるので使いにくい。この問題を解決するために、前回は動的に設定をMSYS2に寄せたり、Windowsに寄せたりする方法を説明したわけだが、Powershellの話が登場したので、理解しきれなかった方もいたかもしれない。本連載はLinuxがテーマなので、PowerShellに関して詳しい説明は行わないつもりだが、前回登場した設定ファイルが読めるくらいには説明しておきたいと思う。

まず、現在のWindowsにおけるデフォルトシェルはPowerShellとされている。と言っても、状況は少々複雑だ。本稿執筆時点でWindows 10およびWindows 11にプレインストールされているシェルは「Windows PowerShell 5.1」である。しかし、Microsoftは「このシェルは互換性維持のためだけに残している」と説明しており、以降はオープンソースで開発されている「PowerShell 7(またはPowerShell Coreとも呼ばれる)」を使うことが推奨されている。ただし、PowerShell 7はデフォルトではインストールされていない。

そのため微妙なところなのだが、今後のことも考えるとWindows 10でもWindows 11でもPowerShell 7を使うというのが妥当なところではないかと思われる。

前回、PowerShell 7向けに作成した「MSYS2寄りの使い方をするための設定」は、次の通りだった。

function mode_msys2 {
        # MSYS2のコマンドパスを最優先に入れ替える
        $sshpath = "C:\WINDOWS\System32\OpenSSH"
        $msyspath = "C:\msys64\usr\bin"
        $mingw64path = "C:\msys64\mingw64\bin"

        Set-Item Env:Path $env:Path.Replace($sshpath,"")
        Set-Item Env:Path $env:Path.Replace($msyspath,"")
        Set-Item Env:Path $env:Path.Replace($mingw64path,"")

        Set-Item Env:Path "$mingw64path;$env:Path"
        Set-Item Env:Path "$msyspath;$env:Path"
        Set-Item Env:Path "$sshpath;$env:Path"

        # MSYS2コマンドと重複するエイリアスを削除する
        Get-Alias cat > $null 2>&1 && Remove-Alias cat
        Get-Alias cp > $null 2>&1 && Remove-Alias cp
        Get-Alias ls > $null 2>&1 && Remove-Alias ls
        Get-Alias mv > $null 2>&1 && Remove-Alias mv
        Get-Alias rm > $null 2>&1 && Remove-Alias rm
        Get-Alias diff > $null 2>&1 && Remove-Alias -Force diff
        Get-Alias sort > $null 2>&1 && Remove-Alias -Force sort
        Get-Alias tee > $null 2>&1 && Remove-Alias -Force tee

        # ls系短縮コマンドを関数として作成
        function global:ls { C:\msys64\usr\bin\ls.exe --color $Args }
        function global:ll { C:\msys64\usr\bin\ls.exe --color $Args -l }
        function global:la { C:\msys64\usr\bin\ls.exe --color $Args -a }
}
mode_msys2

PowerShellは「$PROFILE」という変数に設定ファイルのパスが格納されているのだが、この設定ファイルにPowerShellスクリプトを書いておけば、それがPowerShell起動時に実行される仕組みになっている。これを利用して、このファイルで設定を切り替える関数を定義しておき、状況に応じて関数を実行する仕組みにしよう、というのが前回解説した内容だ。

上記のような内容で「mode_msys2」という関数を定義している。関数を定義した後にその関数を呼び出して実行しているので、MSYS2寄りの設定がデフォルトになっている。

変数

ここでは、Linuxの/bin/sh (Bourne Shell)と比較しながら、PowerShellについてざっくり説明する。PowerShellは見た目はシェルっぽいシンタクスだが、実態はオブジェクト指向のスクリプト言語であり、要所要所で勘所がLinuxのシェルとは異なっている。以降を読み進めるにあたっては、その辺りを覚えておいていただきたい。

まず次に示すコードは、PowerShellで変数を定義している部分だ。PowerShellにはLinuxのシェルとは異なり、きっちりと型の概念があるため、次のコードは文字列を格納する変数を用意しているということになる。

# MSYS2のコマンドパスを最優先に入れ替える
$sshpath = "C:\WINDOWS\System32\OpenSSH"
$msyspath = "C:\msys64\usr\bin"
$mingw64path = "C:\msys64\mingw64\bin"

Linuxのシェルでも変数は「$val」のようにドル記号を使って表記するわけだが、変数の作成時にはドル記号を指定しない。しかし、PowerShellでは変数の作成時にもドル記号を指定する。慣れないと違和感があると思うが、これはもうこういうものなので、慣れてしまったほうがよい。

環境変数とパスの削除

Linuxでは、環境変数PATHに実行可能なファイルが収められたディレクトリのリストを書いておく。パスは「:(コロン)」区切りでつなげて書く。Windowsでも実行可能なファイルが収められたフォルダは環境変数Pathに設定する。区切り文字は「;(セミコロン)」だ。

しかし、PowerShellでは環境変数のアクセスは「$Path」ではなく「$Env:Path」となる。そして、環境変数パスから特定のパスだけを削除する処理は、次のように書く。さっきの説明と矛盾すると感じるかもしれないが、こちらでは設定する際、「$Env.Path」ではなく「Env.Path」とドル記号は指定せずに書く。

Set-Item Env:Path $env:Path.Replace($sshpath,"")
Set-Item Env:Path $env:Path.Replace($msyspath,"")
Set-Item Env:Path $env:Path.Replace($mingw64path,"")

「$env:Path.Replace($sshpath,”“)」は、かなりオブジェクト指向プログラミング言語的な書き方だ。これで「$sshpath」に書いてあるパスが「”“」に置換されるので、結果として削除されることになる。この結果を「Set-Item Env:Path」で環境変数Pathに設定し直しているわけだ。オブジェクト指向プログラミングの書き方と、シェルとスクリプトの書き方が混在したような書き方である。これは、結構PowerShellに特徴的な書き方かもしれない。

「Set-Item」というのは、PowerShellに組み込みで用意されているコマンドで「コマンドレット」と呼ばれる。Linuxのシェルで言うところの組み込みコマンドだと考えてもらうとわかりやすいと思う。

また「Set-Item」という長い名前だが、これはPowerShellの命名規則にのっとっている。PowerShellではコマンドレットのような名称は「動詞-名詞」というかたちを取る。読むだけで何となく意味がわかるという利点と、記憶が怪しくても補完機能を使うと名前が出てくるという利点がある。逆に、コードが冗長になりがちなのが欠点だろうか。

環境変数とパスの追加

次のコードは先ほどの逆だ。環境変数Pathにパスを追加している。こちらはLinuxのシェルで追加する際の方法と似ている。Linuxの/bin/shなら「PATH=/path/to/something/:$PATH」といった書き方をするところを、PowerShellでは次のように書く。

環境変数Pathへ特定のパスを追加する書き方

Set-Item Env:Path "$mingw64path;$env:Path"
Set-Item Env:Path "$msyspath;$env:Path"
Set-Item Env:Path "$sshpath;$env:Path"

区切り文字が「:(コロン)」ではなく「;(セミコロン)」になる点に注意しよう。

エイリアスの削除

次のコードはPowerShellのエイリアスを削除するコードだ。Linuxのシェルであればunalias組み込みコマンドが該当する機能となる。

Get-Alias cat > $null 2>&1 && Remove-Alias cat
Get-Alias cp > $null 2>&1 && Remove-Alias cp
Get-Alias ls > $null 2>&1 && Remove-Alias ls
Get-Alias mv > $null 2>&1 && Remove-Alias mv
Get-Alias rm > $null 2>&1 && Remove-Alias rm

この部分はちょっと複雑だが、mode_msys2を繰り返し実行した場合にエラーが出ないようにする際などに使える実用的な書き方である。処理の本質としては、次のコードだけあればよい。「Remove-Alias」が「unalias」と同等の処理だ。

Remove-Alias cat

では、このコードの前に書いてある次の処理はいったい何なんだ、ということになるわけだが、これは指定された名前のエイリアスが存在しているかどうかを調べている処理だ。 Get-Alias cat > $null 2>&1

エイリアスが存在すればエイリアスを削除し、そうでなければ何もしない。「&&」はLinux shの「&&」と同様に、「&&」の左側の式評価が「True」になるなら、右側の式を評価する、という意味だ。

ただし、「Get-Alias」だけでは出力が伴ってしまうので、「> $null 2>&1」で標準出力と標準エラー出力を削除している。これはLinux shでの「> /dev/null 2>&1」と同じだ。Windowsには「/dev/null」というファイルは存在しないが、その代わりに「$null」が使える。

次の処理は「Remove-Alias」に「-Force」というパラメータが指定されている。

Get-Alias diff > $null 2>&1 && Remove-Alias -Force diff

Linux shなら組み込みコマンドに渡されるハイフンから始まる引数は「オプション」と呼ばれるが、PowerShellでは「パラメータ」と呼ばれる。PowerShellのエイリアスには「リードオンリー」というフラグを設定でき、このフラグが設定されたエイリアスは削除できない。リードオンリーのエイリアスを削除するには「-Force」というパラメータが必要だ。このため、上記のような書き方をしている。

短縮コマンドを関数で代用

Linux shなら「ll」で「ls -l」のように便利な短縮コマンドが設定されていることが多い。これにはエイリアスの機能が使われるのだが、PowerShellのエイリアスはかなり機能が制限されている。本当にコマンド名や関数名を「置き換える」だけの機能であり、そこにパラメータを含めたり、処理を含めたりすることはできない。このため、「ls -l」のようにオプションを含んだエイリアスは設定できないのである。

次の処理では、関数で短縮コマンドを実現している。

# ls系短縮コマンドを関数として作成
function global:ls { C:\msys64\usr\bin\ls.exe --color $Args }
function global:ll { C:\msys64\usr\bin\ls.exe --color $Args -l }
function global:la { C:\msys64\usr\bin\ls.exe --color $Args -a }

PowerShellはプログラミング言語であり、関数の機能は強力だ。そこで、短縮コマンドを上記のように関数を使って実現しているのである。

――と、こんな感じの処理を行っているのが、前回取り上げた設定ファイルの中身だ。PowerShellを使っていれば何ということのない書き方なのだが、使ったことがないと少々読み取りにくいかもしれない。

LinuxでPowerShellを使うという手もあり

PowerShell 7はオープンソースソフトウエアであり、Linuxでも利用できる。もし、業務の大半がWindowsで行われているのなら、Linuxで使うインタラクティブシェルやシェルスクリプトにPowerShell 7を使うというのは、それほど悪くない選択肢ではある。WindowsとLinuxで同じスキルが利用できるので、時短を実現したいユーザーには一考の価値があるのではないだろうか。

逆に、Windowsで/bin/shといったLinuxのシェルを使い倒すという方法もあるのだが、こちらはWindowsの本領を発揮できない。メインの環境がWindowsならPowerShellを選ぶほうが何かと便利なのだ。選択肢の一つとして、PowerShellについても気にかけてもらればと思う。

付録:MSYS2インストール方法など

環境変数 内容
Path C:\msys64\usr\bin
C:\msys64\mingw64\bin

MSYS2のインストール方法

winget install MSYS2
pacman -Syu
コマンド 内容
pacman -S パッケージインストール
pacman -Ss パッケージ検索
pacman -R パッケージアンインストール
pacman -Rs パッケージおよびそのパッケージのみが必要としているパッケージをアンインストール
pacman -Rns パッケージおよびそのパッケージのみが必要としているパッケージおよびバックアップファイルをアンインストール
pacman -Fx 指定したファイルが含まれているパッケージを一覧表示
pacman -Fl 指定したパッケージがインストールするファイルを一覧表示
pacman -Q インストール済みパッケージ一覧表示
pacman -Syu メタデータアップデートとパッケージアップグレード