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

374

我正在寻找一种快速简单的方法来在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

我需要一种更好的方法,或者一种能够强制telnet在8秒内连接并返回可以在Shell中捕获的东西(返回码或stdout字符串)的方法。

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

注意:这是我的服务器正在运行的内容(我需要从该服务器运行此内容)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc


1
那么 Netcat 或者 Nmap 呢? - Jeremiah Willcock
答案在Expect中。我们编写了一个简单的脚本,发送了一个telnet到我们需要的端口,并设置了8秒的超时时间。还有很多例子可供选择。我们的脚本是基于这篇文章的:http://www.unix.com/shell-programming-scripting/146568-expect-telnet-testing-tacacs-cisco.html - Yanick Girouard
1
check_tcp可以从https://github.com/monitoring-plugins/monitoring-plugins进行下载,它可以执行此操作,包括输入字符串并检查预期的答案。 - user3323848
18个回答

5

如果您想使用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


3

在最高得票的答案的基础上,这里有一个函数可以等待两个端口打开,并设置超时时间。请注意,必须打开的两个端口是8890和1111,以及最大尝试次数(每秒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
}

没有成功/失败的返回代码吗? - JDOaktown
没有成功/失败的返回代码吗? - undefined

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,因为它不是默认安装的软件包。


nmap的可过滤输出也可能很有用,可以使用-oG -将其发送到标准输出(stdout)。(grep需要进行一些修改) - Gert van den Berg

0
我猜回答已经太晚了,而且这可能不是一个好的答案,但是这里有一个...

把它放在一个带有定时器的 while 循环内部怎么样?我更喜欢 Perl 而不是 Solaris,但根据你使用的 shell,你应该能够做到类似于:

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

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

我怀疑 telnet 也有一个超时开关,但是仅凭我的经验,我认为上述方法会起作用。


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 

0

我的机器不支持nc/dev/tcp/$hostname/$port,但支持timeout,所以我改用以下的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

0
#!/bin/sh
#
#------------------------------------+
# checkout target server port status |
# with DOH service.                  |
#------------------------------------+
#


A_LIST=$(curl -Ssl --http2 -H "accept: application/dns-json" "https://1.1.1.1/dns-query?name=example.com&type=A" |jq -r '.Answer[]|.data'|tr '\n\t' '|'|sed 's/|*\r*$//')

echo $A_LIST

for SERVER in $(echo $A_LIST | sed "s/|/ /g");do
    echo "$SERVER"
    timeout 3 bash -c "</dev/tcp/$SERVER/80" && echo -e open|| echo -e failed
done

-1

使用nmap-ncat测试未被占用的本地端口


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

它没有告诉我我的80端口是开放的。 - totoro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接