パラメータで値を指定する

本連載ではここ数回にわたり、Thunderbirdのメールコンポーザを起動するPowerShellスクリプトを作成中だ。本文をシステムクリップボードから持ってくると共に、前回は宛先(To)、送信元(From)、件名(Subject)をパラメータから指定できるようにした。作成した成果物「clip2mail.ps1」は次の通りだ。

#!/usr/bin/env pwsh

#========================================================================
# システムクリップボードのテキストでメールを作成
#========================================================================

#========================================================================
# 引数を処理
#   -To foo@sample.com      宛先メールアドレス
#   -From bar@sample.com    送信元メールアドレス
#   -Subject 'Mail Title'   メールサブジェクト
#========================================================================
Param(
    [String]$To = $Env:DEFAULT_EMAIL_TO,
    [String]$From = $Env:DEFAULT_EMAIL_FROM,
    [String]$Subject = "Windowsシステムクリップボード"
)

#========================================================================
# Thunderbirdアプリケーションパス
#========================================================================
$mailer='C:\Program Files\Mozilla Thunderbird\thunderbird.exe'

#========================================================================
# 本文の上限文字数
#========================================================================
# これ以上の値を指定するとThuderbirdがクラッシュすることがある
$bodycharlimit = 24000

#========================================================================
# システムクリップボードのテキストをThunderbirdのコンポーザに貼り付け
# できるフォーマットへ変換
#========================================================================
$body=$(Get-Clipboard | Out-String)

$body=$body -replace " "," "
$body=$body -replace "<","&lt;"
$body=$body -replace ">","&gt;"
$body=$body -replace "  ","<pre style='display:inline'>&#009;</pre>"
$body=$body -replace ",","&#044;"

#========================================================================
# 引数で指定できる文字列長には上限がある。上限を超えている場合には、上限
# 未満まで文字数を減らして使用する。
#========================================================================
function Get-TrimmedBody {
    # 上限文字数で文字列を切り捨てる。
    $trimmed = $body.Substring(0, $bodycharlimit)

    # 文字列の末尾がこの配列の文字列の途中で切れていたと判断できる
    # 場合に、配列の文字列の分も削除する。これら文字列はすべて記載
    # されている必要があり、途中で切れている場合には削除する必要が
    # ある。
    $a = $("&nbsp;",
           "&lt;",
           "&gt;",
           "<pre style='display:inline'>&#009;</pre>",
           "&#044;")
    :EndTreatment foreach ($v in $a) {
        $len = $v.Length - 1
        for ($i=0; $i -lt $len; $i++) {
            $v = $v.Substring(0, $len - $i)
            if ($trimmed.EndsWith($v)) {
                $trimmed = $trimmed.Substring(0, $trimmed.Length - $v.Length)
                break EndTreatment
            }
        }
    }

    # 終端処理を行った文字列を返す
    $trimmed
}

if ($body.Length -gt $bodycharlimit) {
    $body = Get-TrimmedBody
    $body += "<br>"
    $body += "<br>"
    $body += "(サイズオーバーでカットしました)"
}

#========================================================================
# メールを作成
#========================================================================
& $mailer                               `
    -compose                            `
    "to='$To',from='$From',subject='$Subject',body='$body'"

初めは、「To」や「Subject」はシェルスクリプトに固定にしていた。次の段階として環境変数経由で指定できるようにし、前回はこれをパラメータで指定できるように書き換えた。パラメータで指定できるようにしたことで、このスクリプトは汎用度が上がったことになる。

次に実現したくなるのは、メールアドレスを複数指定できるようにすることだ。メールの宛先は、1カ所とは限らない。また、Toは1つでも、CcやBccに複数のメールアドレスを指定したいケースがある。複数アドレスの指定ができないと、汎用スクリプトとしては使い物にならないのだ。

メールアドレスを複数指定できるようにする

前回作成したスクリプトには、CcとBccを指定するパラメータを追加していない。そこで、まずはこれらを指定できるように「Param」の部分の指定を次のように書き換える。

