如果远程TCP端口已打开,则从shell脚本进行testing

我正在寻找一种快速而简单的方法,用于从Shell脚本中正确testing远程服务器上是否打开给定的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 

我要么需要更好的方法,要么强制telnet超时,如果它不能在8秒内连接,并返回一些我可以在壳牌(返回代码,或在标准输出string)捕获的东西。

我知道Perl方法,它使用IO :: Socket :: INET模块,编写了一个testing端口的成功脚本,但是如果可能的话,宁愿避免使用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> 

使用-z-w TIMEOUT选项很容易,但不是所有的系统都安装了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会立即closures它(因为它是从/dev/null读取的),并以状态码0退出,然后通过bash传播,然后timeout 。 如果bash在指定的超时之前获得连接失败,那么bash将以退出代码1退出,该退出代码也将返回。 如果bash无法build立连接,并且指定的超时过期,则timeout将终止bash并退出,状态为124。

TOC:

  • 使用bash和timeout
    • 命令
    • 例子
  • 使用nc
    • 命令
    • RHEL 6(nc-1.84)
      • 安装
      • 例子
    • RHEL 7(nmap-ncat-6.40)
      • 安装
      • 例子
  • 备注

使用bash和timeout

请注意, timeout应该在RHEL 6+中出现,或者可以在GNU coreutils 8.22中find。 在MacOS上,使用brew install coreutils进行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 

使用nc

请注意,在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 

例子:

成功:

 $ 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 $? 命令当然仅用于说明。

使用netcat你可以检查一个端口是否是这样打开的:

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

如果TCP端口被打开,则nc的返回值将是成功的,如果不能进行TCP连接则返回失败(通常返回码1)。

在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 

testing:

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

这里是一行(Bash语法):

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

请注意,有些服务器可以防止SYN洪水攻击的防火墙,所以您可能会遇到TCP连接超时(〜75secs)。 要解决超时问题,请尝试:

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

请参阅: 如何减lessTCP连接()系统调用超时?

虽然这是一个古老的问题,但我刚刚讨论过它的一个变种,但是这里没有一个解决scheme适用,所以我find了另一个解决scheme,并将其添加到后代。 是的,我知道OP说他们意识到这个select,并不适合他们,但是对于以后的任何人来说,这可能是有用的。

在我的情况下,我想testing一个docker构build的本地apt-cacher-ng服务的可用性。 这意味着在testing之前绝对不能安装任何东西。 没有ncnmapexpecttelnet或者python 。 然而, perl和核心库一样,所以我使用了这个:

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

如果您使用kshbash,则它们都使用/ dev / tcp / IP / PORT构造支持到/来自套接字的IOredirect。 在这个Korn shell的例子中,我正在从套接字redirectno-op的( )std-in:

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

如果套接字未打开,shell将打印一个错误:

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

因此,您可以在if条件下将其用作testing

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

无操作是在一个子shell中,所以如果std-inredirect失败,我可以把std-err扔掉。

我经常使用/ dev / tcp通过HTTP检查资源的可用性:

 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从套接字中读取。

我需要一个更灵活的解决scheme来处理多个git存储库,所以我根据1和2编写了以下sh代码。 您可以使用您的服务器地址而不是gitlab.com和您的端口replace22。

 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 

我猜猜答案已经太晚了,这可能不是一个好的答案,但是你在这里…

把它放在一个while循环里面,有一个定时器就可以了。 我更像是一个比Solaris更像Perl的人,但是根据你使用的shell,你应该能够做到这样的事情:

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

然后在while循环中添加一个标志,这样如果在完成之前超时,可以引用超时作为失败的原因。

我怀疑,telnet也有一个超时切换,但只是我的头顶,我认为上述将工作。

如果您想使用nc但是没有支持-z的版本,请尝试使用--send-only

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

并超时:

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

没有DNS查询,如果它是一个IP:

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

它返回的代码为-z基于它是否可以连接。

在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 

Netcat或Nmap呢?

我需要在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上正常工作。 它不使用netcat,因为linux / mac上版本之间的差异,这与默认的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 

使用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 

nmap-ncat来testing尚未使用的本地端口


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

答案与Expect有关。 我们写了一个简单的脚本,在我们需要的端口上发送一个telnet,超时时间为8秒。 还有很多例子可以select。

我们基于我们的这个职位: http : //www.unix.com/shell-programming-scripting/146568-expect-telnet-testing-tacacs-cisco.html