很简单,我想验证 Bash 脚本中的字符串是否代表有效的 IP 地址。我想像许多其他人一样,我也陷入了尝试使用正则表达式来做到这一点的陷阱;虽然这对于 IPv4 来说已经足够好了,但 IPv6 更复杂(因为它支持零压缩),而且它只是一个复杂且不是特别可读的解决方案。
然而,Unix 和 Linux 清楚地理解 IP 地址,所以我想知道是否有更好的方法可以在 bash 中验证 IP 地址?最好是可以用来执行此操作的相当通用的工具。
我想避免使用其他语言(例如 Python)来执行此操作,因为我实际上希望替换当前使用 PHPfilter_var
函数的解决方案。它有效,但与正则表达式一样,它引入了第二种语言,我希望避免使用一种语言。
最后,如果有一个解决方案还可以处理 IP 地址范围(例如0.0.0.0-255.255.255.255
或0.0.0.0/16
等),那将是理想的选择,但我可以自己处理这些。
答案1
使用 ip 命令怎么样?
ip route get <ip-address>
如果无效,则返回值为 1。
(2022/9/17) 为了回答@MaXi32的评论,我在没有互联网连接的情况下在我的Linux笔记本电脑上快速检查了这一点。当网络关闭时,ip 命令返回 1 表示无效 IP,返回 2 表示有效 IP。
对于有效的 IP:
$ ip route get 192.168.0.255 ; echo $?
RTNETLINK answers: Network is unreachable
2
$ ip route get fd00::192.168.0.255 ; echo $?
RTNETLINK answers: Network is unreachable
2
对于无效 IP:
$ ip route get 192.168.0.256 ; echo $?
Error: any valid prefix is expected rather than "192.168.0.256".
1
$ ip route get fd00::192.168.0.256 ; echo $?
Error: any valid prefix is expected rather than "fd00::192.168.0.256".
1
ip命令版本:
$ ip -V
ip utility, iproute2-5.9.0, libbpf 0.3.0
(2022/9/17#2) 可以使用inet_pton编写一个小程序。我认为这样更好。以下代码来自示例inet_pton 联机帮助页尝试将 IP 从文本转换为二进制,如果 IP 无效,则会引发错误:
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(int argc, char *argv[])
{
unsigned char buf[sizeof(struct in6_addr)];
int domain, s;
char str[INET6_ADDRSTRLEN];
if (argc != 3) {
fprintf(stderr, "Usage: %s {i4|i6|<num>} string\n", argv[0]);
exit(EXIT_FAILURE);
}
domain = (strcmp(argv[1], "i4") == 0) ? AF_INET :
(strcmp(argv[1], "i6") == 0) ? AF_INET6 : atoi(argv[1]);
s = inet_pton(domain, argv[2], buf);
if (s <= 0) {
if (s == 0)
fprintf(stderr, "Not in presentation format");
else
perror("inet_pton");
exit(EXIT_FAILURE);
}
if (inet_ntop(domain, buf, str, INET6_ADDRSTRLEN) == NULL) {
perror("inet_ntop");
exit(EXIT_FAILURE);
}
printf("%s\n", str);
exit(EXIT_SUCCESS);
}
答案2
bash
可能缺乏对相关inet_pton(3)
系统调用的直接访问,因此您可能需要调用一些可以执行的操作,例如sipcalc
(应该位于各种 UNIX 的端口或包树中)。更高级的语言可能也可以得到调用inet_pton(3)
,或等效的,但细节将是特定于语言的,例如我使用过NetAddr::IP
Perl 中与子网相关的代码。
答案3
根据 @ktaka 的回答,我不建议使用它ip route get
来验证 IP 地址,除非您的脚本严重依赖于网络连接。原因是,如果您的系统没有良好的互联网连接或完全断开与互联网的连接,那么当您使用 时ip route get
,您的脚本将始终返回错误代码(0 除外),即使 IP 定义正确。
解决方案:
我查找了 @Rolf Rander 给出的手动链接,我很困惑为什么ipcalc
Debian 11 上的二进制文件没有该-c
选项,而且我以前的ipcalc
二进制文件也不支持ipv6
.因此,我在 Google 上搜索了使用此关键字从手册页中获取的作者用户名dcantrell ipcalc
,并在此处获得了官方链接:
https://gitlab.com/ipcalc/ipcalc
所以上面是官方ipcalc
同时支持IPv4和IPv6的IP地址。要安装它,他们建议使用带介子的现代构建(但如果您不想使用介子,他们也提供旧的 make 构建)
所以,这就是我的设置ipcalc
方式Debian 11
:
删除现有的ipcalc(现有版本不好)
apt-get -y remove ipcalc
rm -rf "/usr/bin/ipcalc"
现在从官方仓库安装 ipcalc:
apt -y install meson
git clone https://gitlab.com/ipcalc/ipcalc.git
cd ipcalc
meson setup build --buildtype=release
ninja -C build
cp build/ipcalc /usr/bin
apt -y remove meson
ipcalc --version
1.0.1
现在您可以使用它ipcalc
来检查 IPv4 和 IPv6:
#!/bin/bash
IP="1.1.1.1"
if ipcalc -s -c $IP; then
echo "valid IPv4 or IPv6"
else
echo "Invalid IPv4 or Ipv6"
fi
是-s
忽略错误信息,-c
是检查而不输出
如果您想检查特定的 IPv4,请使用-4
或使用-6
检查 IPv6。例子:
#!/bin/bash
# IP is a valid IPv6
IP="::1"
# But we want to check only IPv4
if ipcalc -s -4 -c $IP; then
echo "valid IPv4"
else
echo "Invalid IPv4"
fi
输出:
Invalid IPv4
我很久以前就一直在使用sipcalc
and ,它们可以同时进行 IPv4 和 IPv6 检查,但我确实更喜欢它们,因为大多数输出都很简单,并且它具有用于其他目的的 json 输出。ipv6calc
ipcalc
编辑
(2022 年 10 月 23 日):
根据 @ktaka 的最新评论,我的主要观点是,在ip route get
没有互联网连接的情况下使用时,如下所示:
$ ip route get 192.168.0.255 ; echo $?
RTNETLINK answers: Network is unreachable
2
如果您的系统位于防火墙后面或者连接状况不佳,它不会返回有效的 IP。 IP是有效的,应该返回成功码0,而不是告诉网络不可达等不必要的信息。
答案4
这可以解决 ipv4 的问题
# check IPv4 syntax
if [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
echo 'Valid IP'
else
echo "Invalid IP: $ip" >&2
exit 1
fi
我正在将 ipv6 实现到我从中提取的脚本中,一旦完成,我将更新这篇文章。