Perl获取主机名、用户、组、网络信息
获取主机名、用户、组、网络信息相关函数首先是获取主机名的方式,Perl提供了 use Sys::Hostname; print hostname,"n"; Perl中提供了下面一大堆的内置函数用来获取用户、组、网络相关的信息。这些perl函数在C中也都有对应的函数。 # 获取和设置用户和组 endgrent - be done using group file endpwent - be done using passwd file getgrent - get next group record getgrgid - get group record given group user ID getgrnam - get group record given group name getlogin - return who logged in at this tty getpwent - get next passwd record getpwnam - get passwd record given user login name getpwuid - get passwd record given user ID setgrent - prepare group file for use setpwent - prepare passwd file for use # 获取和设置网络信息 endhostent - be done using hosts file endnetent - be done using networks file endprotoent - be done using protocols file endservent - be done using services file gethostbyaddr - get host record given its address gethostbyname - get host record given name gethostent - get next hosts record getnetbyaddr - get network record given its address getnetbyname - get networks record given name getnetent - get next networks record getprotobyname - get protocol record given name getprotobynumber - get protocol record numeric protocol getprotoent - get next protocols record getservbyname - get services record given its name getservbyport - get services record given numeric port getservent - get next services record sethostent - prepare hosts file for use setnetent - prepare networks file for use setprotoent - prepare protocols file for use setservent - prepare services file for use 从动作上分为3类:
这里共有18个函数。包括以下几个文件(以Linux操作系统为例):
通过 这里6种结构体对象包含的字段如下: # 0 1 2 3 4 ( $name,$passwd,$gid,$members ) = getgr* ( $name,$aliases,$addrtype,$net ) = getnet* ( $name,$port,$proto ) = getserv* ( $name,$proto ) = getproto* ( $name,$length,@addrs ) = gethost* ( $name,$uid,$quota,$comment,$gcos,$dir,$shell,$expire ) = getpw* # 5 6 7 8 9 这种结构体对象并非只能从这些文件中获取,自己也可以构建或通过其它函数返回,相关内容在后面介绍中会提到。 除了上面的分类,用户、组还能继续分为:
获取网络信息的操作还有:
这些函数在标量上下文中返回单个name或id类数据,在列表上下文中返回各自的ent结构体对象。 虽然看着一大堆,分类后其实很容易记忆。但是,这些函数都不是很方便,因为有不少函数里的参数或返回值是需要或经过了二进制打包的。比如 正是因为这些函数使用起来并不方便,所以每种类型(passwd、group、host、network、service、protocol)都有对应的面向对象的模块,使用这些模块中的方法,可以免去转换的过程。它们对应的模块为:
由于很多函数在用法上是非常类似的,所以在后文介绍重复内容时仅将简单说明重复函数的用法。 获取用户和组信息相关的函数和模块有:
group信息首先解释getgrent以及grent结构对象,因为grent结构将group相关的东西全都串起来了。在后文也都按照这种方式介绍每一种分类。
# 0 1 2 3 ( $name,$members ) = getgr* 例如,/etc/group中的前三行为: $ head -n 3 /etc/group root:x:0:test1,test2,test3 daemon:x:1: bin:x:2: 使用 #!/usr/bin/perl use strict; use warnings; use 5.010; my $i; while(my ($name,$members) = getgrent) { $i++; last if $i == 4; say "group name: $name"; say "group passwd: $passwd"; say "group id: $gid"; say "group member: $members"; say "-" x 10; } 执行结果: group name: root group passwd: x group id: 0 group member: test1 test2 test3 ---------- group name: daemon group passwd: x group id: 1 group member: ---------- group name: bin group passwd: x group id: 2 group member: ---------- 前面说过, 例如,读取两个组后,绕回到开头再读两个组,在这过程中打开的文件并没有关闭。然后使用endgrent关闭已打开的文件句柄。最后再次读取两个组,这会重新打开文件,直到程序退出后文件被关闭。 #!/usr/bin/perl # use strict; use warnings; use 5.010; say join ',',getgrent; say join ',getgrent; setgrent; say join ',getgrent; say '-' x 30; system 'lsof -n | grep "group"'; endgrent; say '-' x 30; system 'lsof -n | grep "group"'; say '-' x 30; say join ',getgrent; 执行结果: root,x,test1 test2 test3 bin,1,root,------------------------------ systemd 1 root 6r DIR 0,21 0 1148 /sys/fs/cgroup/systemd perl 70014 root 3r REG 8,2 721 35750228 /etc/group ------------------------------ systemd 1 root 6r DIR 0,21 0 1148 /sys/fs/cgroup/systemd ------------------------------ root, 使用 #!/usr/bin/perl use strict; use warnings; use 5.010; my $gr_gid = getgrnam 'root'; say "gr_gid: $gr_gid"; say '=' x 10,' scalar context ','=' x 10; my $gr_name = getgrgid 0; say "gr_name: $gr_name"; say '=' x 10,' list context ','=' x 10; my ($name,$members) = getgrnam 'root'; say "name: $name"; say "passwd: $passwd"; say "gid: $gid"; say "members: $members"; 执行结果: gr_gid: 0 ========== scalar context ========== gr_name: root ========== list context ========== name: root passwd: x gid: 0 members: test1 test2 test3 user信息获取用户信息和组信息的方式是一样的,相关函数为 # 0 1 2 3 4 $name,$expire # 5 6 7 8 9 需要注意的是,不同操作系统中支持的字段不一样。 #!/usr/bin/perl use strict; use warnings; use 5.010; my $user_uid = getpwnam 'root'; say "user_uid: $user_uid"; say '=' x 10,'=' x 10; my $user_name = getpwuid 0; say "user_name: $user_name"; say '=' x 10,'=' x 10; say join ',getpwnam 'root'; 结果: user_uid: 0 ========== scalar context ========== user_name: root ========== list context ========== root,这里是root的密码,/root,/bin/bash 当前登录用户
#!/usr/bin/perl use strict; use warnings; use 5.010; say getlogin; root用户执行结果: $ perl login.pl root User::grent和User::pwent模块这两个模块提供了面向对象方式的操作。导入这两个模块,里面的函数默认会覆盖同名内置函数get{pw | gr}{ent | nam | gid},即 # grent OO my $gr = getgrnam 'root' or die "no root group"; # pwent OO my $pw = getpwuid 0 or die "no uid=0 user"; 通过这些对象,可以直接获取到给定字段的值,且在导入了 # grent OO 方法 等价变量 ------------------------ $gr->name $gr_name $gr->gid $gr_gid $gr->passwd $gr_passwd $gr->members $gr_members # pwent OO 方法 等价变量 ------------------------ $pw->name $pw_name $pw->passwd $pw_passwd $pw->uid $pw_uid $pw->gid $pw_gid $pw->quota $pw_quota $pw->comment $pw_comment $pw->gecos $pw_gecos $pw->dir $pw_dir $pw->shell $pw_shell 同时,还各自提供了getpw()和getgr()方法,这两个方法是getpwuid()、getpwnam()、getgrgid()、getgrnam()的多态方法,它根据参数类型判断调用哪个函数。例如给getpw()传递数值时,表示调用getpwuid(),传递字符串时表示调用getpwnam()。 例如: #!/usr/bin/perl use strict; use warnings; use 5.010; use User::pwent qw(:FIELDS); use User::grent qw(:FIELDS); my $gr = getgrnam 'root' or die 'no root group'; my $pw = getpwuid 0 or die 'no uid=0 user'; # grent OO say '=' x 10,' grent OO ','=' x 10; say $gr->name; say $gr_name; say $gr->gid; say $gr_gid; say '=' x 10,' pwent OO ','=' x 10; # pwent OO say $pw->name; say $pw->shell; say $pw_dir; 执行结果: ========== grent OO ========== root root 0 0 ========== pwent OO ========== root /bin/bash /root 地址解析:hosts信息经过前面grent和pwent的解释之后,再理解hosts、net、serv、proto就简单多了。 hosts相关函数和模块:
必要的基础知识在开始解释这些函数之前,先了解些必要基础知识。 在/etc/hosts文件中包含了本地的DNS解析记录,例如: # IP_address canonical_hostname [aliases...] 127.0.0.1 localhost localhost.localdomain ::1 localhost localhost.localdomain 192.168.100.12 www.longshuai.com www1.longshuai.com 其中第一列是IP地址,第二列是该IP地址对应的规范主机名(canonical_hostname),从第三列开始全都是主机别名(aliases),即DNS里的CNAME记录。 如果将上面的 www.longshuai.com. IN A 192.168.100.12 www1.longshuai.com. IN CNAME www.longshuai.com. 也就是说,当查询 例如,使用 $ host www.baidu.com www.baidu.com is an alias for www.a.shifen.com. www.a.shifen.com has address 183.232.231.172 www.a.shifen.com has address 183.232.231.174 结果说明了, hostent结构和前面介绍的grent、pwent一样,hostent也是一个结构体对象,该结构体对象包含如下字段: # 0 1 2 3 4 ( $name,@addrs ) 其中:
看下面的例子就知道了。 例如,下面使用gethostent()获取/etc/hosts中的每一个结构对象: #!/usr/bin/perl # use strict; use warnings; use 5.010; while(my ($name,@addrs) = gethostent){ say "name: $name"; say "aliases: $aliases"; say "addrtype: $addrtype"; say "length: $length"; say "addrs: @addrs"; say '-' x 10; } 结果: name: localhost aliases: localhost.localdomain localhost4 localhost4.localdomain4 addrtype: 2 length: 4 addrs: ---------- name: localhost aliases: localhost.localdomain localhost6 localhost6.localdomain6 addrtype: 2 length: 4 addrs: ---------- name: www.longshuai.com aliases: www1.longshuai.com addrtype: 2 length: 4 addrs: ---------- 可见,第四个字段是无法直接输出的,需要将其解包。可以使用unpack()或Socket模块中的inet_ntoa(): # 使用inet_ntoa()将二进制打包的地址转换成点分十进制的IP use Socket qw(/inet/); for (@addrs){ say "addrs: ",inet_ntoa $_; } # 使用unpack()解包 for (@addrs){ say "addrs: ",join '.',unpack 'C4',$_; } 再看看 #!/usr/bin/perl use strict; use warnings; use Socket qw(/inet/); use 5.010; # scalar context my $addr = gethostbyname $ARGV[0]; say "addr: ",inet_ntoa $addr; # list context my ($name,@addrs) = gethostbyname $ARGV[0]; say "name: $name"; say "aliases: $aliases"; say "addrtype: $addrtype"; say "length: $length"; for (@addrs){ say "addrs: ",inet_ntoa $_; } 执行: $ perl host.pl www.baidu.com addr: 183.232.231.174 name: www.baidu.com aliases: addrtype: 2 length: 4 addrs: 183.232.231.174 addrs: 183.232.231.172 $ perl host.pl www.perl.org addr: 151.101.42.217 name: dualstack.osff.map.fastly.net aliases: www.perl.org cdn-fastly.perl.org addrtype: 2 length: 4 addrs: 151.101.42.217 如果使用 #!/usr/bin/perl use strict; use warnings; use Socket qw(/inet/ AF_INET); use 5.010; # scalar context my $host = gethostbyaddr(inet_aton('192.168.100.21'),AF_INET); say "host: ",$host; # list context my ($name,@addrs) = gethostbyaddr(inet_aton('192.168.100.21'),AF_INET); say "name: $name"; say "aliases: $aliases"; say "addrtype: $addrtype"; say "length: $length"; for (@addrs){ say "addrs: ",inet_ntoa $_; } 一个简单的DNS A记录解析程序在使用gethostbyname和gethostbyaddr的时候,解析之后一定要判断返回结果是否存在,即 #!/usr/bin/perl use strict; use warnings; use Socket qw(/inet/); use 5.010; die "Give me a hostname" unless @ARGV; while(my $lookup = shift @ARGV){ my ($name,@addrs) = gethostbyname $lookup; # must check $name defined or not if($name){ foreach(@addrs){ $_ = inet_ntoa($_); } if ($name eq$lookup){ print "$lookup IP address: @addrsn"; } else { print "$lookup (real name $name) IP address: @addrsn"; } } else { print "host $lookup not foundn"; } } 执行: $ perl host.pl www.sina.com www.sina.com (real name wwwus.sina.com) IP address: 66.102.251.33 $ perl host.pl www.cnblogs.com www.baidu.com www.cnblogs.com IP address: 42.121.252.58 www.baidu.com IP address: 183.232.231.174 183.232.231.172 Net::hostent同 my $host = gethostbyname 'www.baidu.com'; my $host = gethostbyaddr '192.168.100.21'; 同样的,这个模块提供了hostent各字段对应的方法和变量: $host->name ==> $h_name $host->addr ==> $h_addr $host->aliases ==> @h_aliases $host->addrtype ==> $h_addrtype $host->length ==> $h_length $host->addr_list ==> @h_addr_list 此外,还提供了gethost()方法来替代gethostbyname()和gethostbyaddr(),它根据传递的参数类型来决定调用这两个方法中的哪一个。 网段解析:networks信息相关函数和模块:
必要基础这个用的不多,因为它解析/etc/networks文件,而这个文件是定义网段(注意是网段而不是具体的IP地址)和网段字符串名一一映射关系的。而且,它指定指定A、B、C类网段(注意是网段而不是具体的IP地址),所以这个文件中网段总是以 例如,基本上所有操作系统上都有这么两行: default 0.0.0.0 loopback 127.0.0.0 这表示default代表的是 用法跳过,基本用不上。 服务和协议:services和protocols/etc/services中记录了服务和端口/协议相关的信息。例如: # service-name port/protocol [aliases ...] [# comment] tcpmux 1/tcp # TCP port service multiplexer tcpmux 1/udp # TCP port service multiplexer rje 5/tcp # Remote Job Entry rje 5/udp # Remote Job Entry echo 7/tcp echo 7/udp discard 9/tcp sink null discard 9/udp sink null $ grep ' 22/' /etc/services ssh 22/tcp # The Secure Shell (SSH) Protocol ssh 22/udp # The Secure Shell (SSH) Protocol ssh 22/sctp # SSH $ grep ' 3306/' /etc/services mysql 3306/tcp # MySQL mysql 3306/udp # MySQL /etc/protocols文件中保存了tcp/ip协议栈各种协议的信息,包括协议名、代号、别名等等。例如ip协议的代号是0,别名是IP,tcp协议的代号是6,别名是TCP。这个文件是不能改的,一改就导致发送的TCP/IP相关的数据包出错。 这两个服务基本上不用管,除非要TCP/IP编程。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |