首页 文章

如果远程TCP端口已打开,请从shell脚本进行测试

提问于
浏览
258

我正在寻找一种快速而简单的方法,用于在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中的字符串) .

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

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

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

16 回答

  • 119

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

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

    nc -z <host> <port>
    

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

    对于快速交互式检查(超时5秒):

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

    在某些情况下,像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
    
  • 402

    nc-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 handling来尝试打开与指定的服务器和端口的连接 . 如果 bash 可以在超时内打开连接, cat 将立即关闭它(因为它从 /dev/null 读取)并退出状态代码 0 ,它将通过 bash 然后 timeout 传播 . 如果 bash 在指定的超时之前发生连接失败,则 bash 将退出,退出代码为1, timeout 也将返回 . 如果bash无法 Build 连接并且指定的超时到期,则 timeout 将终止 bash 并以状态124退出 .

  • 9

    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,则上述失败命令将循环遍历其中的许多或全部 . 例如:

    $ 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 $? 命令当然仅供参考 .

  • 0

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

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

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

  • 4

    在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防火墙保护泛洪攻击,因此您可能会遇到TCP连接超时(~75secs) . 要解决超时问题,请尝试:

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

    见:How to decrease TCP connect() system call timeout?

  • 5

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

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

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

    如果您正在使用 kshbash ,则它们都支持使用 /dev/tcp/IP/PORT 构造进行套接字的IO重定向 . 在这个 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 条件中将其用作 test

    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$
    

    这个单行打开 file descriptor 9 用于读取和写入套接字,将 HTTP GET 打印到套接字并使用 cat 从套接字读取 .

  • 0

    如果您想使用 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 基于它是否可以连接 .

  • 47

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

    把它放在一个带有定时器的while循环里面怎么样呢 . 我不是Solaris的Perl人,但根据你使用的shell,你应该能够做到这样的事情:

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

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

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

  • 0

    我需要在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,因为它不是默认安装的软件包 .

  • 89

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

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

    在最高投票答案的基础上,这里有一个等待两个端口打开的功能,也有一个超时 . 注意两个打开的端口,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
    }
    
  • 0

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


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

相关问题