シェルスクリプト内から、指定された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を強制的にタイムアウトさせ、Shellでキャッチできるもの(リターンコード、または標準出力の文字列)を返す方法が必要です。
私はPerlメソッドを知っています。これはIO :: Socket :: INETモジュールを使用し、ポートをテストする成功したスクリプトを書きましたが、可能であればPerlの使用を避けたいと思います。
注:これは私のサーバーが実行しているものです(ここから実行する必要があります)。
SunOS 5.10 Generic_139556-08 i86pc i386 i86pc
B. Rhodesが指摘したように、nc
が仕事をします。それを使用するためのよりコンパクトな方法:
nc -z <Host> <port>
このようにしてnc
はポートが開いているかどうかだけをチェックし、成功すれば0、失敗すれば1で終了します。
迅速な対話式チェック(タイムアウト5秒)の場合
nc -z -v -w5 <Host> <port>
nc
name__に対する-z
および-w TIMEOUT
オプションを使用するのは簡単ですが、すべてのシステムにnc
name__がインストールされているわけではありません。あなたが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
name__がサブコマンドを実行し、指定されたタイムアウト(上記の例では1秒)以内に終了しない場合はそれを強制終了することです。この場合、bash
name__がサブコマンドで、その特殊な /dev/tcp処理 を使用して、指定されたサーバーとポートへの接続を試行します。 bash
name__がタイムアウト内に接続を開くことができる場合、cat
name__はただちにそれを閉じ(/dev/null
から読み取っているため)、0
のステータスコードで終了し、bash
name__、次にtimeout
name__を介して伝搬します。指定されたタイムアウトの前にbash
name__が接続障害を起こした場合、bash
name__は終了コード1で終了し、timeout
name__も戻ります。そしてbashが接続を確立できず、指定されたタイムアウト時間が切れると、timeout
name__はbash
name__を終了させ、ステータス124で終了します。
timeout
nameを使用する__ nc
nameを使用する__ timeout
name__を使う:timeout
name__はRHEL 6以降で存在する必要があるか、あるいはGNU coreutils 8.22にあります。 MacOSでは、brew install coreutils
を使用してインストールし、gtimeout
name__として使用します。
$ timeout $TIMEOUT_SECONDS bash -c "</dev/tcp/${Host}/${PORT}"; echo $?
ホストとポートをパラメータ化する場合は、上記のように必ず${Host}
と${PORT}
として指定してください。単に$Host
および$PORT
として、つまり中括弧なしで指定しないでください。この場合はうまくいきません。
$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/80"; echo $?
0
$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
124
bash
name__の終了ステータスを保存する必要がある場合は、
$ timeout --preserve-status 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
143
nc
name__を使用する:nc
name__の下位互換性のないバージョンがRHEL 7にインストールされることに注意してください。
以下のコマンドは、RHEL 6と7の両方で同じであるという点でユニークです。異なるのはインストールと出力だけです。
$ nc -w $TIMEOUT_SECONDS -v $Host $PORT </dev/null; echo $?
$ 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
$ 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 $?
コマンドは、もちろん説明のためだけのものです。
netcat
を使えば、ポートが開いているかどうかを確認できます。
nc my.example.com 80 < /dev/null
TCPポートが開かれていればnc
の戻り値は成功し、TCP接続ができなかった場合は失敗(通常は戻りコード1)になります。
nc
のいくつかのバージョンはこれを試すとハングします、なぜならそれらは/dev/null
からファイルの終わりを受け取った後でさえもソケットの送信側半分を閉じないからです。私自身のUbuntuラップトップ(18.04)で、私がインストールしたnetcatのnetcat-openbsd
バージョンは回避策を提供します:-N
オプションは即座の結果を得るために必要です:
nc -N my.example.com 80 < /dev/null
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.
私は 1 と 2 に基づいて次のようなshコードを書いたので、私は複数のgitリポジトリで作業するためのより柔軟なソリューションを必要としていました。あなたは22の代わりにgitlab.comとあなたのポートの代わりにあなたのサーバーアドレスを使うことができます。
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
ksh または bash を使用している場合は、両方とも /dev/tcp/IP/PORT 構文を使用してソケットへの/からのIOリダイレクトをサポートします。この Kornシェル の例私は、ソケットから何もしない( : )標準入力をリダイレクトしています。
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]
したがって、これを if 条件で test として使用できます。
SERVER=127.0.0.1 PORT=8000
if (: < /dev/tcp/$SERVER/$PORT) 2>/dev/null
then
print succeeded
else
print failed
fi
No-opはサブシェル内にあるので、標準入力リダイレクションが失敗した場合は標準エラーを捨てることができます。
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
を使用してソケットから読み取ります。
古い質問ですが、その変種を扱っただけですが、ここでの解決策はどれも適用できなかったので、別の方法を見つけて、後世に追加しています。はい、私はOPが彼らがこの選択肢を知っていたと言ったことを知っています、そしてそれは彼らには合いませんでした、しかし後に続く誰にとってもそれは役に立つかもしれません。
私の場合は、docker
ビルドからローカルのapt-cacher-ng
サービスが利用可能かどうかをテストしたいと思います。つまり、テストの前にインストールすることは絶対にできません。 nc
、nmap
、expect
、telnet
、またはpython
はありません。 Perl
がコアライブラリと一緒に存在するので、私はこれを使いました:
Perl -MIO::Socket::INET -e 'exit(! defined( IO::Socket::INET->new("172.17.42.1:3142")))'
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
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
として返します。
$ ./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
私は答えが遅すぎると思います、そしてこれは良いものではないかもしれませんが、ここであなたは行きます...
なんらかのタイマーを入れたwhileループの中に置くのはどうでしょうか。私はSolarisというよりPerlの人ですが、使用しているシェルによっては、次のようなことができるはずです。
TIME = 'date +%s' + 15
while TIME != `date +%s'
do whatever
そして、whileループにフラグを追加するだけで、完了前にタイムアウトになった場合は、失敗の理由としてタイムアウトを引用できます。
Telnetにもタイムアウトスイッチがあるのではないかと思いますが、頭のすぐそばで、上記の方法でうまくいくと思います。
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をインストールする必要があります。
これは舞台裏でtelnetを使っており、mac/linuxではうまく動くようです。 linux/macのバージョン間の違いのためにnetcatを使いません、そしてこれはデフォルトのmacインストールで動作します。
$ is_port_open.sh 80 google.com
OPEN
$ is_port_open.sh 8080 google.com
CLOSED
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
最も投票の多い回答を基にして、これは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
}