ネイティブコマンドをPowerShell化するというアプローチ

【連載】

PowerShell Core入門 - 基本コマンドの使い方

【第129回】ネイティブコマンドをPowerShell化するというアプローチ

[2020/12/25 08:00]後藤大地 ブックマーク ブックマーク

先日、「PowerShell Crescendo Preview.1」が公開された。PowerShell Crescendo Preview.1がどのようなもので、どういったことを目的としたものであるかはMicrosoftのアナウンスにわかりやすくまとまっている。

Announcing PowerShell Crescendo Preview.1 | PowerShell


本連載では、WSLで動作するLinuxのコマンドをPowerShellからより簡単に利用できるようにする取り組みを続けてきた。PowerShellからLinuxのコマンドを使用できると何かと便利だからだ。

「PowerShell Crescendo」はネイティブコマンドをコマンドレットに置き換える機能を提供する。これまで本連載で取り組んできた設定とは、逆の取り組みのような感じだ。PowerShellとしては、ネイティブコマンドをコマンドレットにラッピングするほうが自然であり、PowerShell Crescendoはそれを実現するものとされている。

ネイティブコマンドをコマンドレットへ

PowerShellは「管理用のインタラクティブシェル」という側面を備えており、UNIXのインタラクティブシェルやコマンドプロンプトのようなUI/UXを備えている。しかし、実際にはC#などで実現されているオブジェクト指向やその機能に強く影響を受けたプログラミング言語であるとともに、命名規則やオプションの指定方法などにきっちりとした決まりがある。

UNIXシェルの命名規則などは慣例的なもので強制力はない。そこで動作するコマンドも個別に開発されたもので、コマンド名やオプションの指定、サブコマンドの指定やその命名規則などには一貫性がない。ある程度似通ったものではあるのだが、PowerShellのようにきっちりとは決まっていないのだ。

PowerShellでは、当然ネイティブコマンドも実行できるようになっている。しかし、ネイティブコマンドとPowerShellコマンドレットにはシンタックスに違いがあり、PowerShellに慣れたユーザーからしてみればネイティブコマンドをコマンドレットのように扱えたほうが便利だ。ネイティブコマンドごとに操作方法などを覚えるのは、PowerShellをメインに使っているユーザーにとっては負担となる。

先ほどの記事ではコマンドレットの特徴として次の項目を挙げている。

  • コマンドレット名の命名規則: 動詞-名詞
  • 統一されたパラメータ名
  • 入出力がオブジェクト
  • パイプライン内のオブジェクトを簡単に操作
  • コマンドヘルプを取得する一貫した方法


要するに、ネイティブコマンドをコマンドレットでラッピングして上記の特徴を備えてしまえば、PowerShellの流儀で同じように扱えるようになる、というのがPowerShell Crescendoの発想だ。

ネイティブコマンドには上記のような決まりはないし、規則もない。Linuxの管理者であればapt、docker、kubectlといった管理コマンドをよく使うと思うが、これらのコマンドはサブコマンドを指定して利用する。サブコマンド名の規則性はないし、コマンドごとに異なっている。Windowsであればnetsh.extコマンドもよく使うと思うが、これらも独自の指定方法だ。こういったネイティブコマンドの細かい違いを、コマンドレットでラッピングして使ってやろうというわけである。

PowerShell Crescendoのインストール方法

PowerShell CrescendoはPowerShell Galleryで提供されている。本稿執筆時点での最新版は「PowerShell.Crescendo 0.4.1」だ。


マニュアルインストールしてもよいが、以下のようにInstall-Moduleコマンドレットでインストールして使うのがよいだろう。

Install-Module -Name Microsoft.PowerShell.Crescendo

マニュアルは次のようにコマンドレットを実行することで表示させることができる。

Get-Help about_Crescendo

PowerShell Crescendoの使用サンプル

PowerShell Crescendoの使い方は、ネイティブコマンドをどのようにコマンドレットに変換するかを定義してJSONファイルを作成する、という流れになる。JSONファイルの書き方は冒頭で紹介したアナウンスに掲載されているサンプルがわかりやすい。例えば、次のJSONファイルはLinuxのパッケージ管理コマンドであるaptコマンドを「Get-InstalledPackage」というコマンドレットに変換(ラッピング)する設定だ。

{
    "$schema": "./Microsoft.PowerShell.Crescendo.Schema.json", // <-- Schema definition file
    "Verb": "Get", // <-- Cmdlet Verb
    "Noun":"InstalledPackage", // <-- Cmdlet Noun
    "OriginalName": "apt", // <-- Native command
    "OriginalCommandElements": ["-q","list","--installed"],
    "OutputHandlers": [ // <-- Add string output to an object
        {
            "ParameterSetName":"Default",
            "Handler": "$args[0]| select-object -skip 1 | %{$n,$v,$p,$s = "$_" -split ' '; [pscustomobject]@{ Name = $n -replace '/now'; Version = $v; Architecture = $p; State = $s.Trim('[]') -split ',' } }"
        }
    ]
}

次がラッピングしたGet-InstalledPackageコマンドレットの実行サンプルだ。aptコマンドが何となくコマンドレットっぽく使えていることがわかる。

PS> Get-InstalledPackage | Where-Object { $_.name -match "libc"}

