シェルスクリプトからリモートTCPポートが開いているかどうかをテストする


297

シェルスクリプト内から、指定されたTCPポートがリモートサーバーで開いているかどうかを適切にテストするための迅速で簡単な方法を探しています。

私はなんとかtelnetコマンドでそれを行うことができました、そしてそれはポートが開かれるときうまくいきます、しかしそれがそうでないときそれがタイムアウトするようには見えないし、そこでハングアップします...

これがサンプルです:

l_TELNET=`echo "quit" | telnet $SERVER $PORT | grep "Escape character is"`
if [ "$?" -ne 0 ]; then
  echo "Connection to $SERVER on port $PORT failed"
  exit 1
else
  echo "Connection to $SERVER on port $PORT succeeded"
  exit 0
fi

私はより良い方法、またはたとえば8秒未満で接続しない場合にtelnetを強制的にタイムアウトさせ、シェルでキャッチできるものを返す方法(戻りコード、または標準出力の文字列)が必要です。

IO :: Socket :: INETモジュールを使用し、ポートをテストする成功したスクリプトを記述したPerlメソッドを知っていますが、可能であればPerlの使用を避けたいです。

注:これは私のサーバーが実行しているものです(ここから実行する必要があります)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc



答えは期待にそそがれた。8秒のタイムアウトで、必要なポートにTelnetを送信する簡単なスクリプトを作成しました。そこから選ぶべき例もたくさんあります。私たちはこのポストオフに基づいて我々 :unix.com/shell-programming-scripting/...
Yanick Girouard

1
github.com/monitoring-plugins/monitoring-pluginsのcheck_tcpは、文字列の入力や予想される回答の確認など、これを行うことができます。

回答:


452

B. Rhodesが指摘したncように、仕事をします。それを使用するよりコンパクトな方法:

nc -z <host> <port>

この方法でncは、ポートが開いているかどうかのみがチェックされ、成功すると0、失敗すると1で終了します。

簡単なインタラクティブチェックの場合(タイムアウトは5秒):

nc -z -v -w5 <host> <port>

48
centos7のデフォルトはnmap netcatを使用し、-zオプションはありません。
-jolestar

7
これはRHEL / Centosでは機能しません。これらのディストリビューションでは、次のことを行う必要があります。nc-vn <host> <port>
Justin Dennahower

2
FWIW、私はRHEL 6とRHEL 7の両方に個別に適用可能な例を使用して私の回答を完全に見直しました
Acumenus

4
Macでは少なくとも、-G#タイムアウトとは別に、または-w#基本的に読み取りタイムアウトとして機能する接続タイムアウトを設定するために、追加する必要がある場合があります。
Doktor J

1
@jolestar Centos 7でNcatを手動でアップグレードして、-zオプションを取得できます。あなたは検討する必要があります。unix.stackexchange.com/questions/393762/...
ダミアンÓCeallaigh

150

これはと行う簡単な十分だ-z-w TIMEOUTオプションにnc、しかし、すべてのシステムはしていないncインストールされています。最近の十分なバージョンのbashがある場合、これは機能します。

# Connection successful:
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/google.com/80'
$ echo $?
0

# Connection failure prior to the timeout
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/sfsfdfdff.com/80'
bash: sfsfdfdff.com: Name or service not known
bash: /dev/tcp/sfsfdfdff.com/80: Invalid argument
$ echo $?
1

# Connection not established by the timeout
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/google.com/81'
$ echo $?
124

ここで起こっていることtimeoutは、指定されたタイムアウト(上記の例では1秒)内に終了しない場合、サブコマンドを実行して強制終了することです。この場合bashはサブコマンドであり、特別な/ dev / tcp処理を使用して、指定されたサーバーとポートへの接続を試行して開きます。場合はbashタイムアウト時間内に接続を開くことができ、catちょうど近いことでしょう、すぐに(それから読んでいるので、/dev/null)とのステータスコードで終了し0て伝播しbash、その後とtimeoutbash指定されたタイムアウトの前に接続障害が発生した場合は、bash終了コード1で終了し、これtimeoutも戻ります。そして、bashが接続を確立できず、指定されたタイムアウトの期限が切れた場合、timeout殺すbashと124のステータスで終了。


