🚀

PowerShell「全員が全員 /bin/bash だと思うなよ」

2022/10/21に公開

はじめまして.ちょっと株式会社で技術顧問をしています,池口といいます. 普段は別の会社で働きつつ,副業という形で参画させていただいております. ブログもたまに書こうかと思っておりますので,よろしくお願いします.

そもそもシェルとは?

さて,みなさんがお使いのシェルは何でしょうか. シェルは, OS の機能を呼び出したり別のアプリケーションを呼び出したりするためのコマンド言語インタプリタです[1] . 具体的には bash や fish , zsh などが挙げられます. macOS の場合,既定のシェルは zsh です.これをそのまま使っているという方も多いでしょう. GNU/Linux の場合はほとんどのディストリビューションで bash が採用されています. bash や zsh は POSIX 互換モードを持っている[2][3] こともあってか幅広く使われています.

しかし,これらのシェルはとても伝統的 (traditional) なインターフェースを持ちます. 入出力はともに文字列が想定されているため,私たちはすべてを文字列として扱う必要があります. したがって私たちの書くシェルスクリプト[4] は,そのスクリプトの持つ機能の拡大に合わせて,とても複雑で冗長なものとなっていました.

だから私は,PowerShell

ここでみなさんが普段シェルスクリプトで扱うデータを考えてみましょう. 例えば,あるコマンドを実行して(これはビルトインではなく,アプリケーションの実行可能ファイルによって定義されるコマンドとします), その結果から必要な情報を取り出し,集計して書き出す.こういったものを書くことはしばしばあるかと思います. ここで目をつけてほしいのは,私たちが扱いたいものはコマンドが書き出した文字列ではなく,その表現するデータだということです.

こういったケースでコマンドが JSON[5] 文字列を書き出す場合は, jq[6] といったコマンドにパイプ[7] することも多いでしょう. そうして得られたデータをまた,他のシェルスクリプトから文字列処理を利用して扱ったりします. シェルスクリプトで実現したいことの本質は JSON 文字列をパースしてデータを取り出すことではなく,データを処理して書き出すロジックにあることを忘れがちです.

そこで私が紹介したいのが PowerShell[8] です. この言葉を聞いて「私は Windows ユーザではないので関係ない」と感じた方もいるかと思いますが,実は PowerShell はクロスプラットフォームなツールです. 語弊のないように言うと, PowerShell Core がそうです. Windows 用の共通言語ランタイム (CLR) である .NET Framework から派生した .NET Core というオープンソースプロジェクトがありますが (現在は .NET という名前で合流しました), .NET Core はクロスプラットフォームです. PowerShell が .NET Framework ベースなのに対して, PowerShell Core は .NET Core をベースにして開発されたためにクロスプラットフォームとなりました. この記事ではこれら 2 つをまとめて PowerShell と呼ぶことにします[9]

PowerShell の薀蓄をひとしきり語ったところで,シェルの紹介に戻ります.名前の通り PowerShell もシェルの一つです.PowerShell の特徴というと,データをデータのまま扱うことができる点が一番に挙げられます.コマンドが返すのは文字列ではなく,データなのです.これは正確には .NET におけるオブジェクトです.つまり,シェルでありながら,コマンド間で .NET オブジェクトを渡すことができるのです..NET を扱ったことのある方ならピンときたかもしれませんが, LINQ[10] . を使ったデータの絞り込みなどをすることももちろん可能です.

これを分かりやすく説明するために,簡単な例を見てみましょう.マシンに紐付いている IP アドレスのうち, 127. で始まるもの[11] を除いたものを列挙することを考えます

一般的な GNU/Linux システム[12] 上の bash では以下のように書くことができます:

ip addr | grep inet | cut -d ' ' -f 6 | grep -v '^127.'

ワンライナーで書くことはできるのですが,あまり直感的ではないことが分かります.複雑な条件が増えたり,より高度なフィルタリングの要件ができたときにメンテナンスしづらいでしょう.さらに,文字列処理で必要な値を取り出しているため変更にとても弱い[13] です.

PowerShell で同じことをしようとすると,以下のようになります:

Get-NetIPAddress | where IPAddress -NotLike "127.*" | select IPAddress

かなり直感的に書けることがわかるかと思います. jq などを使ったことのある方なら一目で理解できるでしょう.そして,何よりもデータ目線で絞り込み及び抽出ができていることがおわかりいただけるかと思います.この例から PowerShell の思想のすばらしさが伝われば幸いです.

おわりに

今回は PowerShell を紹介しました.普段 bash や zsh しか触れない方も,ぜひ様々なシェルに触れて,自分好みのものを見つけてみてはいかがでしょうか.


Cover image by Shutterstock

脚注
  1. https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html ↩︎

  2. https://www.gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html ↩︎

  3. https://zsh.sourceforge.io/Doc/Release/Invocation.html ↩︎

  4. シェルに実行させるコマンド群をまとめて記述したもの.一括実行できるため環境によってはしばしばバッチスクリプトとも呼ばれます. ↩︎

  5. JavaScript Object Notation ↩︎

  6. https://stedolan.github.io/jq/ ↩︎

  7. あるコマンドの出力を,別のコマンドの入力として扱うようにリダイレクトすること. ↩︎

  8. https://docs.microsoft.com/en-us/powershell/ ↩︎

  9. .NET Framework と .NET Core が合流したように, PowerShell と PowerShell Core もいずれ合流するでしょう.また, PowerShell Core はバージョン 7 から始まったため PowerShell 7 とも呼ばれています. ↩︎

  10. Language-Integrated Query (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/) ↩︎

  11. 一般に 127.0.0.0/8 はループバックアドレスとして使われます (https://datatracker.ietf.org/doc/html/rfc6890). ↩︎

  12. 余談ですが,一般に Linux と呼ぶとき暗示的に GNU/Linux を指すことが多いです.これは歴史上の理由ですが,それはさておき Linux コマンドというものは存在しません.あなたが触れているのは Linux ではなくて GNU なのです. ↩︎

  13. コマンドの製作者は容易に出力を変更します.また環境によって差異が出る場合もあります.私たちはシェルスクリプト上で無意識にコマンドの出力に依存していますが,この依存は危険といえます. ↩︎

chot Inc. tech blog

Discussion