我正在寻找一种快速而简单的方法,用于正确地测试一个给定的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


当前回答

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


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

其他回答

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

在我的例子中,我想从docker构建中测试本地apt-cache -ng服务的可用性。这意味着在测试之前绝对不能安装任何东西。无nc、nmap、expect、telnet、python。perl然而是存在的,以及核心库,所以我使用这个:

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

我需要在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 

我的机器不支持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