C言語原人の、興味の PowerShell コーディングですので、ちゃんと
したコーディングは余所で学んでね。

今まで公開してきたコードを、関数を使って、さらにメンテしやすく
してみました。(コードのコンパクト化)
また、いらない処理を省いたり、冗長なとこを修正したりもしてます。

Telnetセッション開始から、ネゴ終了まで関数にまとめてみましたが
PowerShell の変数って独特な有効範囲ですね。

PowerShell のターミナルウインドウを閉じるまでは、処理系も変数も
有効なんですね。すべてグローバル変数になっちゃう PHP と似た感じ
ですね。

{} 括り内は、その範囲で有効ですけど、try {} での宣言は全体ですね。

便宜上、関数を使えるけど、内部解釈では関数スタックではなくて、
一連の処理になるんですね。

引数をいっぱい書かなくて良くて便利ですけど、落とし穴に注意ですね。




## telnet-robot.ps1
##
## telnet プロトコルでサーバに接続してコマンドを実行する。
##
## 引数: remoteHost ホスト名
## port 接続ポート
## cmdfile リモートホストで実行するコマンドリスト
## logfile 実行ログを保存するファイル名
## user ユーザ名
## passwd パスワード
param(
[string] $remoteHost = "ukikusa",
[int] $port = 23,
[string] $cmdfile = "c:\users\kotora\com.txt",
[string] $logfile = "c:\users\kotora\Temp.txt",
[string] $user = "user",
[string] $passwd = "password"
)


# Telnet サーバからの問い合わせに応答(端末設定だけ vt100 にする)
# 復帰値: True ... テキストを受信したので続きの処理をお願い!
# False ... ネゴ用のストリームだけだったよ。
###############################################################################
function reply_IAC
{
$c = 0
for($c=0; $buffer[$c] -eq $IAC; $c+=3) {
if($buffer[$c+1] -eq $DO) {
switch($buffer[$c+2]) {
$TELOPT_TTYPE {
$nego = $IAC,$WILL,$buffer[$c+2]
$stream.Write($nego, 0, $nego.Length)
}
default {
$nego = $IAC,$WONT,$buffer[$c+2]
$stream.Write($nego, 0, $nego.Length)
}
}
$stream.Flush()
}
if($buffer[$c+1] -eq $SB) {
switch($buffer[$c+2]) {
$TELOPT_TTYPE {
# ターミナルタイプを vt100 に設定
$nego = $IAC,$SB,$TELOPT_TTYPE,00,118,116,49,48,48,$IAC,$SE
$stream.Write($nego, 0, $nego.Length)
}
default {
throw ("Unknown IAC SB request.$CR")
}
}
$stream.Flush()
# SB パケットだけ6バイトなのでその調整
$c+=3
}
if($buffer[$c+1] -eq $WILL) {
$nego = $IAC,$DO,$buffer[$c+2]
$stream.Write($nego, 0, $nego.Length)
$stream.Flush()
}
}
if($c -le 3) {
return $true
} else {
return $false
}
}


# Start Logging
###############################################################################
Start-Transcript -Path "$logfile"