1
ある/dev/tcpLinux以外のシステムで利用可能?特にMacはどうですか?
ピーター

8
/ dev / tcpはbashの機能なので、はい。ただし、
Macに

1
@onlynone- timeoutFreeBSDにも存在しないようです。古いUbuntuボックスではインストール可能なパッケージですが、デフォルトではありません。タイムアウトやネットキャットのようなサードパーティのツールなしで、bashだけでこれを行う方法があったら素晴らしいでしょう。
グラハム

3
timeoutGNU coreutilsの一部であるように思われ、homebrew:を使用してMacにインストールできることをお伝えしたいと思いますbrew install coreutils。その後、として利用できるようになりますgtimeout
onlynone

1
@Wildcard bashの変更ログが示唆bash-2.04-develしていますが、ホスト名のサポートが追加されている可能性がありますbash-2.05-alpha1
Acumenus 2016年

109

目次:

  • bashと timeout
    • コマンド
  • 使用する nc
    • コマンド
    • RHEL 6(nc-1.84)
      • 取り付け
    • RHEL 7(nmap-ncat-6.40)
      • 取り付け
  • 備考

bashとtimeout

timeoutRHEL 6+に存在するか、GNU coreutils 8.22に存在することに注意してください。MacOSでは、brew install coreutilsを使用してインストールし、として使用しますgtimeout

コマンド:

$ timeout $TIMEOUT_SECONDS bash -c "</dev/tcp/${HOST}/${PORT}"; echo $?

ホストとポートをパラメータ化した場合、としてそれらを指定してください${HOST}${PORT}、上記のようです。$HOSTand としてのみ$PORT、つまり中括弧なしで指定しないでください。この場合は機能しません。

例:

成功:

$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/80"; echo $?
0

失敗:

$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
124

の終了ステータスを保持する必要がある場合bash

$ timeout --preserve-status 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
143

使用nc

の下位互換性のないバージョンがncRHEL 7にインストールされることに注意してください。

コマンド:

以下のコマンドは、RHEL 6とRHEL 7の両方で同一であるという点でユニークであることに注意してください。異なるのは、インストールと出力だけです。

$ nc -w $TIMEOUT_SECONDS -v $HOST $PORT </dev/null; echo $?

RHEL 6(nc-1.84):

インストール:

$ sudo yum install nc

例:

成功:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Connection to canyouseeme.org 80 port [tcp/http] succeeded!
0
失敗:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
nc: connect to canyouseeme.org port 81 (tcp) timed out: Operation now in progress
1

ホスト名が複数のIPにマップされている場合、上記の失敗したコマンドはそれらの多くまたはすべてを循環します。例えば:

$ nc -w 2 -v microsoft.com 81 </dev/null; echo $?
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
1

RHEL 7(nmap-ncat-6.40):

インストール:

$ sudo yum install nmap-ncat

例:

成功:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connected to 52.202.215.126:80.
Ncat: 0 bytes sent, 0 bytes received in 0.22 seconds.
0
失敗:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection timed out.
1

ホスト名が複数のIPにマップされている場合、上記の失敗したコマンドはそれらの多くまたはすべてを循環します。例えば:

$ nc -w 2 -v microsoft.com 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection to 104.43.195.251 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 23.100.122.175 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 23.96.52.53 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 191.239.213.197 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection timed out.
1

備考:

-v--verbose)の引数とecho $?コマンドは、例示のみのために当然です。


3
timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?素晴らしい点です!
peacocks

2>&1エコーのステータスに 追加result_test=$(nc -w 2 $ip_addreess 80 </dev/null 2>&1 ; echo $?)
Sanya Snex

