在Linux上高效地测试端口是否打开?

208

如何从bash脚本中快速找出服务器上端口445是否开放/监听。

我尝试了几种方法,但都想要一些更快的方法:
1. lsof -i :445 (几秒钟内返回)
2. netstat -an |grep 445 |grep LISTEN (几秒钟内返回)
3. telnet (没有返回结果)
4. nmapnetcat在服务器上不可用

最好知道一种不需要枚举和筛选的方法。


1
Netcat 可用吗?如果我没记错的话,它有一个快速失败路径。http://netcat.sourceforge.net/ - JimR
5
使用 netstat -lnt 命令(带有 -t 选项且不带 -a)将只显示正在监听的 TCP 连接。这可能会加快一些速度。如果您不需要 IPv6,可以添加 -4 来仅显示 IPv4。 - Bartosz Moczulski
15
如果输出为空,则表示端口未被使用。原始命令为:netstat -an | grep PORTNUMBER | grep -i listen - automatix
我不知道为什么lsof对你来说很慢,但通常它是你列出的解决方案中最好的。你的netstat解决方案并不是非常可靠(每当你使用grep时都可以猜测;无论如何,如果有人在4450上监听,它都会返回true)。telnetnetcat实际上尝试创建一个连接,这可能并不总是你想要的。 - petersohn
尝试使用在线工具以最快的方式转移详细信息- http://www.yougetsignal.com/tools/open-ports/ - Rohit Suthar
显示剩余2条评论
14个回答

169
最近我发现一个惊喜,那就是Bash原生支持将TCP连接作为文件描述符。使用方法如下:

exec 6<>/dev/tcp/ip.addr.of.server/445
echo -e "GET / HTTP/1.0\n" >&6
cat <&6

我将使用6作为文件描述符,因为0、1、2是标准输入、标准输出和错误输出。5有时会被Bash for child processes使用,所以3、4、6、7、8和9应该是安全的。
根据下面的注释,在脚本中测试在本地服务器上是否正在监听:
exec 6<>/dev/tcp/127.0.0.1/445 || echo "No one is listening!"
exec 6>&- # close output connection
exec 6<&- # close input connection

尝试通过回送连接来确定是否有人在听,如果失败,则端口关闭或我们无权访问。之后,关闭连接。
根据您的用例进行修改,例如发送电子邮件,在失败时退出脚本,或启动所需服务。

