测试远程TCP端口是否从shell脚本打开

时间:2011-02-07 15:19:06

标签: shell tcp port solaris telnet

我正在寻找一种快速而简单的方法,用于在Shell脚本内部正确测试远程服务器上是否打开给定的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中捕获的东西(返回代码或stdout中的字符串)。 / p>

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

注意:这就是我的服务器正在运行的地方(我需要从中运行它)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

16 个答案:

答案 0 :(得分:421)

正如B. Rhodes指出的,nc将完成这项工作。一种更紧凑的使用方式:

nc -z <host> <port>

那样nc只检查端口是否打开,成功时退出0,失败时退出1。

进行快速互动检查(超时5秒):

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

答案 1 :(得分:129)

-z的{​​{1}}和-w TIMEOUT选项很容易,但并非所有系统都安装了nc。如果您有最新版本的bash,这将有效:

nc

这里发生的是# 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 将运行子命令并在它未在指定超时内退出时杀死它(在上例中为1秒)。在这种情况下,timeout是子命令,并使用其特殊的/dev/tcp handling来尝试打开与指定的服务器和端口的连接。如果bash可以在超时内打开连接,bash将立即关闭它(因为它正在从cat读取)并退出状态代码/dev/null,这将传播通过0然后bash。如果timeout在指定的超时之前导致连接失败,则bash将退出,退出代码为1 bash也将返回。如果bash无法建立连接且指定的超时时间到期,则timeout将终止timeout并退出状态为124。

答案 2 :(得分:96)

TOC:

  • 使用bash和timeout
    • 命令
    • 实施例
  • 使用nc
    • 命令
    • RHEL 6(nc-1.84)
      • 安装
      • 实施例
    • RHEL 7(nmap-ncat-6.40)
      • 安装
      • 实施例
  • 说明

使用bash和timeout

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

使用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,则上述失败命令将循环显示其中的多个或所有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,则上述失败命令将循环显示其中的多个或所有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 :(得分:49)

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

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

如果TCP端口被打开,nc的返回值将成功,如果无法建立TCP连接,则返回失败(通常是返回码1)。

尝试此操作时,某些版本的nc会挂起,因为即使在收到/dev/null的文件结尾后,它们也不会关闭其套接字的一半。在我自己的Ubuntu笔记本电脑(18.04)上,我安装的netcat-openbsd版本的netcat提供了一种解决方法:-N选项是获得即时结果所必需的:

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

答案 4 :(得分:25)

在Bash中使用pseudo-device files进行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 Flood攻击,因此您可能会遇到TCP连接超时(~75秒)。要解决超时问题,请尝试:

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

请参阅:How to decrease TCP connect() system call timeout?

答案 5 :(得分:10)

如果您使用 ksh bash ,他们都支持使用 / dev / tcp / IP / PORT 构造。在这个 Korn shell 示例中,我正在从套接字重定向no-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 条件中将其用作测试

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

no-op在子shell中,所以如果std-in重定向失败,我可以抛弃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 打印到套接字并使用 {{1} 从套接字读取。

答案 6 :(得分:10)

我需要一个更灵活的解决方案来处理多个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

答案 7 :(得分:9)

虽然一个老问题,我刚刚处理了它的一个变体,但这里没有一个解决方案是适用的,所以我找到了另一个,并为后代添加它。是的,我知道OP说他们知道这个选项而且它不适合他们,但是对于之后跟随它的人来说它可能是有用的。

在我的情况下,我想测试apt-cacher-ng版本中本地docker服务的可用性。这意味着在测试之前绝对不能安装任何东西。否ncnmapexpecttelnetpython。然而,perl和核心库一起存在,所以我使用了这个:

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

答案 8 :(得分: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

答案 9 :(得分:4)

如果您想使用def compute_av_precision(match_list): n = len(match_list) tp_counter = 0 cumulate_precision = 0 for i in range(0,n): if match_list[i] == True: tp_counter += 1 cumulate_precision += (float(tp_counter)/float(i+1)) if tp_counter != 0: av_precision = cumulate_precision/float(tp_counter) return av_precision return 0 但没有支持nc的版本,请尝试使用-z

--send-only

并且超时:

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

如果是IP,则不进行DNS查找:

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

根据是否可以连接,它将代码作为nc -n -w 1 --send-only <IP> <PORT> </dev/null 返回。

答案 10 :(得分:3)

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

答案 11 :(得分:1)

我猜这个答案已经太迟了,这可能不是一个好的,但是你去了......

把它放在一个带有定时器的while循环中怎么样?我不仅仅是Solaris的Perl人,但根据你使用的shell,你应该可以做类似的事情:

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

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

我怀疑telnet也有一个超时开关,但是我认为上面的内容是可行的。

答案 12 :(得分: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,因为它不是默认安装包。

答案 13 :(得分:0)

这在幕后使用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 

答案 14 :(得分:0)

在最高投票答案的基础上,这里有一个等待两个端口打开的功能,也有一个超时。注意两个打开的端口,8890和1111,以及max_attempts(每秒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
}

答案 15 :(得分:-1)

nmap-ncat,用于测试尚未使用的本地端口

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