【问题标题】:Test if remote TCP port is open from a shell script测试远程 TCP 端口是否从 shell 脚本打开
【发布时间】:2011-06-22 19:36:57
【问题描述】:

我正在寻找一种快速简单的方法来正确测试给定的 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 中捕获的内容(返回代码或标准输出中的字符串)。

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

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

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

【问题讨论】:

标签: shell tcp port solaris telnet


【解决方案1】:

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

nc -z <host> <port>

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

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

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

【讨论】:

  • centos7 默认使用nmap netcat 并且没有-z 选项。
  • 这在 RHEL/Centos 中不起作用。对于那些发行版,您需要: nc -vn
  • FWIW,我已经彻底改造了my answer with an example,分别适用于 RHEL 6 和 RHEL 7。
  • 至少在 Mac 上,您可能需要添加 -G# 以设置独立于/除了 -w# 超时之外的连接超时,它基本上用作读取超时。
  • @jolestar 您可以在 Centos 7 上手动升级 Ncat 以获得-z 选项。您可能要考虑:unix.stackexchange.com/questions/393762/…
【解决方案2】:

使用-z-w TIMEOUT 选项到nc 很容易,但并非所有系统都安装了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 传播然后@987654335 @。如果bash 在指定的超时之前发生连接失败,则bash 将退出,退出代码为 1,timeout 也将返回。如果 bash 无法建立连接并且指定的超时到期,那么 timeout 将杀死 bash 并以状态 124 退出。

【讨论】:

  • /dev/tcp 在 Linux 以外的系统上是否可用?尤其是 Mac 呢?
  • /dev/tcp 是 bash 的一个特性,所以是的。但是看起来macs没有超时......
  • @onlynone - 看起来 timeout 在 FreeBSD 中也不存在,在我的旧 Ubuntu 机器上它是一个可安装的包,但默认情况下不存在。如果有一种方法可以单独在 bash 中执行此操作,而无需诸如 timeout 或 netcat 之类的第三方工具,那就太好了。
  • 只想提一下timeout 似乎是 GNU coreutils 的一部分,并且可以使用自制软件安装在 Mac 上:brew install coreutils。然后它将以gtimeout 的形式提供。
  • @Wildcard A bash change log 建议使用bash-2.04-devel,尽管bash-2.05-alpha1 中可能已添加对主机名的支持。
【解决方案3】:

目录:

  • 使用 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 $? 命令当然仅用于说明。

【讨论】:

  • timeout 2 bash -c "&lt;/dev/tcp/canyouseeme.org/81"; echo $?是个好点!
  • 添加2&gt;&amp;1 不回显状态result_test=$(nc -w 2 $ip_addreess 80 &lt;/dev/null 2&gt;&amp;1 ; echo $?)
【解决方案4】:

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

【讨论】:

  • 非常适合不支持 -w 标志的 nc 风格。
  • 谢谢 - 也适用于不支持 -z 的 nc 版本 - 例如适用于 RHEL/CentOS 7
  • 虽然这种方法很好,但-w 是必要的,否则在脚本中使用的命令可能会挂起十多秒。我写了一个answer,其中包括-w
  • +1 这很棒,因为 nc 是 alpine linux 和 ubuntu 的标准配置。可能是其他人。当您被远程访问并且无法安装时,无需额外安装。谢谢你!我可以补充一下,nc host port -w 2 &amp;&amp; echo it works
  • 我更喜欢nc -vz localhost 3306。它提供了更详细的输出。仅在 mac 上尝试过。
【解决方案5】:

在 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 连接超时(约 75 秒)。要解决超时问题,请尝试:

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

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

【讨论】:

  • @A-B-B 这与服务器的防火墙配置有关,它没有给出任何响应以防止任何 SYN 洪水攻击。运行telnet canyouseeme.org 81 也会挂起。这由您的超时限制控制,这些限制可能在 bash 中硬编码。请参阅:decrease TCP connect() system call timeout
  • 对于参数化,现在似乎有必要用大括号指定$SERVER$PORT,至少为${SERVER}${PORT}
【解决方案6】:

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

【讨论】:

  • 谢谢! /etc/rc.local 让我的生活变得轻松
【解决方案7】:

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

    如果您使用的是 kshbash,它们都支持使用 /dev/tcp/IP/PORT强>构造。在这个 Korn shell 示例中,我从套接字重定向 no-op 的 (:) 标准输入:

    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$ 
    

    这一行打开 文件描述符 9 以读取和写入套接字,将 HTTP GET 打印到套接字并使用 cat 从套接字读取。

    【讨论】:

    • @A-B-B 我认为你的意思是如果端口没有响应,它会挂起很长时间,例如在过滤时,或者 IP 上没有任何内容。如果端口主动拒绝连接,则关闭的端口不会导致延迟。出于许多目的,这是完全可以接受的。
    • 此技术已在prior answer by kenorb 中发布此答案之前。无论如何,更大的一点是,如果端口没有响应,它会挂起很长时间。例如,尝试&lt;/dev/tcp/canyouseeme.org/80,然后尝试&lt;/dev/tcp/canyouseeme.org/81
    【解决方案9】:

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

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

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

    【讨论】:

      【解决方案10】:

      在某些情况下,如果 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
      

      【讨论】:

        【解决方案11】:

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

        【讨论】:

          【解决方案12】:

          我猜现在回答太晚了,这可能不是一个好答案,但你去吧......

          如果把它放在一个带有某种计时器的while循环中呢?我比 Solaris 更喜欢 Perl,但根据您使用的 shell,您应该能够执行以下操作:

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

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

          我怀疑 telnet 也有一个超时开关,但只是在我的脑海中,我认为上面的方法会起作用。

          【讨论】:

            【解决方案13】:

            我需要在 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 的 grepable 输出也可能有用 -oG - 将其发送到标准输出。 (grep 需要一些修改)
            【解决方案14】:

            在投票率最高的答案的基础上,这是一个等待两个端口打开的函数,也有一个超时。注意必须打开的两个端口,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】:

              这在幕后使用 telnet,并且似乎在 mac/linux 上运行良好。由于 linux/mac 上的版本不同,它不使用 netcat,这适用于默认的 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 
              

              【讨论】:

                【解决方案16】:

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


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

                【讨论】:

                • 它并没有告诉我我的 80 端口是打开的。
                猜你喜欢
                • 2020-02-07
                • 2014-12-04
                • 2023-04-04
                • 2013-05-12
                • 2021-04-18
                • 1970-01-01
                • 2011-01-14
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多