ChatGPT解决这个技术问题 Extra ChatGPT

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

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

NetcatNmap 呢?
答案在于Expect。我们编写了一个简单的脚本,在我们需要的端口上发送一个 telnet,超时时间为 8 秒。也有很多例子可供选择。我们以这篇文章为基础:unix.com/shell-programming-scripting/…
github.com/monitoring-plugins/monitoring-plugins 中的 check_tcp 可以执行此操作,包括输入字符串和检查预期答案。

R
Raedwald

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

nc -z <host> <port>

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

对于快速交互式检查(超时 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/…
o
onlynone

使用 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 传播,然后是 {2 }。如果 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 bash change log 建议使用 bash-2.04-devel,但可能已在 bash-2.05-alpha1 中添加了对主机名的支持。
C
Community

目录:

使用 bash 和 timeout 命令示例

命令

例子

使用 nc 命令 RHEL 6 (nc-1.84) 安装示例 RHEL 7 (nmap-ncat-6.40) 安装示例

命令

RHEL 6 (nc-1.84) 安装示例

安装

例子

RHEL 7 (nmap-ncat-6.40) 安装示例

安装

例子

评论

使用 bash 和超时:

请注意,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

使用数控:

请注意,在 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 "</dev/tcp/canyouseeme.org/81"; echo $?很好!
2>&1 添加到不回显状态 result_test=$(nc -w 2 $ip_addreess 80 </dev/null 2>&1 ; echo $?)
B
Brandon Rhodes

使用 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 是必需的,否则在脚本中使用该命令可能会挂起超过十秒。我写了一个包含 -wanswer
+1 这很棒,因为 nc 是 alpine linux 和 ubuntu 的标准配置。可能是其他人。当您被远程访问并且无法安装时,无需额外安装。谢谢你!我可以补充一下,nc host port -w 2 && echo it works
我更喜欢nc -vz localhost 3306。它提供了更详细的输出。只在mac上试过。
C
Community

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


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

我需要一个更灵活的解决方案来处理多个 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 让我的生活变得轻松
A
Asclepius

如果您使用的是 ksh 或 bash,它们都支持使用 /dev/tcp/IP/PORT 构造来/从套接字进行 IO 重定向。在这个 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 条件下的测试:

SERVER=127.0.0.1 PORT=8000
if (: < /dev/tcp/$SERVER/$PORT) 2>/dev/null
then
    print succeeded
else
    print failed
fi

无操作位于子外壳中,因此如果 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从套接字读取。


@ABB我认为你的意思是如果端口没有响应,它会挂起很长时间,例如在过滤时,或者IP上没有任何东西。如果端口主动拒绝连接,则关闭的端口不会导致延迟。出于许多目的,这是完全可以接受的。
此技术已在 prior answer by kenorb 中的此答案之前发布。无论如何,更大的一点是,如果端口没有响应,它会挂起很长时间。例如,先尝试 </dev/tcp/canyouseeme.org/80,然后再尝试 </dev/tcp/canyouseeme.org/81
k
krbu

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

m
mc0e

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

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

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

P
PRF

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

C
Chris Mendez

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


J
João Rocha da Silva

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

K
Krystian Kulasza

我需要在 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 需要一些修改)
R
Rob Kielty

我猜现在回答为时已晚,这可能不是一个好答案,但你去...

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

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

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

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


B
Brad Parks

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

f
fluter

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

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

它并没有告诉我我的端口 80 是打开的。