はじめに

以前 Windows Server 主体の環境で Active Directory 管理やアカウント処理を担当していたことがあり、それらを自動化する手段として PowerShell を毎日のように触っていました。頻度は落ちましたが、現在でも以下のような感じで触っています。

  • AWS CLI より AWS Tools for PowerShell の方が得意
  • mac にも pwsh 入れてる

本記事は PowerShell を触ってきて蓄積したノウハウを小ネタとして放出したいというのがモチベーションで書いています。ひとつひとつの粒度が小さいので、複数回に分けて紹介します。なお、基本的にクロスプラットフォーム対応後の PowerShell 6 以降を前提とします。ところどころで bash と比較します。

1. OS によって違うコマンドを実行する
2. アプリケーションのパスを取得する
3. 管理者権限があるか確認する (Windows 限定)
4. 例外の出力をすっきりさせる
5. コマンドラインで天気予報を見る (小ネタ)

1. OS によって違うコマンドを実行する

PowerShell 6 以降は以下の自動変数で OS が判定できます。

  • $IsWindows
  • $IsMacOs
  • $IsLinux

これを利用して以下のように OS ごとに挙動が変わる関数を簡単に書くことができます。

#Requires -Version 6.0

function oscmd {
  param (
    [Alias('w')]
    [scriptblock]$windows,
    [Alias('m')]
    [scriptblock]$macos,
    [Alias('l')]
    [scriptblock]$linux
  )

  if ($IsWindows -and $null -ne $windows) {
    Invoke-Command -ScriptBlock $windows
  }
  if ($IsMacOS -and $null -ne $macos) {
    Invoke-Command -ScriptBlock $macos
  }
  if ($IsLinux -and $null -ne $linux) {
    Invoke-Command -ScriptBlock $linux
  }
}

使い方

oscmd -windows { explorer "https://google.co.jp" } -macos { open "https://google.co.jp" } -linux { curl "https://google.co.jp" }

クラスプラットフォームのシェルという特性に着目したアプローチができて便利ですね。

2. アプリケーションのパスを取得する

使いたいツールがインストールされているか確認したい場合、bash では以下のようにすると思います。

command -v jq

# or
which jq

これを PowerShell でやりたい場合、以下のような関数で似た挙動を実現できます。bash と違って $? には $true が格納されますが、戻り値が $null かどうかで判定できます。

function apppath {
  param (
    [Alias('n')]
    [string]$name
  )

  if (!$name) {
    throw 'Name required'
  }

  $app = Get-Command -Name $name -CommandType Application -ErrorAction Ignore

  if ($app) {
    return $app.Source
  }

  return
}

ツールがない場合に早期リターンしたい場合は以下のようにします (bash と比較)

bash の場合

if ! command -v jq &>/dev/null; then
  echo "ERROR: 'jq' not found"
  return 1
fi

PowerShell の場合

if (!(apppath -name jq)) {
  Write-Output "ERROR: 'jq' not found"
  return
}

3. 管理者権限があるか確認する (Windows 限定)

管理者権限の有無をコマンドラインで確認して処理を分岐する必要があるとします。Windows 端末の初期化などで重宝しそうですね。以下のように .NET のクラスを使えば実現できます。

#Requires -Version 6.0

function isadmin {
  if (!$IsWindows) {
    throw 'This function is only valid if Windows'
  }
  $principal = [Security.Principal.WindowsPrincipal]::new([Security.Principal.WindowsIdentity]::GetCurrent())
  return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}

4. 例外の出力をすっきりさせる

通常 PowerShell で例外を throw すると、ユーザーに見せたくない内部的なコードが露出します。試しに .ps1 ファイルに以下を書いて実行してみましょう。

throw "this is exception"

こんな感じで表示されます。ユーザーは関数の内部を知る必要がなく、代わりにメッセージだけ表示されたほうがいい場面も多そうです。

Exception: /Users/user1/bin/test.ps1:1:1
Line |
   1 |  throw "this is exception"
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~
     | this is exception

そういう場合は以下のように書くことで要件を満たせます。

[CmdletBinding()]
param()

try {
  throw "this is exception"
}
catch {
  $PSCmdlet.ThrowTerminatingError($PSItem)
}

まず CmdletBinding を使って関数をコマンドレットのように機能させることを指示します (高度な関数と呼ぶらしいです)
これによって今回使いたい自動変数 $PSCmdlet$PSItem も使えるようになります。

