web-dev-qa-db-ja.com

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

シェルスクリプト内から、指定された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

274
Yanick Girouard

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

nc -z <Host> <port>

このようにしてncはポートが開いているかどうかだけをチェックし、成功すれば0、失敗すれば1で終了します。

迅速な対話式チェック(タイムアウト5秒)の場合

nc -z -v -w5 <Host> <port>
419
Alessio Gaeta

ncname__に対する-zおよび-w TIMEOUTオプションを使用するのは簡単ですが、すべてのシステムにncname__がインストールされているわけではありません。あなたが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

ここで起こっていることは、timeoutname__がサブコマンドを実行し、指定されたタイムアウト(上記の例では1秒)以内に終了しない場合はそれを強制終了することです。この場合、bashname__がサブコマンドで、その特殊な /dev/tcp処理 を使用して、指定されたサーバーとポートへの接続を試行します。 bashname__がタイムアウト内に接続を開くことができる場合、catname__はただちにそれを閉じ(/dev/nullから読み取っているため)、0のステータスコードで終了し、bashname__、次にtimeoutname__を介して伝搬します。指定されたタイムアウトの前にbashname__が接続障害を起こした場合、bashname__は終了コード1で終了し、timeoutname__も戻ります。そしてbashが接続を確立できず、指定されたタイムアウト時間が切れると、timeoutname__はbashname__を終了させ、ステータス124で終了します。

126
onlynone

目次:

  • Bashとtimeoutnameを使用する__
    • コマンド
  • ncnameを使用する__
    • コマンド
    • RHEL 6(nc-1.84)
      • Installation
    • RHEL 7(nmap-ncat-6.40)
      • Installation
  • 備考

Bashとtimeoutname__を使う:

timeoutname__はRHEL 6以降で存在する必要があるか、あるいはGNU coreutils 8.22にあります。 MacOSでは、brew install coreutilsを使用してインストールし、gtimeoutname__として使用します。

コマンド:

$ 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

bashname__の終了ステータスを保存する必要がある場合は、

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

ncname__を使用する:

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

コマンド:

以下のコマンドは、RHEL 6と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 $?コマンドは、もちろん説明のためだけのものです。

96
A-B-B

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
49
Brandon Rhodes

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()システムコールタイムアウトを減らす方法

25
kenorb

私は 12 に基づいて次のような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
10
Ahmad Yoosofan

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を使用してソケットから読み取ります。

10
merebagatelle

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

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

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

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
PRF

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として返します。

4
Chris Mendez

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
3
krbu

私は答えが遅すぎると思います、そしてこれは良いものではないかもしれませんが、ここであなたは行き​​ます...

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

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

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

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

1
Red Thomas

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をインストールする必要があります。

1

これは舞台裏で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
Brad Parks

最も投票の多い回答を基にして、これは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
}