我正在寻找一种快速简单的方法来正确测试给定的 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
正如 B. Rhodes 所指出的,nc
(netcat
) 将完成这项工作。更紧凑的使用方式:
nc -z <host> <port>
这样 nc
只会检查端口是否打开,成功时退出 0,失败时退出 1。
对于快速交互式检查(超时 5 秒):
nc -z -v -w5 <host> <port>
使用 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 呢?
timeout
在 FreeBSD 中也不存在,在我的旧 Ubuntu 机器上它是一个可安装的包,但默认情况下不存在。如果有一种方法可以单独在 bash 中执行此操作,而无需第三方工具(如 timeout 或 netcat),那就太好了。
timeout
似乎是 GNU coreutils 的一部分,并且可以使用自制软件安装在 Mac 上:brew install coreutils
。然后它将作为 gtimeout
提供。
目录:
使用 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 $?)
使用 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
nc host port -w 2 && echo it works
nc -vz localhost 3306
。它提供了更详细的输出。只在mac上试过。
在 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?
telnet canyouseeme.org 81
也会挂起。这由您的超时限制控制,这些限制可能在 bash 中硬编码。请参阅:decrease TCP connect() system call timeout。
$SERVER
和 $PORT
,至少是 ${SERVER}
和 ${PORT}
。
如果您使用的是 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
从套接字读取。
</dev/tcp/canyouseeme.org/80
,然后再尝试 </dev/tcp/canyouseeme.org/81
。
使用 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
虽然是一个老问题,但我刚刚处理了它的一个变体,但这里没有一个解决方案适用,所以我找到了另一个,并为后代添加它。是的,我知道 OP 说他们知道这个选项并且它不适合他们,但是对于之后跟随的任何人来说,它可能会证明是有用的。
就我而言,我想测试来自 docker
构建的本地 apt-cacher-ng
服务的可用性。这意味着在测试之前绝对不能安装任何东西。没有 nc
、nmap
、expect
、telnet
或 python
。但是 perl
与核心库一起存在,所以我使用了这个:
perl -MIO::Socket::INET -e 'exit(! defined( IO::Socket::INET->new("172.17.42.1:3142")))'
在某些情况下,如果 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
如果您想使用 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
返回。
在投票率最高的答案的基础上,这是一个等待两个端口打开的函数,也有超时。注意必须打开的两个端口,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
}
我需要在 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 需要一些修改)
我猜现在回答为时已晚,这可能不是一个好答案,但你去...
将它放在带有某种计时器的while循环中怎么样?我比 Solaris 更喜欢 Perl,但根据您使用的 shell,您应该能够执行以下操作:
TIME = 'date +%s' + 15
while TIME != `date +%s'
do whatever
然后只需在 while 循环中添加一个标志,这样如果它在完成之前超时,您可以将超时作为失败的原因。
我怀疑 telnet 也有一个超时开关,但就在我的脑海中,我认为上面的方法会起作用。
这在幕后使用 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
nmap-ncat 测试尚未使用的本地端口
availabletobindon() {
port="$1"
nc -w 2 -i 1 localhost "$port" 2>&1 | grep -v -q 'Idle timeout expired'
return "$?"
}
-G#
以设置与-w#
超时分开的连接超时,该超时基本上用作读取超时。-z
选项。您可能需要考虑:unix.stackexchange.com/questions/393762/…