#========================================================================
# 引数を処理
#   -To a@example.com,b@example.com 宛先メールアドレス
#   -Cc a@example.com,b@example.com Ccメールアドレス
#   -Bcc a@example.com,b@example.com    Bcccメールアドレス
#   -From bar@example.com       送信元メールアドレス
#   -Subject 'Mail Title'       メールサブジェクト
#========================================================================
Param(
    [String]$To = $Env:DEFAULT_EMAIL_TO,
    [String]$Cc = "",
    [String]$Bcc = "",
    [String]$From = $Env:DEFAULT_EMAIL_FROM,
    [String]$Subject = "Windowsシステムクリップボード"
)

続いて、コメントにあるように、メールアドレスとして複数のメールアドレスをカンマ区切りで指定できるようにする。

PowerShellスクリプトでは、Paramで指定するパラメータを複数回使うことができない。つまり「clip2mail.ps1 -To a@example.com -To b@example.com」のようには指定できないのだ。

となると、1回の指定で複数のメールアドレスを指定できるようにする必要がある。この場合、最も自然なのはカンマで区切って複数のメールアドレスを指定できるようにすることだろう。「-To a@example.com,b@example.com」のような感じだ。こうした指定ができるようにスクリプトを書き換えていく。

カンマ区切りの値の扱いに注意

ただし、ここで注意が必要だ。PowerShellではカンマで区切った指定は、クォートしている場合としていない場合とで挙動が異なる。どのように異なるか、次のテスト用スクリプト「CommaTest.ps1」を使って調べてみる。

Param (
    [String]$To = ""
)

Write-Host $To

実行すると以下のようになる。

% .\CommaTest.ps1 -To a,b
a b
% .\CommaTest.ps1 -To 'a,b'
a,b
% 

動作が異なっていることがおわかりいただけるだろう。

パラメータの値をカンマ区切りで指定した場合、文字列としては空白区切りと見なされてスクリプトで処理が行われている。一方、クォートを使うと、カンマを含んだ1つの文字列として処理が行われている。つまり、ユーザーがどういった指定を行うかで挙動が変わってしまうということだ。これだと扱いにくい。

Thunderbirdのメールコンポーザは、「to='a@example.com,b@example.com'」のようにカンマ区切りで値を指定することで複数のメールアドレスを渡すことができるので、先ほどの指定をカンマ区切りに揃えると、そのまま利用できて便利ということになる。

ここでは次の処理を追加して、-To、-Cc、-Bccの指定をカンマ区切り指定へ整理するようにする。

#========================================================================
# To、Cc、BccをThunberbirdで指定できる形へ展開
#========================================================================
$to=$to.Replace(' ',',')
$cc=$cc.Replace(' ',',')
$bcc=$bcc.Replace(' ',',')

これで主な事前準備は完了だ。

Thunderbirdメールコンポーザ起動部分をCcとBccにも対応させる

前回のPowerShellスクリプトではCcとBccは指定していなかったので、Thunderbirdメールコンポーザを起動している部分の処理を次のように書き換える。

#========================================================================
# メールを作成
#========================================================================
& $mailer                               `
    -compose                            `
    "to='$To',cc='$Cc',bcc='$Bcc',from='$From',subject='$Subject',body='$body'"

これで今回の改善は完了だ。

実行して動作を確認

今回の取り組みをシェルスクリプトに統合した「clip2mail.ps1」は次のようになる。

#!/usr/bin/env pwsh

#========================================================================
# システムクリップボードのテキストでメールを作成
#========================================================================

#========================================================================
# 引数を処理
#   -To a@example.com,b@example.com 宛先メールアドレス
#   -Cc a@example.com,b@example.com Ccメールアドレス
#   -Bcc a@example.com,b@example.com    Bcccメールアドレス
#   -From bar@example.com       送信元メールアドレス
#   -Subject 'Mail Title'       メールサブジェクト
#========================================================================
Param(
    [String]$To = $Env:DEFAULT_EMAIL_TO,
    [String]$Cc = "",
    [String]$Bcc = "",
    [String]$From = $Env:DEFAULT_EMAIL_FROM,
    [String]$Subject = "Windowsシステムクリップボード"
)