如果端口被占用,它就不会返回任何东西,有没有办法让它显示“端口已被 <进程 ID> 占用”或者其他类似的信息? - niken
@Nik 如果你在第一行加上 &&,那么它可以打印出端口是活动的,但由于其本质,该命令不知道拥有该端口。 - Spencer Rathbun
@Spencer 我猜你可以运行 lsof -i 或者类似的命令...谢谢! - niken
基于此,我添加了一个简单的一行代码(https://dev59.com/CWkw5IYBdhLWcg3w2-Ik#43364744),用于基于bash的操作。 - user1338062
2
这不是一种可靠的方法,因为并非所有操作系统(例如我今天发现的Ubuntu 16)都附带编译用于构建/dev/tcp/IP/PORT树的bash。 - dyasny
显示剩余8条评论

133

这里有一个非常简短的“快速回答”:如何从Shell脚本测试远程TCP端口是否打开?

nc -z <host> <port>; echo $?

我使用127.0.0.1作为“远程”地址。

如果端口开放返回“0”,关闭则返回“1”。

例如:

nc -z 127.0.0.1 80; echo $?

-z指定NC应该只扫描正在监听的守护程序,而不向其发送任何数据。 在使用-l选项时使用此选项是错误的。


2
那似乎是最简单的方法,谢谢。虽然示例脚本链接不再有效,但它仍然相当易于理解。 - derFunk
不错!在一个打开了很多端口的服务器上,这比其他答案要快得多。对于我来说,返回时间不到<0.01秒,而netstat / lsof需要1秒以上。 - Tim
2
最近的发行版(如Fedora、Centos等)都使用基于nmap的ncat,其中-z标志不可用。(nmap-ncat-6.01-9.fc18.x86_64) - Zack
10
有些出人意料的是,如果端口开放,这会返回“0”,如果端口关闭,将返回“1”。 - Sean
3
通常情况下,Unix 命令返回“0”表示成功,非零则表示失败。因此,“0”表示成功连接,非零表示由于某些原因未能连接。但请注意,有些版本的“nc”命令不支持“-z”参数,因此 https://dev59.com/CWkw5IYBdhLWcg3w2-Ik#25793128 可能是更好的解决方案。 - Rich Sedman
显示剩余3条评论

90

为了获得更快的结果,你可以像这样使用netstat:

在Linux上:

netstat -lnt | awk '$6 == "LISTEN" && $4 ~ /\.445$/'

在 Mac 上:

netstat -anp tcp | awk '$6 == "LISTEN" && $4 ~ /\.445$/'

这将输出在指定端口(此处为445)上正在监听的进程列表,如果端口空闲则不会输出任何内容。


1
您的 netstat 语法不正确。 netstat -ln --tcp 可以工作,但仍然很慢。 - Aman Jain
6
实际上,这是正确的语法,但可能你正在使用Linux系统,而我在使用Mac系统。如果你在使用Linux系统,请使用以下命令:netstat -lnt | awk '$6 == "LISTEN" && $4 ~ ".445"' - anubhava
1
问题是关于Linux的,因此也许评论应该放在答案中。 - UpTheCreek
1
为了检查端口80,我需要使用awk '$6 == "LISTEN" && $4 ~ "80$"'。我使用了80$来检查端口号前的点而不是用\ .80。否则,这也会匹配包含.80的IP地址和以80开头的端口,如8000 - Patrick Oscity
1
@PatrickOscity:说得好,但为了使其更加健壮,您需要_结合_这两种方法:awk '$6 == "LISTEN" && $4 ~ "\.80$"' - mklement0
显示剩余4条评论

40
你可以使用netcat来完成这个任务。
nc ip port < /dev/null

连接服务器并直接关闭连接。如果netcat无法连接,则返回非零的退出代码,该退出代码存储在变量$?中。例如:

nc ip port < /dev/null; echo $?

只有当netcat成功连接到端口时,才会返回0。


2
这个答案需要更多的赞。nc 对于这种情况完美地工作。/dev/tcp 技巧很巧妙,但似乎难以在脚本中实现信号中断。 - Avindra Goolcharan
6
nc 命令有 -z 标志,可用于此目的,它不需要从 /dev/null 获取输入。上面已经有一个使用 -z 标志的答案。 - Abe Voelker
2
@AbeVoelker 并非所有版本的nc都支持-z标志。我使用的是CentOS 7,发现Tony的解决方案正是我所需要的。 - Shadoninja
@Shadoninja 很高兴知道这个!如果我能够编辑掉我在2014年评论中的轻率之处就好了。 - Abe Voelker
'nc'不再支持'-z',因此这个答案似乎是最佳解决方案。 - Rich Sedman
nc -z 在 Debian 11 中仍然可以正常工作。 - JonathanDavidArndt

29

根据 Spencer Rathbun 的回答,使用 bash:

true &>/dev/null </dev/tcp/127.0.0.1/$PORT && echo open || echo closed

3
针对在新连接后不发送数据的服务,最佳解决方案。比使用 netcat 命令快约 20 倍。可以缩短为 : &>/dev/null </dev/tcp/127.0.0.1/$PORT - ens
我会添加超时来更快地获取“closed”结果: timeout 5s bash -c ': &>/dev/null </dev/tcp/127.0.0.1/${PORT}' && echo open || echo closed - emi
@emi,你需要在哪个平台上设置超时?如果端口关闭,这对我来说会立即返回。 - user1338062
@user1338062,这是在 Azure 上的 Ubuntu Bionic 系统中针对外部 IP(不是localhost127.0.0.1)发生的。 - emi
@emi 啊,我甚至没有想到有人会使用这个来测试远程端口。在内部,bash 调用 connect,所以如果它被阻塞(例如防火墙),那么你确实需要一个超时。 - user1338062
显示剩余2条评论

17

它们在/proc/net/tcp中列出。

这是十六进制的“:”后面的第二列:

> cat /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
   0: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 10863 1 ffff88020c785400 99 0 0 10 -1                     
   1: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 7983 1 ffff88020eb7b3c0 99 0 0 10 -1                      
   2: 0500010A:948F 0900010A:2328 01 00000000:00000000 02:00000576 00000000  1000        0 10562454 2 ffff88010040f7c0 22 3 30 5 3                   
   3: 0500010A:E077 5F2F7D4A:0050 01 00000000:00000000 02:00000176 00000000  1000        0 10701021 2 ffff880100474080 41 3 22 10 -1                 
   4: 0500010A:8773 16EC97D1:0050 01 00000000:00000000 02:00000BDC 00000000  1000        0 10700849 2 ffff880104335440 57 3 18 10 -1                 
   5: 0500010A:8772 16EC97D1:0050 01 00000000:00000000 02:00000BF5 00000000  1000        0 10698952 2 ffff88010040e440 46 3 0 10 -1                  
   6: 0500010A:DD2C 0900010A:0016 01 00000000:00000000 02:0006E764 00000000  1000        0 9562907 2 ffff880104334740 22 3 30 5 4                    
   7: 0500010A:AAA4 6A717D4A:0050 08 00000000:00000001 02:00000929 00000000  1000        0 10696677 2 ffff880106cc77c0 45 3 0 10 -1  

所以我猜第三列中那些:50之一必须是stackoverflow :o)

查看man 5 proc以获取更多详细信息。将其与sed等分开是留给温柔读者的练习...


10
ss -tl4 '( sport = :22 )'

2ms够快吗?

添加冒号后,此命令可在Linux上运行。


2
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Campa
很遗憾,ss命令没有返回反映其查找结果的退出代码;它总是返回0退出代码。 - John Greene
| grep LISTEN - leucos
我得到了 State Recv-Q Send-Q Local Address:Port Peer Address:Port,现在呢?这是什么意思? - Black

7
nc -l 8000

8000是端口号。如果端口空闲,它将启动一个服务器,你可以轻松关闭它。如果不空闲,则会抛出错误:

nc: Address already in use

6
这是一个适用于Mac和Linux的解决方案:
netstat -aln | awk '$6 == "LISTEN" && $4 ~ "[\\.\:]445$"'

我认为你可以安全地删除 [\\.\:] - Patrick Oscity

5

我想确认我们的一个Linux测试服务器上的端口是否开放。我可以通过尝试从我的开发机连接到测试服务器来实现这一点。在您的开发机上尝试运行以下命令:

$ telnet test2.host.com 8080
Trying 05.066.137.184...
Connected to test2.host.com

在这个例子中,我想检查主机test2.host.com的端口8080是否开放。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接