次に全体を try/catch で囲み、catch の中で $PSCmdlet.ThrowTerminationgError($PSItem) と書いて捕捉したエラーを再スローします。$PSItem の中身は ErrorRecord なので、これを再スローすると ErrorRecord のみが表示されます(元ネタ)

Exception: this is exception

5. コマンドラインで天気予報を見る (小ネタ)

小ネタ中の小ネタです。アスキーアートで天気予報を確認できる wttr.in というサイトがあります。けっこう昔からあるみたいですね。これを利用してコマンドラインで直近 3 日間の天気予報を確認できます。引数で地名を渡しましょう。

bash

tenki() {
  curl "https://wttr.in/$1?lang=ja"
}

PowerShell

function tenki {
  param (
    [string]$place
  )
  Invoke-RestMethod -Uri "https://wttr.in/${place}?lang=ja" -Method Get
}

とある日の天気

$ tenki -place 東京
天気予報: 東京

     \  /       所により曇り
   _ /"".-.     +28(30) °C     
     \_(   ).   ↖ 13 km/h      
     /(___(__)  10 km          
                0.0 mm         
                                                       ┌─────────────┐                                                       
┌──────────────────────────────┬───────────────────────┤  木 14 9月  ├───────────────────────┬──────────────────────────────┐
│              朝              │              昼       └──────┬──────┘        夕             │              夜              │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│    \  /       所により曇り   │     \   /     晴れ           │     \   /     快晴           │     \   /     快晴           │
│  _ /"".-.     +28(32) °C     │      .-.      +31(34) °C     │      .-.      +29(32) °C     │      .-.      +29(32) °C     │
│    \_(   ).   ↖ 13-14 km/h   │   ― (   ) ―   ↑ 21-24 km/h   │   ― (   ) ―   ↑ 24-31 km/h   │   ― (   ) ―   ↑ 17-23 km/h   │
│    /(___(__)  10 km          │      `-’      10 km          │      `-’      10 km          │      `-’      10 km          │
│               0.0 mm | 0%    │     /   \     0.0 mm | 0%    │     /   \     0.0 mm | 0%    │     /   \     0.0 mm | 0%    │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
                                                       ┌─────────────┐                                                       
┌──────────────────────────────┬───────────────────────┤  金 15 9月  ├───────────────────────┬──────────────────────────────┐
│              朝              │              昼       └──────┬──────┘        夕             │              夜              │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│     \   /     晴れ           │               曇り           │               曇り           │               曇り           │
│      .-.      +30(33) °C     │      .--.     +32(36) °C     │      .--.     +29(33) °C     │      .--.     +27(30) °C     │
│   ― (   ) ―   ↓ 12-14 km/h   │   .-(    ).   ← 10-13 km/h   │   .-(    ).   ← 17-23 km/h   │   .-(    ).   ↙ 17-22 km/h   │
│      `-’      10 km          │  (___.__)__)  10 km          │  (___.__)__)  10 km          │  (___.__)__)  10 km          │
│     /   \     0.0 mm | 0%    │               0.0 mm | 0%    │               0.0 mm | 0%    │               0.0 mm | 0%    │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
                                                       ┌─────────────┐                                                       
┌──────────────────────────────┬───────────────────────┤  土 16 9月  ├───────────────────────┬──────────────────────────────┐
│              朝              │              昼       └──────┬──────┘        夕             │              夜              │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│               曇り           │               曇り           │    \  /       所により曇り   │    \  /       所により曇り   │
│      .--.     +28(31) °C     │      .--.     +30(34) °C     │  _ /"".-.     +30(33) °C     │  _ /"".-.     +29(32) °C     │
│   .-(    ).   ↙ 6-8 km/h     │   .-(    ).   ↑ 2 km/h       │    \_(   ).   ↑ 16-21 km/h   │    \_(   ).   ↑ 15-20 km/h   │
│  (___.__)__)  10 km          │  (___.__)__)  10 km          │    /(___(__)  10 km          │    /(___(__)  10 km          │
│               0.0 mm | 0%    │               0.0 mm | 0%    │               0.0 mm | 0%    │               0.0 mm | 0%    │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
位置情報: 東京, 千代田区, 東京都, 日本

Follow @igor_chubin for wttr.in updates

PowerShell の話ではないですね。。

おわりに

PowerShell はと呼ばれるものがたくさんあり、癖が強く扱いづらい面があります。が、使いこなせば強力なツールになり得ます。