原文:http://stackoverflow.com/questions/4922943/test-from-shell-script-if-remote-tcp-port-is-open


法一:使用nc

nc -z <host> <port>

使用nc -z +IP或域名+端口检查目标主机端口是否开启,返回0则表示开启,返回1则表示未开启。

当然,为了更快的检测目标端口的状态,可以使用-w参数指定超时时间。(下方示例设置的超时时间为5秒)

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

$ nc -v -z -w 5 stackoverflow.com 80; echo $?
Connection to stackoverflow.com 80 port [tcp/http] succeeded!
0

$ nc -v -z -w 5 stackoverflow.com 81; echo $?
nc: connect to stackoverflow.com port 81 (tcp) timed out: Operation now in progress
1

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

法二:使用bash

以下需要使用到timeout命令,对于CentOS5.x,安装方法见下:
wget ftp://ftp.pbone.net/mirror/ftp5.gwdg.de/pub/opensuse/repositories/home:/crt0solutions:/extras/CentOS_CentOS-5/x86_64/timeout-8.4-20.3.crt0.x86_64.rpm
#curl ftp://ftp.pbone.net/mirror/ftp5.gwdg.de/pub/opensuse/repositories/home:/crt0solutions:/extras/CentOS_CentOS-5/x86_64/timeout-8.4-20.3.crt0.x86_64.rpm -vO
rpm -ivh timeout-8.4-20.3.crt0.x86_64.rpm
 
# 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会运行其后的子命令,若在指定的超时时间还未完成的话,timeout会kill掉子命令对应的进程。在这种情况下,bash就是一个子命令,用"/dev/tcp"的特殊方式尝试与目标主机的指定端口建连。

  1. 如果bash可以指定的超时时间内与目标主机端口建连,cat会立即终止(由于其是从/dev/null中读取内容),bash命令接着也会执行完毕,最终返回一个0状态码。

  2. 若在超时时间范围内与目标主机端口建连失败,bash会退出并与timeout一起返回一个1的状态码

  3. 若过了超时时间后bash还是无法成功建连,timeout就会kill掉bash并返回一个124的状态码


法三:使用pseudo-device file(</dev/tcp/$SERVER/$PORT)

#!/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

融合为一行(判断本机11211端口是否存活)

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

法四:使用perl(未测试,暂时无法保证可用性)

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

法五:使用nmap

前提是保证你的机器安装有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

法六:使用telnet

其在mac与linux系统中都可以较好地工作。

使用方法:

$ is_port_open.sh 80 google.com
OPEN

$ is_port_open.sh 8080 google.com
CLOSED

脚本内容:

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

使用timeout重新改写脚本(安装方法见法二,用法同上)

#!/bin/bash

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
}

function eztimeout() { perl -e 'alarm shift; exec @ARGV' "$@"; }

function testPort()
{

  # 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=$(timeout $TIMEOUT_IN_SEC telnet $HOST $PORT &> $FILENAME; cat $FILENAME | tail -n1|awk '{print $1$2}')
  rm -f $FILENAME;
  SUCCESS=$(eztern "$RESULT" "Escapecharacter" "$VALUE_IF_OPEN" "$VALUE_IF_CLOSED")

  echo "$SUCCESS"
}

testPort


法七:使用wget(译者注:经测试该方法并不可行)

当诸如curl、telnet、nv、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