我正在寻找一种快速而简单的方法,用于正确地测试一个给定的TCP端口是否在远程服务器上打开,从Shell脚本中。

我已经设法用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

我要么需要一个更好的方法,或者一种方法强制telnet超时,如果它没有在8秒内连接,例如,并返回一些我可以在Shell中捕获的东西(返回代码,或stdout中的字符串)。

我知道Perl方法,它使用IO::Socket::INET模块,并编写了一个成功的测试端口的脚本,但如果可能的话,宁愿避免使用Perl。

注意:这是我的服务器正在运行(我需要从哪里运行这个)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc


当前回答

在某些情况下,像curl, telnet, nc和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

其他回答

TOC:

使用bash和timeout 命令 例子 使用数控 命令 RHEL 6 (nc-1.84) 安装 例子 RHEL 7 (nmap-ncat-6.40) 安装 例子 讲话

使用bash和timeout:

请注意,超时应该在RHEL 6+中出现,或者在GNU coreutils 8.22中也可以找到。在MacOS上,使用brew install coreutils安装它,并将其用作gtimeout。

命令:

$ 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的退出状态,

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

使用数控:

注意,在RHEL 7上安装了向后不兼容的nc版本。

命令:

注意,下面的命令是唯一的,因为它对于RHEL 6和7都是相同的。只是安装和输出不同而已。

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

RHEL 6 (nc-1.84):

安装:

$ sudo yum install nc

例子:

Success:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Connection to canyouseeme.org 80 port [tcp/http] succeeded!
0
Failure:
$ 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,上述失败的命令将循环遍历多个或所有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

例子:

Success:
$ 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
Failure:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection timed out.
1

如果主机名映射到多个ip,上述失败的命令将循环遍历多个或所有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)参数和回显$?命令当然只是为了说明。

使用netcat,你可以像这样检查端口是否打开:

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

如果TCP端口被打开,nc的返回值为success,如果不能建立TCP连接,则返回值为failure(通常为返回码1)。

当你尝试这样做时,某些版本的nc会挂起,因为它们即使在从/dev/null.接收到文件结束后也不会关闭它们的套接字的发送部分在我自己的Ubuntu笔记本电脑(18.04)上,我安装的netcat-openbsd版本的netcat提供了一个变通方案:必须使用-N选项才能立即得到结果:

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

在某些情况下,像curl, telnet, nc和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或/dev/tcp/$hostname/$port,但支持超时,所以我回到telnet,如下所示:

if echo "quit" | timeout 2 telnet $SERVER $PORT 2>&1 | grep -q 'Connected to'; then
    echo "Connection to $SERVER on port $PORT succeeded"
    exit 0
else
    echo "Connection to $SERVER on port $PORT failed"
    exit 1
fi

我需要一个更灵活的解决方案来处理多个git存储库,所以我根据1和2编写了下面的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