Linux上使用perl取得本机ip地址
2022-02-21 13:29:09 阿炯

本站赞助商链接,请多关照。 本文收集了一些在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写法