こんにちは。エンジニアの @daiki です。
僕は会社からWindowsマシンを貸与してもらって使っているのですが、会社では殆どのエンジニアがMacマシンを使っていることもあり、Windowsは少数派。
そんなWindowsですが、実はWindows Vista世代(Ver. 6)ぐらいから Windows PowerShell という強力なシェル機能が提供されるようになりました。
Windowsには、当初から MS-DOS互換の CMD.EXE というCUI環境が用意されていたのですが、長年変わらない使い心地を提供しつつも、最近は機能不足に物足りなさを感じている人も少なくないのでした。
そこで、MicrosoftはWindows VistaにWindowsの管理環境の一つとしてWindows PowerShellをていきょうしたという流れです。
Windows PowerShellは出た当初は逆に機能てんこ盛り過ぎて(&とっつきにくくて)厄介者扱いされていましたが、徐々にWindowsマシンのオペレータには受け入れられつつあり、PowerShell Core(オープンソースのクロスプラットフォームシェル環境)も出てきて少しずつではありますがコミュニティに受け入れられつつあります。
今日はUnixシェルを使い慣れてる方に、少しでも「おっ、すごいな」と思ってもらいたくてこの記事を書いてみています。
PowerShellではコマンドじゃなくてコマンドレット
UnixシェルやMS-DOSプロンプトになれているとコマンドを駆使する場面は多いと思います。組み込みコマンドから path に通した個別のプログラムまで、プロンプトから直接呼び出せるというものです。プログラムを直接呼び出すという性質から、そのプログラムの命名(コール関数・エンドポイント)に強く依存しています。
余談:Unixシェルを使っている人だと誰でもが使う cdコマンドもちゃんとオープンソースのソフトウェアです https://github.com/freebsd/freebsd-src/blob/master/bin/sh/cd.c
PowerShellでもコマンドの概念は変わらないのですが、より規則が厳密に定められたコマンドレットというものになっています。
たとえば、上記でちらっと出した cd
コマンドは、 “Change Directory” の略で命名されたとよく説明されますが、一方で ls
コマンドは “list segments” の略だと言われている(諸説あり)が、 segments は欠落してコマンドになってしまっている・・・。
このような状況を改善するべく、PowerShellではコマンドレットとしてエンドポイントの強い制約が生まれることとなりました。
例えば、 ls
をPowerShellでやろうと思うと
PS: > Get-ChildItem
とする必要がある。
一見めちゃくちゃめんどくせえ・・・と思われるかもしれないが、Get
というVerbと ChildItem
という対象のNounがハイフンで繋がれた初学者にはわかりやすい構造となっています。
もちろん、このVerbはなんでも登録出来るわけではなく、https://docs.microsoft.com/ja-jp/dotnet/api/System.Management.Automation.VerbsCommon?view=powershellsdk-7.0.0この辺にあるとおり、すでに定義済みの語を使うのが標準です。
となると、ある程度やりたいことは決まったコマンドレットで表現されることとなりとってもわかりやすいんです。
もちろん、Unixシェルのように短く呼び出すことも出来ます。
PS > gci
PS > dir # MS-DOS互換エイリアス
PS > ls # Unixシェル互換エイリアス
また、 mkdir
(make dir)は一見PowerShellでも Make-Directory
なんじゃないの?と思われがちだが、上述の厳格なVerb規制によってMakeが使えないため以下の通りになってます。
PS > New-Item -ItemType Directory
PS > ni -ItemType Directory # Alias
ぱっと見はめんどくささもあるかもしれないが、無限に存在する同士からそれっぽいを探したり、使った度に覚えるよりかは想像しやすいし、辞書やマニュアルも引きやすかったりします(これ自体の善し悪しは別として)。
PowerShellはオブジェクトをパイプ出来る
さて、コマンドレットの紹介はPowerShellを語る上で避けて通れないと思ったので触れた訳ですが、正直、こっちの方が僕はびっくりでした。
PowerShellはテキストだけではなく、なんとオブジェクトを渡したり出来る。
PS C:\Users\daiki\tmp> gci
ディレクトリ: C:\Users\daiki\tmp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/01/31 22:48 14 hoge.txt
-a---- 2022/01/31 22:48 14 hoge2.txt
-a---- 2022/01/31 22:48 14 hoge3.txt
こんな感じだったとき、一括リネームしたいなあと思ったらUnixシェルだと、ファイル名をどうにか mv
コマンドに入れられる感じに整形して、それを forループで・・・みたいな感じになってしまう。for ループは避けられないが、PowerShellだともうちょっと簡単に書ける。
PS C:\Users\daiki\tmp> $i = 0
PS C:\Users\daiki\tmp> $files = Get-ChildItem
PS C:\Users\daiki\tmp> foreach ($file in $files){
>> Rename-Item $file "moge${i}.txt"
>> $i++}
PS C:\Users\daiki\tmp> Get-ChildItem
ディレクトリ: C:\Users\daiki\tmp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/01/31 22:48 14 moge0.txt
-a---- 2022/01/31 22:48 14 moge1.txt
-a---- 2022/01/31 22:48 14 moge2.txt
こういうかんじで書けてしまうんです。
肝は、$files = Get-ChildItem
でオブジェクトを変数に格納していることと、格納したオブジェクトに対して foreachループを実行できているという点です。
文字列を渡しているわけではないのだから、受け手のコマンドレットが受け付けられる形に整形し直す配慮はしなくて良いのがめちゃくちゃ便利です。
他にも関数をシェル内で簡単に定義できるなど、.Net
を書いたことがある人にはとっても使いやすいシェルになっています。
もし、Unixシェルを使い慣れてて毎日使っている方でも、現在お使いの環境でPowerShell(Core)を導入することが出来ますので、ぜひお遊びがてら体験してもらえたら「おぉ~」となっていただけると思います。
https://github.com/PowerShell/PowerShell
おわりに
ここまで2つPowerShellの特徴を述べてきましたが、PowerShellの面白いところはこれらにとどまりません。もし少しでも興味を持って頂けたら公式のドキュメントを眺めながらいじり倒して頂けたら休日の暇つぶしには打って付けかもしれません。