55

ではnetcat、あなたは、ポートが、このようなオープンであるかどうかを確認することができます。

nc my.example.com 80 < /dev/null

の戻り値はnc、TCPポートが開かれた場合は成功し、TCP接続を確立できなかった場合は失敗します(通常は戻りコード1)。

ncからファイルの終わりを受け取った後でも、ソケットの送信側の半分を閉じないため、これを試行するとの一部のバージョンがハングします/dev/null。私自身のUbuntuラップトップ(18.04)では、netcat-openbsdインストールしたnetcat のバージョンが回避策を提供します。この-Nオプションは、すぐに結果を得るのに必要です。

nc -N my.example.com 80 < /dev/null

1
フラグをncサポートしていないフレーバーに最適です -w
David Dossot、2015年

5
おかげで- -zサポートなしのncのバージョンでも動作します-たとえば、RHEL / CentOS 7で動作します
Andrew

1
この方法は適切です-wが、スクリプトで使用すると10秒以上ハングする可能性があるため失敗します。を含む回答を書きました-w
Acumenus 2016年

2
+1 ncはalpine linuxとubuntuの両方に標準で付属しているので、これは素晴らしいことです。おそらく他の人。遠隔地にいて遠隔地に行けない場合でも、追加でインストールする必要はありません。これをありがとう!追加するかもしれませんnc host port -w 2 && echo it works
std''OrgnlDave

2
私は好むnc -vz localhost 3306。より詳細な出力が得られます。Macでのみ試してみました。
EFreak 2017年

29

Bashでは、TCP / UDP接続に疑似デバイスファイルを使用するのは簡単です。スクリプトは次のとおりです。

#!/usr/bin/env bash
SERVER=example.com
PORT=80
</dev/tcp/$SERVER/$PORT
if [ "$?" -ne 0 ]; then
  echo "Connection to $SERVER on port $PORT failed"
  exit 1
else
  echo "Connection to $SERVER on port $PORT succeeded"
  exit 0
fi

テスト:

$ ./test.sh 
Connection to example.com on port 80 succeeded

以下はワンライナー(Bash構文)です。

</dev/tcp/localhost/11211 && echo Port open. || echo Port closed.

一部のサーバーはSYNフラッド攻撃からファイアウォールで保護できるため、TCP接続タイムアウト(〜75秒)が発生する可能性があることに注意してください。タイムアウトの問題を回避するには、次のことを試してください。

timeout 1 bash -c "</dev/tcp/stackoverflow.com/81" && echo Port open. || echo Port closed.

参照:TCP connect()システムコールタイムアウトを減らす方法は?


1
@ABBこれは、SYNフラッド攻撃を防ぐために応答しないサーバーのファイアウォール構成に関連しています。実行telnet canyouseeme.org 81もハングします。これは、おそらくbashにハードコードされているタイムアウト制限によって制御されます。参照:TCP connect()システムコールタイムアウトを減らす
kenorb

パラメータ化のために、今で指定する必要があるようだ$SERVER$PORT、少なくともとして、中括弧で${SERVER}${PORT}
Acumenus 2016年

13

複数のgitリポジトリで作業するためのより柔軟なソリューションが必要だったので、12に基づいて次のshコードを書きました。gitlab.comの代わりにサーバーアドレスを使用し、22の代わりにポートを使用できます。

SERVER=gitlab.com
PORT=22
nc -z -v -w5 $SERVER $PORT
result1=$?

#Do whatever you want

if [  "$result1" != 0 ]; then
  echo  'port 22 is closed'
else
  echo 'port 22 is open'
fi

1
ありがとう!/etc/rc.localの使いやすさ
Shahin Khaled

10

kshまたはbashを使用している場合、どちらも/ dev / tcp / IP / PORT構成を使用してソケットへの、またはソケットからのIOリダイレクトをサポートします。この中でKornシェルの例私は組合のをリダイレクトしないのです()STD-にソケットから:

