Powershell: ファイルへの書き込みと文字コード

Powershell は、Windows が動作している環境からシステム情報を取ってくる場合に便利ということで使い始めましたが、ログや定型文の処理をさせるにも便利です。

これまでに、TeraTerm マクロで SSHでサーバーに自動ログイン自動で画面を記録する、自動で決められたコマンドを発行することが出来ることをメモに残しました。それを合わせると次のようになります。

hostname = '192.168.1.3'
username = 'nike' 
Password = 'AaAa1234'
;以下 SSHで接続するためのコマンド
msg = hostname
strconcat msg ':22 /ssh /auth=challenge /user='
strconcat msg username
strconcat msg ' /passwd='
strconcat msg Password
connect msg
;以下、ロギングのためのコマンド
logdir = 'C:\temp\tterm_log\' 
logfile = logdir
strconcat logfile hostname
strconcat logfile '.log'
logopen logfile 0 1

;以下、自動コマンド発行のためのコマンド
wait "~>"
sendln "ps auxww|grep httpd"
wait ">"
sendln "exit"
; マクロ終了

一つのホストにだけログインするマクロで良ければこれで終わり。

SSHログイン可能で、初期設定が必要な大量の同型LANスイッチを購入した場合など、繰り返し繰り返しスイッチにログインして初期設定や設定確認を行わなければならないケースがあります。
または、大量のほぼ同一設定のVMのステータスチェックを行うために TeraTerm マクロを使いたいような場合です。

例えば、上述のTeraTerm マクロのホスト名だけを替えて、ホストの台数分のログインマクロを作りたいような場合です。5台、10台程度であれば、マクロファイルをコピーして、内容を手作業で編集してもいいのですが、50台、100台ともなるとスクリプトで作成してしまいたいものです。

実際、今回そういうケースが発生し、PowerShellを使って、約100個分のスイッチへログインするTeraTerm マクロを作ろうとして、はまりました。

hostname = '192.168.1.3'
username = 'nike' 
Password = 'AaAa1234'

上述マクロのホスト名だけを、host_a, host_b, host_c …… と台数分だけ替えた .TTL ファイルを台数文だけ作りたい場合、「hostname = ‘%HOSTNAME%’」のような記述をしたテンプレートファイルを作り、ホストの数だけループして .ttl  ファイルを生成するために次の PowerShell スクリプトを書きました。

$tt_temp_file="C:\temp\ttmacro\template1.ttl"
$tt_temp_dir="C:\temp\ttmacro"
$hostname_file="C:\temp\ttmacro\hostname.txt"
$tt_temp=(Get-Content $tt_temp_file)
foreach ($hostname in (Get-Content $hostname_file)){
    $tt_temp -replace "%HOSTNAME%", $hostname > $tt_temp_dir/$hostname.ttl
}

$tt_temp という変数にマクロテンプレートを読み込み、foreach ループの中で、ホスト名に相当するパラメータを実際のホスト名に置き換えるという具合です。

テスト用に hostname.txt と tt_template1.ttl を次のように準備します。

hostname.txt

host_a
host_b
host_c

tt_template1.ttl

hostname = '%HOSTNAME%'
username = 'nike' 
Password = 'AaAa1234'
;以下 SSHで接続するためのコマンド

msg = hostname
strconcat msg ':22 /ssh /auth=challenge /user='
strconcat msg username
strconcat msg ' /passwd='
strconcat msg Password
connect msg

;以下、ロギングのためのコマンド
logdir = 'C:\temp\tterm_log\' 
logfile = logdir
strconcat logfile hostname
strconcat logfile '.log'
logopen logfile 0 1

;以下、自動コマンド発行のためのコマンド
wait "~>"
sendln "ps auxww|grep httpd"
wait ">"
sendln "exit"
; マクロ終了

そして、Powershell を実行。
%HOSTNAME% の部分が実際のホスト名に置き換えられた .ttl ファイルが出来上がったものの、出来上がったマクロを実行しようとすると、次のように Macro: Error, Syntax error が発生し、マクロ停止するしかなくなってしまいます。TeraTerm マクロ syntax error

これで数時間はまりました。

内容は同じなのに、テンプレートファイルのファイルサイズが約500バイト、生成されたTTLファイルのサイズはなぜか1kB近くに膨れあがっています。そこで、文字コードが原因だと気づきました。
Powershell でこのようなファイル出力の書き方をすると、UTF8 で出力されるとのことで、TTPmacro.exe はUTF8で書かれたコマンドを理解しないことが原因だと気づきました。そこで、

$tt_temp -replace "%HOSTNAME%", $hostname > $tt_temp_dir/$hostname.ttl

$tt_temp -replace "%HOSTNAME%", $hostname | Set-Content -Encoding Ascii $tt_temp_dir/$hostname.ttl

に修正。
コメント行が文字化けしますが、マクロはエラーなく動くようになりました。Ascii だと 7bitコードなので、string の方がいいかも。最終的に、ホスト名だけ変換する必要があるテンプレートの場合、次の Powershell スクリプトでうまくゆきました。

$tt_temp_file="C:\temp\ttmacro\template1.ttl"
$tt_temp_dir="C:\temp\ttmacro"
$hostname_file="C:\temp\ttmacro\hostname.txt"
$tt_temp=(Get-Content $tt_temp_file)
foreach ($hostname in (Get-Content $hostname_file)){
    $tt_temp -replace "%HOSTNAME%", $hostname | Set-Content -Encoding String $tt_temp_dir/$hostname.ttl
}

もっと発展させるなら、hostname, username, password を変数としたテンプレートファイルを作り、3つのパラメータを .csv ファイルに書かれた情報で置き換えるように改造すべきでしょう。

今回学んだことは、Powershell でファイルに出力する時は、文字コードに気をつけようということでした。

コメントを残す