Linux上使用perl取得本机ip地址
本文收集了一些在Linux下取得本机网络接口上的IP地址的方法,分别从其它指令、配置文件、模块分析、适用场景等方面进行了分析整理。1、直接分析shell指令的输出结果
$ip = `ifconfig eth0|grep -oE '([0-9]{1,3}\.?){4}'|head -n 1`;
注:这里输入是固定的,所以简单的 [0-9]{1,3} 了,如果是在 web 程序等地方验证 ip,需要更严谨!
或者
$ip = `ifconfig eth0|awk -F: '/inet addr/{split($2,a," ");print a[1];exit}'`;
好吧,这样显得太不 perl 了,而且频繁的调用外部 shell 不太好。
2、间接分析shell指令的输出结果
open FH,"ifconfig eth0|";
while(){
last unless /inet addr:((\d{1,3}\.?){4})/;
print $1;
}
看起来稍微 perl 了一些,虽然实质跟上面的shell调用是一样的。
3、分析网卡的配置文件
更 perl 一点,纯粹读文件:
open FH,'<','/etc/sysconfig/network-scripts/ifcfg-eth0';
while(){
next unless /IPADDR\s*=\s*(\S+)/;
print $1;
}
如果不一定 rpm 系,还要去读'/etc/issue'来确定操作系统信息,再决定网络配置文件到底是'/etc/sysconfig/network-script/ifcfg-eth0'还是'/etc/network/interfaces'还是其它,然后根据不同发行版写不同的处理方法……额,这是打算要干吗?
好吧,大家来充分体会 CPAN 的魅力,去检索一下,找到一把 Sys::HostIP、Sys::HostAddr 等模块。
4、Sys::HostAddr/Hostname
use Sys::HostAddr;
my $interface = Sys::HostAddr->new(ipv => '4', interface => 'eth0');
print $interface->main_ip;
use v5.16;
use Socket;
use Sys::Hostname;
my $addr = inet_ntoa((gethostbyname(hostname))[4]);
say "$addr";
不过看了一下相关的pm文件,这几个模块都是调用ifconfig命令,不过是根据操作系统的发行版的不同进行封装而已。适用的环境有限。
还有办法么?真还有。
5、Socket1
perl -MPOSIX -MSocket -e 'my $host = (uname)[1];print inet_ntoa(scalar gethostbyname($host))';
不过有人会说可能因为hostname的原因,导致获取的都是127.0.0.1……
那么还有一招。通过 strace ifconfig 命令可以看到,linux 实质是通过 ioctl 命令完成的网络接口 ip 获取。那么这里也用 ioctl 就是了。
6、Socket2
use Socket;
require 'sys/ioctl.ph';
sub get_ip_addr($){
my $socket;
my $pack = pack("a*", shift);
socket($socket, AF_INET, SOCK_DGRAM, 0);
ioctl($socket, SIOCGIFADDR(), $pack);
return inet_ntoa(substr($pack,20,4));
};
print get_ip_addr("eth0");
这样的好处就是只调用了核心模块,在分发脚本时,不用连带安装其它模块,非常值得推荐。
use v5.16;
use Socket;
require 'sys/ioctl.ph';
print get_interface_address('eth0');
sub get_interface_address{
my ($iface) = @_;
my $socket;
socket($socket, PF_INET, SOCK_STREAM, (getprotobyname('tcp'))[2]) || die "unable to create a socket: $!\n";
my $buf = pack('a256', $iface);
if(ioctl($socket, SIOCGIFADDR(), $buf) && (my @address = unpack('x20 C4', $buf))){
return join('.', @address);
}
return undef;
}
7、Socket3
借用GPS的原理,通过连接已知的IP和端口信息来以反射得知自己的IP。
下面是代码段的节选,通过连接一台redis服务来得到自己的IP地址,当然这里没有考虑该网口上的其它地址、其它网卡的情况。
if(defined(my $rdsock=IO::Socket::INET->new(PeerAddr=>$rdsip,PeerPort=>$rdsport,Proto=>'tcp',Timeout=>'3'))){
$mycip=$rdsock->sockhost;
$rdsock->close();
say "My Connection IP:$mycip.";
}else{
warn "Redis $rdsip:$rdsport connect faild.";
}
8、IO::Interface
IO::Interface充分借鉴了C语言中的相关方法,比较全面的分析了本机的网卡情况,非常值得使用。
use v5.16;
use Data::Dumper;
use IO::Interface::Simple;
my @interfaces = IO::Interface::Simple->interfaces;
for my $if (@interfaces){
print "interface = $if\n";
print "addr = ",$if->address,"\n",
"broadcast = ",$if->broadcast,"\n",
"netmask = ",$if->netmask,"\n",
"dstaddr = ",$if->dstaddr,"\n",
"hwaddr = ",$if->hwaddr,"\n",
"mtu = ",$if->mtu,"\n",
"metric = ",$if->metric,"\n",
"index = ",$if->index,"\n";
print "is running\n" if $if->is_running;
print "is broadcast\n" if $if->is_broadcast;
print "is p-to-p\n" if $if->is_pt2pt;
print "is loopback\n" if $if->is_loopback;
print "is promiscuous\n" if $if->is_promiscuous;
print "is multicast\n" if $if->is_multicast;
print "is notrailers\n" if $if->is_notrailers;
print "is noarp\n" if $if->is_noarp;
say '-' x 64;
}
给出每网口上的ip地址
perl -MIO::Interface::Simple '-Esay $_->address for grep { $_->is_running && defined $_->address } IO::Interface::Simple->interfaces'
从使用上来看,IO::Interface是目前来说比较好用的模块了,比较全面且级别较高。
本文参考
linux上获取本机ip的各种perl写法