W$ python -m SimpleHTTPServer &
[1]     16833
Serving HTTP on 0.0.0.0 port 8000 ...
W$ : </dev/tcp/127.0.0.1/8000

ソケットが開いていない場合、シェルはエラーを出力します。

W$ : </dev/tcp/127.0.0.1/8001
ksh: /dev/tcp/127.0.0.1/8001: cannot open [Connection refused]

そのため、これを使用することができ、テストあれば条件:

SERVER=127.0.0.1 PORT=8000
if (: < /dev/tcp/$SERVER/$PORT) 2>/dev/null
then
    print succeeded
else
    print failed
fi

no-opはサブシェルにあるため、std-inリダイレクトが失敗した場合にstd-errを破棄できます。

HTTPを介してリソースの可用性を確認するために/ dev / tcpをよく使用します。

W$ print arghhh > grr.html
W$ python -m SimpleHTTPServer &
[1]     16863
Serving HTTP on 0.0.0.0 port 8000 ...
W$ (print -u9 'GET /grr.html HTTP/1.0\n';cat <&9) 9<>/dev/tcp/127.0.0.1/8000
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.6.1
Date: Thu, 14 Feb 2013 12:56:29 GMT
Content-type: text/html
Content-Length: 7
Last-Modified: Thu, 14 Feb 2013 12:55:44 GMT

arghhh
W$ 

このワンライナーは、ソケットの読み取りと書き込みのためにファイル記述子9を開き、ソケットにHTTP GETを出力し、ソケットcatからの読み取りに使用します。


1
@ABBたとえば、ポートがフィルターされている場合など、ポートが応答していない場合や、IPに何もない場合は、長時間ハングすることになります。ポートが接続をアクティブに拒否した場合、ポートが閉じても遅延は発生しません。多くの目的で、これはまったく問題ありません。
mc0e 2018年

この手法は、この回答の前にkenorbによる以前の回答ですでに投稿されています。とにかく、より大きなポイントは、ポートが応答しない場合は長時間ハングすることです。たとえば、してみてください</dev/tcp/canyouseeme.org/80そしてそして</dev/tcp/canyouseeme.org/81
Acumenus 2018年

9

古い質問でしたが、私はそれの変種を扱っただけですが、ここでの解決策のどれも適用できなかったので、私は別のものを見つけて、後世のためにそれを追加しています。はい、私はOPが彼らがこのオプションを知っていて、それが彼らに合わなかったと言ったのを知っています、しかし、後に続く誰にとっても、それは役に立つかもしれません。

私の場合、ビルドapt-cacher-ngからローカルサービスが利用可能かどうかをテストしたいと思いますdocker。つまり、テストの前にインストールできるものはまったくありません。ノーncnmapexpecttelnetまたはpythonperlただし、コアライブラリと共に存在するため、これを使用しました。

perl -MIO::Socket::INET -e 'exit(! defined( IO::Socket::INET->new("172.17.42.1:3142")))'

6

curl、telnet、nc o nmapなどのツールが使用できない場合でも、wgetを使用する機会があります

if [[ $(wget -q -t 1 --spider --dns-timeout 3 --connect-timeout 10  host:port; echo $?) -eq 0 ]]; then echo "OK"; else echo "FAIL"; fi

6

bashを使用してポートを確認する

$ ./test_port_bash.sh 192.168.7.7 22

ポート22が開いている

コード

HOST=$1
PORT=$2
exec 3> /dev/tcp/${HOST}/${PORT}
if [ $? -eq 0 ];then echo "the port $2 is open";else echo "the port $2 is closed";fi

4

を使用したいncがサポートするバージョンがない場合は、次-zを使用してみてください--send-only

nc --send-only <IP> <PORT> </dev/null

タイムアウトあり:

nc -w 1 --send-only <IP> <PORT> </dev/null

