PMPの流儀

我が家の流儀

我が家で気になったことを夫婦で取り上げていきます

MENU

PowerShell TCPポートの接続確認(警告表示なし)

PowerShell では Test-NetConnection でTCPポートを指定して接続確認ができます。この命令は対話向けコマンドで進行状況や警告が表示されます。スクリプトで結果だけほしい場合に非表示にする方法を紹介します。

1. コマンドで確認

存在しないポート確認を指定してみます。yahooのポート81への接続確認です。

Test-NetConnection www.yahoo.co.jp -Port 81

コマンド実行中にコンソールの一部に以下のメッセージが表示されます。ポップアップのイメージです。

Test-NetConnection - 182.22.28.252:81
     Attemting TCP connect

     Waiting for response

完了するとこのメッセージは消えて、コマンドの結果として以下が出力されます。

警告: TCP connect to (183.79.250.123 : 81) failed                                                                                                             
ComputerName           : www.yahoo.co.jp
RemoteAddress          : 183.79.250.123
RemotePort             : 81
InterfaceAlias         : Wi-Fi
SourceAddress          : 192.168.0.14
PingSucceeded          : True
PingReplyDetails (RTT) : 38 ms
TcpTestSucceeded       : False

結果とともに、警告の行も出力されています。

2. スクリプトでの実装

結果を変数で受けて判定します。メンバーのTcpTestSucceededに boolean で結果が入っています。

$ret = Test-NetConnection www.yahoo.co.jp -Port 81
if( $ret.TcpTestSucceeded ) {
   # 成功ケース
}

直接コマンドを入力したときと同じように、進行状況や警告はそのまま表示されています。

3. スクリプトでの実装の最適化

ここから最適化をします。

  • 結果を直接 Boolean で受けとる
  • コマンド進行状況を非表示にする
  • 存在しないポートだった場合の警告を非表示にする。

3.1 結果を直接 Boolean で受けとる

引数に -InformationLevel Quiet を追加します。

これにより、TcpTestSucceeded の結果が戻り値になります。

3.2 コマンド進行状況を非表示にする

表示を抑制するために、基本設定変数の1つである $ProgressPreference の値を変更します。

$global:ProgressPreference = "SilentlyContinue"

スコープを global 指定することで、ローカル変数へのアクセスを防止します。

初期値は、Continue です。

公式サイトの解説はこちら
基本設定変数について - PowerShell | Microsoft Learn

3.4 警告出力を非表示にする。

警告メッセージは「警告ストリーム」に出力されるため、コマンドの結果を変数に代入しても出力されてしまいます。警告ストリームを処理しないと結果としてコンソールに出力されてしまいます。

対策として、警告ストリームを捨てる処理をします。コマンド最後に以下を追加します。

3> OUT-Null

3が警告ストリームを意味します。'3' と '>' の間にスペースを入れるとエラーになるので注意です。

3.5 最終系

上記の内容を取り入れた結果が以下です。

$global:ProgressPreference="SilentlyContinue"
$ret = Test-NetConnection www.yahoo.co.jp -Port 81 -InformationLevel Quiet 3> Out-Null
if($ret) {
  # 成功ケース
}

これにより余計な出力がなく結果を得ることができるようになりました。

サーバーを起動後、接続できるまで待つ時はこんな感じで組めます。その間余計な出力はありません。

$global:ProgressPreference="SilentlyContinue"
while(1) {
    $ret = Test-NetConnection MyServer -Port 80 -InformationLevel Quiet 3> Out-Null
    if( $ret ) {
        break
    }
    Start-Sleep -Milliseconds 200
}

4. 別実装

これで最終系にしたつもりだったのですが、Test-NetConnection は接続先が見つからないとタイムアウトが長いことに気づきました。これは socket の connect() と同じ動きです。オプションを探したのですがタイムアウトを設定する方法はないらしく。

接続できることが分かっていて待つ場面であればよいのですが、ただの存在確認の場合には致命的です。

仕方ない…自分で作るか・・・と、.Net Framework をみて以下の関数を作成しました。

Function test_connect($host, $port, $timeout)
{
    $cn = New-Object Net.Sockets.TcpClient
 
    $ac = $cn.BeginConnect( $host, $port, $null, $null);
    $ac.AsyncWaitHandle.WaitOne( $timeout) | Out-Null

    $ret = $cn.Connected

    $cn.Close()
    $cn.Dispose()
    $cn = $null

    return $ret
}

$timeout は msec のタイムアウト値です。戻り値は Boolean 型で接続が成功したか否かを返します。

結論は Test-NetConnection はインタラクティブツールだったという事です。PowerShellのコマンドレット群は便利なのですが機能を絞っている部分もあるので、本格的に作りこむときには .Net ベースで作らないと駄目な場面もあります。