Name        Version            Architecture State
----        -------            ------------ -----
libc-bin    2.31-0ubuntu9.1    amd64        {installed, local}
libc6       2.31-0ubuntu9.1    amd64        {installed, local}
libcap-ng0  0.7.9-2.1build1    amd64        {installed, local}
libcom-err2 1.45.5-2ubuntu1    amd64        {installed, local}
libcrypt1   1:4.4.10-10ubuntu4 amd64        {installed, local}

PS> Get-InstalledPackage | Group-Object -Property Architecture

Count Name   Group
----- ----   -----
   10 all    {@{Name=adduser; Version=3.118ubuntu2; Architecture=all; State=System.String[}, @{Name=debconf; V...
   82 amd64  {@{Name=apt; Version=2.0.2ubuntu0.1; Architecture=amd64; State=System.String[]}, @{Name=base-files...

次のJSONにファイルはWindowsのipconfig.exeコマンドをGet-IpConfigコマンドレットへラッピングする設定が掲載されている。パラメータを定義しており、さきほどのJSONファイルのサンプルよりも複雑になっている。

{
    "$schema" : "./Microsoft.PowerShell.Crescendo.Schema.json",
    "Verb": "Get",
    "Noun": "IpConfig",
    "OriginalName":"c:/windows/system32/ipconfig.exe",
    "Description": "This will display the current IP configuration information on Windows",

    "Parameters": [ // <-- Parameter definition
        {
            "Name":"All", // <-- Name of parameter that appears in cmdlet
            "OriginalName": "/all", // <-- Name of original parameter that appears in native cmd
            "ParameterType": "switch",
            "Description": "This switch provides all ip configuration details" // <-- Help parameter
        },
        {
            "Name":"AllCompartments",
            "OriginalName": "/allcompartments",
            "ParameterType": "switch",
            "Description": "This switch provides compartment configuration details"
        }
    ],

    "OutputHandlers": [
        {
        "ParameterSetName": "Default",
        "Handler":"param ( $lines )
        $post = $false;
        foreach($line in $lines | ?{$_.trim()}) {
            $LineToCheck = $line | select-string '^[a-z]';
            if ( $LineToCheck ) {
                if ( $post ) { [pscustomobject]$ht |add-member -pass -typename $oName }
                $oName = ($LineToCheck -match 'Configuration') { 'IpConfiguration' } else {'EthernetAdapter'}
                $ht = @{};
                $post = $true
            }
            else {
                if ( $line -match '^   [a-z]' ) {
                    $prop,$value = $line -split ' :',2;
                    $pName = $prop -replace '[ .-]';
                    $ht[$pName] = $value.Trim()
                }
                else {
                    $ht[$pName] = .{$ht[$pName];$line.trim()}
                }
            }
        }
        [pscustomobject]$ht | add-member -pass -typename $oName"
        }
    ]
}

実行すると次のようになる。ipconfig.exeコマンドがあたかも最初からコマンドレットだったかのように動作していることがわかる。

PS> Import-Module .ipconfig.psm1
PS> Get-IpConfig -All | Select-Object -Property ipv4address, DefaultGateway, subnetmask

ipv4address  DefaultGateway                              subnetmask
-----------  --------------                              ----------

172.24.112.1                                             255.255.240.0
192.168.94.2 {fe80::145e:1779:5cfa:c2a5%8, 192.168.94.1} 255.255.255.0

コマンドレットにラッピングしてしまうと、まるで最初からそのようなコマンドレットが存在しているかのように見える。なかなか不思議な感覚だ。

プレビュー版が登場したばかりなので何とも言えないのだが、多くのユーザーはPowerShell Crescendoを直接使うことはないのではないかと思う。ロジック的には自然な発想だと思うので、人気が高まっていけば、PowerShell Crescendoを使って開発されたコマンドレットを知らず知らずのうちに使っている、といった状況になっていくのではないだろうか。

※ 本記事は掲載時点の情報であり、最新のものとは異なる場合がございます。予めご了承ください。

一覧はこちら

連載目次

もっと知りたい!こちらもオススメ

【連載】RPA入門 - ツールで学ぶ活用シーン

【連載】RPA入門 - ツールで学ぶ活用シーン

AIには、ルールベース、機械学習、深層学習(ディープラーニング)の3つのレベルがあり、レベルが上がるに連れてより高度な人工知能を実現しますが、AIのスピンオフという位置付けで、Digital Labor(仮想知的労働者)によるホワイトカラー業務の自動化を実現するRPAが注目されています。

関連リンク

この記事に興味を持ったら"いいね!"を Click
Facebook で IT Search+ の人気記事をお届けします
注目の特集/連載
[解説動画] Googleアナリティクス分析&活用講座 - Webサイト改善の正しい考え方
[解説動画] 個人の業務効率化術 - 短時間集中はこうして作る
ミッションステートメント
教えてカナコさん! これならわかるAI入門
AWSではじめる機械学習 ~サービスを知り、実装を学ぶ~
対話システムをつくろう! Python超入門
Kubernetes入門
SAFeでつくる「DXに強い組織」~企業の課題を解決する13のアプローチ~
PowerShell Core入門
AWSで作るマイクロサービス
マイナビニュース スペシャルセミナー 講演レポート/当日講演資料 まとめ
セキュリティアワード特設ページ

一覧はこちら

今注目のIT用語の意味を事典でチェック!

一覧はこちら

会員登録(無料)

ページの先頭に戻る