IPの場合はDNSルックアップなし:

nc -n -w 1 --send-only <IP> <PORT> </dev/null

-z接続できるかどうかに基づいてコードを返します。


1

答えを出すには遅すぎると思いますが、これは良い答えではないかもしれませんが、ここに行きます...

なんらかのタイマーを備えたwhileループの中にそれを置くのはどうですか?私はSolarisよりもPerlの人ですが、使用しているシェルによっては、次のようなことができるはずです。

TIME = 'date +%s' + 15
while TIME != `date +%s'
do whatever

次に、whileループにフラグを追加するだけで、完了前にタイムアウトになった場合に、失敗の理由としてタイムアウトを引用できます。

Telnetにもタイムアウトスイッチがあるのではないかと思いますが、頭の上の部分では、上記の方法でうまくいくと思います。


1

cronで実行され、出力されない短いスクリプトが必要でした。nmapを使用して問題を解決します

open=`nmap -p $PORT $SERVER | grep "$PORT" | grep open`
if [ -z "$open" ]; then
  echo "Connection to $SERVER on port $PORT failed"
  exit 1
else
  echo "Connection to $SERVER on port $PORT succeeded"
  exit 0
fi

実行するにはデフォルトのインストールパッケージではないため、nmapをインストールする必要があります。


nmapのgrepable出力は-oG -、それをstdoutに送信するのにも役立つ場合があります。(grepを少し変更する必要があります)
Gert van den Berg

0

これは裏でtelnetを使用しており、mac / linuxでは問題なく動作するようです。linux / mac上のバージョン間の違いのため、netcatを使用していません。これはデフォルトのmacインストールで動作します。

例:

$ is_port_open.sh 80 google.com
OPEN

$ is_port_open.sh 8080 google.com
CLOSED

is_port_open.sh

PORT=$1
HOST=$2
TIMEOUT_IN_SEC=${3:-1}
VALUE_IF_OPEN=${4:-"OPEN"}
VALUE_IF_CLOSED=${5:-"CLOSED"}

function eztern()
{
  if [ "$1" == "$2" ]
  then
    echo $3
  else
    echo $4
  fi
}

# cross platform timeout util to support mac mostly
# https://gist.github.com/jaytaylor/6527607
function eztimeout() { perl -e 'alarm shift; exec @ARGV' "$@"; }

function testPort()
{
  OPTS=""

  # find out if port is open using telnet
  # by saving telnet output to temporary file
  # and looking for "Escape character" response
  # from telnet
  FILENAME="/tmp/__port_check_$(uuidgen)"
  RESULT=$(eztimeout $TIMEOUT_IN_SEC telnet $HOST $PORT &> $FILENAME; cat $FILENAME | tail -n1)
  rm -f $FILENAME;
  SUCCESS=$(eztern "$RESULT" "Escape character is '^]'." "$VALUE_IF_OPEN" "$VALUE_IF_CLOSED")

  echo "$SUCCESS"
}

testPort 

0

最も投票数の多い回答に基づいて、タイムアウト付きで2つのポートが開くまで待機する関数を次に示します。開いている2つのポート、8890と1111、およびmax_attempts(1秒あたり1)に注意してください。

function wait_for_server_to_boot()
{
    echo "Waiting for server to boot up..."
    attempts=0
    max_attempts=30
    while ( nc 127.0.0.1 8890 < /dev/null || nc 127.0.0.1 1111 < /dev/null )  && [[ $attempts < $max_attempts ]] ; do
        attempts=$((attempts+1))
        sleep 1;
        echo "waiting... (${attempts}/${max_attempts})"
    done
}

-1

nmap-ncatは、まだ使用されていないローカルポートをテストします


availabletobindon() {
  port="$1"
  nc -w 2 -i 1 localhost "$port" 2>&1 | grep -v -q 'Idle timeout expired'
  return "$?"
}

ポート80が開いていることはわかりません。
totoro
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.