#========================================================================
# Thunderbirdアプリケーションパス
#========================================================================
$mailer='C:\Program Files\Mozilla Thunderbird\thunderbird.exe'

#========================================================================
# 本文の上限文字数
#========================================================================
# これ以上の値を指定するとThuderbirdがクラッシュすることがある
$bodycharlimit = 24000

#========================================================================
# システムクリップボードのテキストをThunderbirdのコンポーザに貼り付け
# できるフォーマットへ変換
#========================================================================
$body=$(Get-Clipboard | Out-String)

$body=$body -replace " ","&nbsp;"
$body=$body -replace "<","&lt;"
$body=$body -replace ">","&gt;"
$body=$body -replace "  ","<pre style='display:inline'>&#009;</pre>"
$body=$body -replace ",","&#044;"

#========================================================================
# 引数で指定できる文字列長には上限がある。上限を超えている場合には、上限
# 未満まで文字数を減らして使用する。
#========================================================================
function Get-TrimmedBody {
    # 上限文字数で文字列を切り捨てる。
    $trimmed = $body.Substring(0, $bodycharlimit)

    # 文字列の末尾がこの配列の文字列の途中で切れていたと判断できる
    # 場合に、配列の文字列の分も削除する。これら文字列はすべて記載
    # されている必要があり、途中で切れている場合には削除する必要が
    # ある。
    $a = $("&nbsp;",
           "&lt;",
           "&gt;",
           "<pre style='display:inline'>&#009;</pre>",
           "&#044;")
    :EndTreatment foreach ($v in $a) {
        $len = $v.Length - 1
        for ($i=0; $i -lt $len; $i++) {
            $v = $v.Substring(0, $len - $i)
            if ($trimmed.EndsWith($v)) {
                $trimmed = $trimmed.Substring(0, $trimmed.Length - $v.Length)
                break EndTreatment
            }
        }
    }

    # 終端処理を行った文字列を返す
    $trimmed
}

if ($body.Length -gt $bodycharlimit) {
    $body = Get-TrimmedBody
    $body += "<br>"
    $body += "<br>"
    $body += "(サイズオーバーでカットしました)"
}

#========================================================================
# To、Cc、BccをThunberbirdで指定できる形へ展開
#========================================================================
$to=$to.Replace(' ',',')
$cc=$cc.Replace(' ',',')
$bcc=$bcc.Replace(' ',',')

#========================================================================
# メールを作成
#========================================================================
& $mailer                               `
    -compose                            `
    "to='$To',cc='$Cc',bcc='$Bcc',from='$From',subject='$Subject',body='$body'"

次のようにパラメータで複数のメールアドレスを指定してPowerShellスクリプトを実行する。

clip2mail.ps1 -To a@example.com,b@example.com -Subject 'サンプルメール' -Cc c@example.com,d@example.com -Bcc e@example.com,f@example.com

実行すると次のようなThunderbirdメールコンポーザが起動してくる。複数のメールアドレスが指定できることがわかるんじゃないかと思う。

  • 実行結果

    実行結果

複数のメールアドレスが指定できるようになるとかなり汎用度が増す。新規メール作成用のPowerShellスクリプトとして結構便利な仕上がりになってきた。

ちょっとずつ改善していくことがポイント

こうした日常で使うタイプのPowerShellスクリプトは、ともかくも必要に応じて徐々に書き換えるというところがポイントだ。一気に作らずに、必要な部分を作っては使い、機能が欲しくなったり整理したくなったら整理する。スクリプトの最大の利点はその手軽さにあるのであって、作り出した最初のうちはこのようにサクサクと作り変えていくことが大切だ。必要に応じて書き換えを繰り返していくことで、実際に役立つスクリプトへ成長させていく。

プログラミング言語の学習は、直接的にプログラミングのスキルに結び付かないことも多い。だが、実際に自分が必要とするものを作ることで、リアルに役立つスキルを身に付けていこう。