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


297

我正在寻找一种快速,简单的方法来从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

我或者需要一种更好的方法,或者是一种方法,例如如果telnet在不到8秒内没有连接,则强制telnet超时,然后返回我可以在Shell中捕获的内容(返回代码或stdout中的字符串)。

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

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

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc



答案与Expect撒谎。我们编写了一个简单的脚本,该脚本在所需的端口上发送telnet,超时时间为8秒。也有很多例子可供选择。我们以此为基础:unix.com/shell-programming-scripting/…–
Yanick Girouard,

1
github.com/monitoring-plugins/monitoring-plugins中的check_tcp可以做到这一点,包括输入字符串并检查期望的答案。

Answers:


452

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

nc -z <host> <port>

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

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

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

48
centos7默认使用nmap netcat,并且没有-z选项。
jolestar

7
这在RHEL / Centos中不起作用。对于这些发行版,您需要:nc -vn <主机> <端口>
贾斯汀·戴纳豪威尔

2
FWIW,我已经彻底检查我的答案有一个例子,分别适用于两种RHEL 6和RHEL 7
阿卡门斯

4
至少在Mac上,您可能需要添加-G#设置超时时间之外的连接超时-w#时间,超时时间基本上可以用作读取超时。
Doktor J

1
@jolestar您可以在Centos 7上手动升级Ncat以获得该-z选项。你可能要考虑:unix.stackexchange.com/questions/393762/...
达明ÓCeallaigh

149

使用-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处理来尝试打开与指定服务器和端口的连接。如果bash可以在超时时间内打开连接,cat则将其立即关闭(因为它正在从中读取/dev/null),然后退出,并且状态代码0将通过bash然后再传播timeout。如果bash在指定的超时之前连接失败,bash则会以退出代码1退出timeout并返回。如果bash无法建立连接并且指定的超时时间到期,则timeout将杀死bash并退出,状态为124。


1
/dev/tcp可在Linux以外的系统?Mac呢?
彼得

8
/ dev / tcp是bash的功能,所以可以。但是,看起来
Mac电脑

1
@onlynone- timeoutFreeBSD中似乎也不存在,在我的旧Ubuntu盒子上,它是一个可安装的软件包,但默认情况下不存在。如果有一种方法可以单独使用bash进行操作,而无需使用诸如timeout或netcat之类的第三方工具,那就太好了。
格雷厄姆

3
只是想提一下,它timeout似乎是GNU coreutils的一部分,可以通过homebrew:安装在macs上brew install coreutils。然后它将以形式提供gtimeout
onlynone

1
@Wildcard一个bash 更改日志表明bash-2.04-devel,虽然主机名的支持可能已被添加bash-2.05-alpha1
Acumenus '16

109

目录:

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

请注意,ncRHEL 7上安装了向后不兼容的版本。

命令:

请注意,以下命令是唯一的,因为它对于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,则上述失败命令将循环遍历许多或所有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,则上述失败命令将循环遍历许多或所有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 $?命令当然仅用于说明。


3
timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?很棒!
ipeacocks

添加2>&1为不回声状态 result_test=$(nc -w 2 $ip_addreess 80 </dev/null 2>&1 ; echo $?)
Sanya Snex

55

使用以下命令netcat可以检查端口是否打开:

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

nc如果打开了TCP端口,则返回值为成功,如果不能建立TCP连接,则返回值为失败(通常为返回码1)。

nc尝试使用时,某些版本的会挂起,因为即使从接收到文件结束后,它们也不会关闭套接字发送部分/dev/null。在我自己的Ubuntu笔记本电脑(18.04)上,netcat-openbsd我安装的netcat版本提供了一种解决方法:-N必须使用该选项才能立即获得结果:

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

1
对于nc不支持该-w标志的口味非常有用 。
David Dossot

5
谢谢-也适用于不-z支持nc的版本-例如在RHEL / CentOS 7上有效
Andrew

1
尽管这种方法是好的,但这-w是必需的,否则脚本中使用的命令可能会挂起十秒钟以上。我写了一个答案,其中包括-w
Acumenus

2
+1很棒,因为nc是alpine linux和ubuntu的标准配置。可能是其他人。当您无法进入远程环境时,无需安装其他设备。谢谢你!我可能会加上nc host port -w 2 && echo it works
std''OrgnlDave

2
我更喜欢nc -vz localhost 3306。它给出了更详细的输出。仅在Mac上试用过。
EFreak '17

29

在Bash中,使用伪设备文件进行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.

请参阅:如何减少TCP connect()系统调用超时?


1
@ABB这与服务器的防火墙配置有关,该配置没有给出任何响应来阻止任何SYN Flood攻击。运行telnet canyouseeme.org 81也挂起。这由您的超时限制控制,该限制可能以bash硬编码。请参阅:减少TCP connect()系统调用超时
kenorb

对于参数化,现在似乎有必要使用大括号指定$SERVER$PORT,至少使用as ${SERVER}${PORT}
Acumenus

13

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

1
谢谢!/etc/rc.local使我的生活变得轻松
Shahin Khaled

10

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

如果套接字未打开,则外壳会显示错误:

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

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


1
@ABB我认为您的意思是,如果端口没有响应(例如经过过滤)或IP上没有任何内容,则它将挂起很长时间。如果端口主动拒绝连接,则关闭的端口不会引起延迟。对于许多目的,这是完全可以接受的。
mc0e

该技术已经在kenorb先前答复中发布于此答复之前。无论如何,更大的一点是,如果端口没有响应,它将挂起很长时间。例如,尝试</dev/tcp/canyouseeme.org/80然后</dev/tcp/canyouseeme.org/81
Acumenus

9

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

就我而言,我想apt-cacher-ngdocker构建中测试本地服务的可用性。这意味着在测试之前绝对不能安装任何东西。不ncnmapexpecttelnetpythonperl但是与核心库一起存在,因此我使用了以下方法:

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

6

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

6

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

4

如果要使用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根据是否可以连接返回代码。


1

我猜想答案已经太迟了,这可能不是一个好答案,但是您来了...

将其放在带有某种计时器的while循环内怎么样?我比Solaris更像Perl,但是根据您使用的Shell,您应该可以执行以下操作:

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

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

我怀疑该telnet也有一个超时开关,但就在我脑海中,我认为以上方法都可以使用。


1

我需要在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 -将其发送到stdout 可能也很有用。(grep需要进行一些修改)
Gert van den Berg

0

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

0

在获得最高投票的答案的基础上,这是一个等待两个端口打开且具有超时的功能。请注意必须打开的两个端口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
}

-1

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


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

它没有告诉我我的端口80是开放的。
龙猫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.