# Main Loop
###############################################################################
try {
## ホストに TCP で接続
write-host "Connecting to $remoteHost on port $port`r`n"
$socket = new-object System.Net.Sockets.TcpClient($remoteHost, $port)
if($socket -eq $null) {
throw ("Could Not Connect")
}
write-host "Connected $remoteHost on port $port`r`n"

$stream = $socket.GetStream()
$writer = new-object System.IO.StreamWriter($stream)

$buffer = new-object System.Byte[] 1024
$nego = new-object System.Byte[] 1024
$encoding = new-object System.Text.UTF8Encoding

# Telnet Negotiation
$IAC = 255 # interpret as command:
$DONT = 254 # you are not to use option
$DO = 253 # please, you use option
$WONT = 252 # I won't use option
$WILL = 251 # I will use option
$SB = 250 # interpret as subnegotiation
$GA = 249 # you may reverse the line
$EL = 248 # erase the current line
$EC = 247 # erase the current character
$AYT = 246 # are you there
$AO = 245 # abort output--but let prog finish
$IP = 244 # interrupt process--permanently
$BREAK= 243 # break
$DM = 242 # data mark--for connect. cleaning
$NOP = 241 # nop
$SE = 240 # end of record (transparent mode)
$EOR = 239 # end of record (transparent mode)
$ABORT= 238 # Abort process
$SUSP = 237 # Suspend process
$xEOF = 236 # End of file: EOF is already used...

$TELOPT_BINARY = 0
$TELOPT_ECHO = 1
$TELOPT_SGA = 3
$TELOPT_NAWS = 4
$TELOPT_STATUS = 5
$TELOPT_NAOCRD = 10
$TELOPT_NAOFFD = 13
$TELOPT_TTYPE = 24
$TELOPT_TSPEED = 32
$TELOPT_XDISPLOC = 35
$TELOPT_NEW_ENVIRON = 39

$TELQUAL_IS = 0
$TELQUAL_SEND = 1
$TELQUAL_INFO = 2
$TELQUAL_REPLY = 2
$TELQUAL_NAME = 3

$CR = "`r`n"
$login_str = "login:"
$passwd_str = "Password:"
$debug = 0
$skip_byte = 0
$retry = 1800 # コマンド実行の最大待機時間: ($io_wait*$retry/1000)秒
$login_wait = 2000
$io_wait = 500

# Exsec comand list read
###########################################################################
$commands = Get-Content $cmdfile;

##
## Telnet Negitiation

# STAGE1: Telnet ネゴエーションとログイン
###########################################################################
$rep = 0
for($r=0; $r -lt $retry; $r++) {
start-sleep -m $io_wait
if ($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
if(reply_IAC) {
$line= $encoding.GetString($buffer, 0, $read)
if($line -match $login_str) {
write-host -n $line.SubString(3)
start-sleep -m $io_wait
$writer.WriteLine($user)
$writer.Flush()
}
if($line -match $passwd_str) {
write-host -n $line
start-sleep -m $io_wait
$writer.WriteLine($passwd)
$writer.Flush()
break
}
$rep = 1
}
}
}
if($rep -eq 0) {
throw ("No response negoation.$CR")
}
# プロンプトを受信
###########################################################################
$rep = 0
for($r=0; $r -lt $retry; $r++) {
start-sleep -m $io_wait
if ($stream.DataAvailable) {
while($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
write-host -n ($encoding.GetString($buffer, 0, $read))
}
$rep = 1
break
}
}
if($rep -eq 0) {
throw ("No response login.$CR")
}
# ここから実行したいコマンドを実行…コマンドがなくなるまで繰り返し
###########################################################################
start-sleep -m $login_wait
for($i=0; $i -le $commands.Count; $i++) {
$rep = 0
$line = ""
start-sleep -m $io_wait
$writer.WriteLine($commands[$i])
$writer.Flush()
$i++
for($r=0; $r -lt $retry; $r++) {
start-sleep -m $io_wait
if ($stream.DataAvailable) {
while($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
$line = ($encoding.GetString($buffer, 0, $read))
write-host -n $line
# 実行完了文字列をチェック
if($line -match $commands[$i]) {
$rep = 1
}
}
if($rep -eq 1) {
break
}
}
}
if($rep -eq 0) {
throw ("No response Command.$CR")
}
}
# ログアウト
###########################################################################
start-sleep -m $io_wait
$writer.WriteLine("exit")
$writer.Flush()

# ホストからの応答を受けて処理終了
###########################################################################
$rep = 0
for($r=0; $r -lt $retry; $r++) {
start-sleep -m $io_wait
if ($stream.DataAvailable) {
while($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
write-host -n ($encoding.GetString($buffer, 0, $read))
}
start-sleep -m $io_wait
$writer.WriteLine("bye!") # 送信はなんでもOK!
$writer.Flush()
$rep = 1
break
}
}
if($rep -eq 0) {
throw ("No response exit.$CR")
}
}

# ERROR Routine(catch throw)
###############################################################################
catch {
write-host $error[0]
$dateTime = get-date
$errorOccurence = "Error occurred connecting to $remoteHost on $port at $dateTime"
write-host $errorOccurence
}

# Finalize
###############################################################################
finally {
write-host $CR
write-host exit program.$CR
$writer.Close()
$stream.Close()

stop-transcript
}