• PSターミナル・エクスペリメント

PowerShell、Windows PowerShell(以後PowerShellと表記)に搭載されているPSReadLineは、キーボードショートカットに、PSReadLineの機能(メソッドや関数)を割り当てることができる。このとき、スクリプトブロックで割り当てを行うことができるので、いわゆるマクロを作ることができる。

このとき利用できる機能に関しては、「about_PSReadLine_Functions(PowerShell 7.5)」または「about_PSReadLine_Functions - PowerShell(Windows PowerShell 5.1)」を参照されたい。

キーボードショートカットの追加には、Set-PSReadLineKeyHandlerで行う。コマンド構文は、


Set-PSReadLineKeyHandler
   [-ScriptBlock] <ScriptBlock>
   [-BriefDescription <String>]
   [-Description <String>]
   [-Chord] <String[]>
   [-ViMode <ViMode>]
   [<CommonParameters>]

となっている。ここで、「-BriefDescription 」は、Get-PSReadlineKeyHandlerで「Function」プロパティとして表示される文字列。「[-Description ]」は、「Description」として表示される文字列だ。

具体的にマクロを定義してみよう。PowerShellでは、コマンドを実行したあと、特定のプロパティだけを取り出したい場合がある。特にワンライナーの一部で使う場合など、コマンド全体をカッコでくくって、ピリオドでプロパティ名を指定する記述を使う。つまり「command」を「(command).Property」という形式にすることが少なくない。このとき、行の先頭に開き括弧を入れる必要があり、少し面倒くさい。これをマクロで自動化してみよう。

仕様としては、コマンドラインの現在のカーソル位置に「).」を入れ、行の先頭に「(」を入れる。行末ではなくカーソル位置としたのは、後続のコマンドラインがあることを考慮したからだ。選択範囲があるなら、その先頭に「(」を入れ、その末尾に「).」を入れる。キー定義は、リスト01のようになる。

■リスト01


Set-PSReadLineKeyHandler -Chord Ctrl+Shift+b -BriefDescription "Macro" -Description "括弧で囲んでピリオドを付けるV4" -ScriptBlock {
    $start = $null
    $length=$null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$start,[ref]$length)
    if( $start -eq -1) {
        $line = $null
        $cursor = $null
        [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
        [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition(0)
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert('(')
        [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor+1)
    } else {
        [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($start)
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert('(')
        [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($start+$length+1)
    }
    [Microsoft.PowerShell.PSConsoleReadLine]::Insert(').')
}

ここでは、キーボードショートカットとしてWindowsモード、Emacsモードのどちらでも衝突しない「Ctrl+Shift+b」を選んだ。ただし、このキーは、Get-PSReadLineKeyHandlerでは、「Ctrl+B」と表示されることに注意されたい。Get-PSReadLineKeyHandlerでは「Shift+b」を「B」と表し、Ctrl+bとCtrl+Bが区別されている。

スクリプトブロックを簡単に解説しよう。先頭にあるのは、3行目のGetSelectionStateで使うための変数。引数に指定した変数に値が入って戻ってくる。この関数で最初の引数($start)は、選択範囲の開始位置を示すが、選択範囲がない場合には、-1が入る。5行目のif文はこれを判定している。

選択範囲がない場合、先頭に括弧を入れる。そのための処理が次の行から。GetBufferStateで現在のカーソル位置を取得している。

次の「SetCursorPosition(0)」で行(複数行可)の先頭にカーソルを移動させる。「Insert('(')」で先頭に開き括弧を挿入し、「SetCursorPosition($cursor+1)」で元の位置(先頭に括弧を挿入したので+1で1文字右に移動している)に戻る。

「else」の後は選択範囲がある場合の処理だ。ここでは選択範囲の先頭に「Insert('(')」で開き括弧を挿入し、SetCursorPosition($start+$length+1)で選択範囲の直後にカーソルを戻している。

どちらの場合も最後に「Insert(').')」で閉じ括弧とピリオドを付けている。

今回のタイトルネタは、ロバート・J・ソーヤー(Robert J. Sawyer)の「ターミナル・エクスペリメント」(ハヤカワ文庫SF。原題The Terminal Experiment,1995年)である。この作品、雑誌に掲載されていたときには、「Hobson's Choice」という題名だった。Hobson's Choiceとは、選択肢といいながら、受け入れるか、受け入れないかを選ばねばならず、受け入れないことは望ましくないため、実際には1つしか選択できない「選択肢」のことだ。Windowsにはcmd.exeや様々な言語のREPL、さらにはWSLのbashなど「シェル」や「シェル」らしいプログラムが多数ある。しかし、実際にシステム管理などのシェルとして使えるのはPowerShellだけしかない。