ちょっとしたこだわりの改良

ここ数回にわたり、システムクリップボードのテキストを本文に使って、新規メール作成のアプリケーションを起動するPowerShellスクリプト「clip2mail.ps1」の作成を進めている。前回は、さまざまなテキストをシステムクリップボードに貼り付け、“うまく動作しないケース”を探しては解消してスクリプトの精度を高めていった。その成果は、次の通りだ。

#!/usr/bin/env pwsh

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

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

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

#========================================================================
# 宛先とサブジェクト
#========================================================================
$to=$env:DEFAULT_EMAIL_TO
$subject="Windowsシステムクリップボード"

#========================================================================
# システムクリップボードのテキストを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;"

#========================================================================
# 引数で指定できる文字列長には上限がある。上限を超えている場合には、条件
# 未満まで文字数を減らして使用する。
#========================================================================
if ($body.Length -gt $bodycharlimit) {
    $body = $body.Substring(0, $bodycharlimit)
    $body += "<br>"
    $body += "<br>"
    $body += "(サイズオーバーでカットしました)"
}

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

このスクリプトでは、貼り付けるテキストの文字数に上限を設けている。コマンドライン引数経由で本文を渡すという構造上、渡すことができる文字数に上限があるためだ。また、文字数が多すぎるとThunderbirdもクラッシュするため、ある程度の量に抑える必要があることがわかっている。

前回までのclip2mail.ps1では、文字数上限でバッサリと切り捨てるようにした。だが、このやり方だと本文の末尾に「本来は存在しない文字列」が表示されることがある。今回はこの部分を改良する方法を取り上げる。

問題が出るケース

次のテキストをシステムクリップボードにコピーしてから、clip2mail.ps1を実行してみる。

  • コピー対象のテキストファイル

    コピー対象のテキストファイル

この状態で起動するThunderbirdメールコンポーザでは、貼り付けられている本文の最後の部分は次のようになっている。

  • 起動してくるThunderbirdメールコンポーザ

    起動してくるThunderbirdメールコンポーザ

本文の最後に「&」と表示されているが、これは元のテキストデータには存在しないものだ。なぜこのようなテキストが勝手に追加されているのか。これには、clip2mail.ps1の次の処理が関係している。

まず、clip2mail.ps1では次の処理でテキストの置換を行っている。

$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;"

このようなエスケープ処理を行わないと、文字が思ったようにThunderbirdのコンポーザに渡ってくれないからだ。エスケープ後、次の処理でテキストデータをサイズで切り捨てている。

    $body = $body.Substring(0, $bodycharlimit)

この切り捨てで区切られた部分が、先の置換処理で置き換えられた文字列の途中だった場合、意味不明な文字列が末尾に付加されたような状態になる。

例えば、置換後テキストの末尾が「&gt;」であり、切り捨てによってこの部分が「&g」となったとする。本来であれば「>」と表示されてほしいのだが、その部分が「&g」と表示されてしまうわけだ。これが先ほどの「意味不明な文字列が付加された」状況を生み出している。