即使忽略文件的网络数据库的可能性,您也确实不能grep
用于这项工作,至少,如果您要真正彻底地做到这一点,则不能。您可能应该使用 Perl 或 Python 或您选择的其他类似脚本语言。
请注意,文件中有一个由数字指定的用户组/etc/passwd
。/etc/group
对于同一用户,可以列出任意数量的其他组。假设用户 'primary GID' 是 23 in /etc/passwd
。不能保证组 23 的条目/etc/group
会列出该用户。
请注意,您可以在组文件中出现各种奇怪的东西。
- 同一个 GID 的多个名称。
- 具有不同 GID 值的相同组名的多行。
- 中列出的 GID 值
/etc/passwd
未在 中列出/etc/group
。
- 文件中具有相同 UID 的多个名称
/etc/passwd
(通常允许不同的人使用自己的凭据(名称和密码)登录,但都以 UID 0 运行,即 aka root
)。
处理所有这些是一场噩梦——是的,几乎所有这些都发生在我工作但不管理的系统上。
在某个地方,我有代码可以帮助进行这种分析;它是 Perl,使用getpwent()
和getgrent()
检索信息(来自管理员配置的文件或网络)。它不漂亮。
对于课堂练习分析,您可能需要:
来自/etc/passwd
:
- 用户名
- 用户 ID (UID)
- 主要组 ID (GID)
来自/etc/group
:
- 主 GID 的名称。
- 列出用户名的所有其他组条目。
如果您只是简单地躲避并使用id
命令为您完成大部分工作,那就简单多了。通过/etc/passwd
withgrep
获取用户名,然后用于id
指定用户所属的组。请参阅gvalkov的答案,以获得可能足以满足健全系统的答案。
grep -o '^[^:]*' /etc/passwd |
xargs -L1 id
这给了你名字和数字;调整选项以id
满足您的要求。这是使用 GNU grep
;该-o
选项很方便,但不便携。幸运的是,Unix 用户名不包含换行符(或冒号);这简化了xargs
.
用户组图
243 行或 Perl,其中一些注释。观看shebang线;#!/usr/bin/env perl
该代码已有三年之久,自从编写以来我就开始使用;然后我use warnings;
在代码主体的顶部。
#!/bin/perl -w
#
# @(#)$Id: usergroupmap.pl,v 1.6 2009/06/08 02:30:19 jleffler Exp $
#
# Create a map of groups associated with users
use strict;
use constant debug => 0;
$| = 1;
my $group_entries = 0; # Number of rows returned by getgrent()
my %usr_hash = (); # List of lists of GID values keyed by user name
my %gid_hash = (); # List of GID values count definitions
my %grp_hash = (); # List of group name values and corresponding GID value
my %grp_count = (); # List of count of entries with given group name
my %gid_name = (); # List of first occurring name for group, indexed by GID
{
while (my ($name, $password, $gid, $userlist) = getgrent())
{
print "# $gid ($name) $userlist\n" if debug > 1;
$group_entries++ ;# if debug > 0;
$grp_hash{$name} = $gid unless defined $grp_hash{$name} ;# if debug > 0;
$grp_count{$name} = 0 unless defined $grp_count{$name} ;# if debug > 0;
$grp_count{$name}++ ;# if debug > 0;
$gid_hash{$gid} = 0 unless defined $grp_hash{$gid} ;# if debug > 0;
$gid_hash{$gid}++ ;# if debug > 0;
$gid_name{$gid} = $name unless defined $gid_name{$gid};
foreach my $user (split /[, ]/, $userlist)
{
print ". $user\n" if debug > 1;
$usr_hash{$user} = { } unless defined $usr_hash{$user};
$usr_hash{$user}->{$gid} = 1;
}
printf "-- Group %-8s reappears with GID %5d (previously %5d)\n",
$name, $gid, $grp_hash{$name} if $grp_hash{$name} != $gid;
printf "-- GID %-8d reappears with name %-8s (previously %-8s)\n",
$gid, $name, $gid_name{$gid} if $name ne $gid_name{$gid};
}
}
printf "Number of group entries: %5d\n", $group_entries ;# if debug > 0;
printf "Number of group names: %5d\n", scalar(keys %grp_hash) ;# if debug > 0;
printf "Number of group numbers: %5d\n", scalar(keys %gid_hash) ;# if debug > 0;
printf "Number of user names: %5d\n", scalar(keys %usr_hash) ;# if debug > 0;
{
foreach my $gid (sort keys %gid_hash)
{
printf " Group ID %5d (%-8s) appears %2d times\n",
$gid, $gid_name{$gid}, $gid_hash{$gid} if $gid_hash{$gid} > 1;
}
}
# Nominally, this should print nothing.
# However, when the local /etc/group file and the NIS+ group file disagree, it does.
foreach my $name (sort keys %grp_count)
{
printf " Group name %-8s (%-5d) appears %2d times\n",
$name, $grp_hash{$name}, $grp_count{$name} if $grp_count{$name} > 1;
}
# Determining canonical name for a group turns out to be tricky!
# On Solaris, it appears that:
# --- When groups are listed in /etc/group, the first name for a given GID is used
# -1- Add to /etc/group:
# a123::54876:username
# a12::54876:username
# a1::54876:username
# a::54876:username
# --- With these entries present, first one listed in /etc/group is 'name of group'
# --- Demonstrated with multiple permutations of 4 entries.
#
# --- When groups are listed via NIS+, the shortest name for a given GID is used
# -1- In NIS+ data,
# -- GID 1360 reappears with name rand8 (previously rand4 )
# -- GID 1360 reappears with name rand3 (previously rand4 )
# -- GID 1360 reappears with name rand (previously rand4 )
# -- GID 1360 reappears with name rand9 (previously rand4 )
# -- GID 1360 reappears with name rand1 (previously rand4 )
# -- GID 1360 reappears with name rand2 (previously rand4 )
# -- GID 1360 reappears with name rand10 (previously rand4 )
# -- GID 1360 reappears with name rand5 (previously rand4 )
# -- GID 1360 reappears with name rand7 (previously rand4 )
# -- GID 1360 reappears with name rand11 (previously rand4 )
# -- GID 1360 reappears with name rand12 (previously rand4 )
# -- GID 1360 reappears with name rand6 (previously rand4 )
# --- With these entries present, shortest name (rand) is listed by 'ls'.
# -2- In NIS+ data,
# -- GID 1240 reappears with name pd (previously rd )
# --- With these entries present, first name with shortest length (rd) is listed by 'ls'.
# -3- In NIS+ data,
# -- GID 8714 reappears with name vcs-vsnet (previously vcs-mgr2)
# -- GID 8714 reappears with name vcs (previously vcs-mgr2)
# -- GID 8714 reappears with name vcs-tech (previously vcs-mgr2)
# -- GID 8714 reappears with name vcs-tech1 (previously vcs-mgr2)
# -- GID 8714 reappears with name vcs-sys2 (previously vcs-mgr2)
# -- GID 8714 reappears with name vcs-mgr1 (previously vcs-mgr2)
# -- GID 8714 reappears with name vcs-other (previously vcs-mgr2)
# -- GID 8714 reappears with name vcs-sys1 (previously vcs-mgr2)
# -- GID 8714 reappears with name vcs-mgr (previously vcs-mgr2)
# -- GID 8714 reappears with name vcs-mgr3 (previously vcs-mgr2)
# -- GID 8714 reappears with name vcs-sys (previously vcs-mgr2)
# --- With these entries present, shortest name (vcs) is listed by 'ls'.
# --- Could be first name without punctuation?
# -4- In NIS+ data + /etc/group data (other::1:root in /etc/group)
# -- Group other reappears with GID 20 (previously 1)
# --- With these entries present, 'chgrp 1 x; ls -l x' lists group as other.
# --- With these entries present, 'chgrp 20 x; ls -l x' lists group as other.
# --- Hence, 'ls' must use getgrgid() to determine group name.
# -5- In NIS+ data
# -- GID 7777 reappears with name xgrp (previously pdxgrp )
# --- With these entries present, 'chgrp pdxgrp x; ls -l x' lists xgrp as group.
# --- Hence, as expected, chgrp uses getgrnam() to determine GID, and ls uses getgrgid().
# -6- Add entry 'ccc::8714:' to /etc/group.
# With this entry present, 'chgrp 8714 x; ls -l x' lists ccc as group.
# NB: /etc/nsswitch.conf lists 'group: files nis' (and 'passwd: files').
#
# --- NB: No definitive test with same group name listed in both /etc/group and NIS+.
# --- NB: No definitive info on why rand.
# --- NB: No definitive info on why vcs.
# Hence: most reliable way to determine canonical name for a given GID is via getgrgid().
# Determining it from the results of getgrent() is unreliable.
my $max_groups = 0;
my $max_user = "";
my $tot_usrgrp = 0;
my %grp_lists = ();
foreach my $user (sort keys %usr_hash)
{
my $groups = $usr_hash{$user};
my $numgrps = scalar(keys %{$groups});
$tot_usrgrp += $numgrps;
if ($numgrps > $max_groups)
{
$max_groups = $numgrps;
$max_user = $user;
}
my $grplst = "";
foreach my $group (sort keys %{$groups})
{
$grplst .= " $group";
}
$grp_lists{$grplst} = 1;
print "$user: $grplst\n" if debug;
}
printf "Maximum number of groups for one user (%s): %5d\n", $max_user, $max_groups;
printf "Total number of groups listed for all users: %5d\n", $tot_usrgrp;
printf "Total number of distinct group lists: %5d\n", scalar(keys %grp_lists);
my %name_hash = (); # List of distinct names - group names and user names
foreach my $user (keys %usr_hash)
{
$name_hash{$user} = 1;
}
foreach my $group (keys %grp_hash)
{
$name_hash{$group} = 1;
}
my $name_offset = 0;
foreach my $name (keys %name_hash)
{
$name_hash{$name} = $name_offset;
$name_offset += length($name) + 1;
}
printf "Total space needed for names = %5d\n", $name_offset;
# Add gid to group list if not already present
# If input is sorted, add condition: last if $grpnum > $gid;
sub add_gid
{
my($gid, @groups) = @_;
foreach my $grpnum (@groups)
{
return(@groups) if ($grpnum == $gid);
}
return sort { $a <=> $b } $gid, @groups;
}
# Get group set for given user name
sub getgrsetnam
{
my($user) = @_;
my(@groups) = ();
my($usrref) = $usr_hash{$user};
print "getgrsetnam(): name = $user\n" if debug > 0;
push(@groups, sort { $a <=> $b } keys %$usrref) if defined $usrref;
print "getgrsetnam(): groups = @groups\n" if debug > 0;
my($name, $pass, $pw_uid, $gid) = getpwnam($user);
# Not all users listed in groups appear in password
if (defined $name)
{
print "getgrsetnam(): user = $name, $pw_uid, $gid\n" if debug > 0;
@groups = add_gid($gid, @groups);
}
return(@groups);
}
# Get set of group IDs for given user number
sub getgrsetuid
{
my($uid) = @_;
print "getgrsetuid(): $uid\n" if debug > 0;
my($name, $pass, $pw_uid, $gid) = getpwuid($uid);
print "getgrsetuid(): $name, $pw_uid, $gid\n" if debug > 0;
my(@groups) = ();
# Not all UID values have a user name
if (defined $name)
{
print "getgrsetuid(): name = $name\n" if debug > 0;
@groups = getgrsetnam($name);
@groups = add_gid($gid, @groups);
}
return(@groups);
}
{
foreach my $user (sort keys %usr_hash)
{
print "user = $user\n" if debug > 0;
my(@groups) = getgrsetnam($user);
printf "%-9s @groups\n", "$user:";
}
}
{
foreach my $uid (0..65535)
{
my($name, $pass, $pw_uid, $gid) = getpwuid($uid);
if (defined $name)
{
print "uid = $uid\n" if debug > 0;
my(@groups) = getgrsetuid($uid);
printf "%-9s (uid = %6d) @groups\n", "$name:", $uid;
}
}
}
__END__
一些总结输出:
...
-- Group nobody reappears with GID 60001 (previously 99)
...
Number of group entries: 225
Number of group names: 221
Number of group numbers: 148
Number of user names: 1072
Group name xxxxxxx1 (297 ) appears 2 times
Group name xxxxxxx2 (296 ) appears 2 times
Group name xxxxxxx3 (102 ) appears 2 times
Group name nobody (99 ) appears 2 times
Maximum number of groups for one user (xxxxxxxx): 32
Total number of groups listed for all users: 2275
Total number of distinct group lists: 108
Total space needed for names = 9562
x 的字符串是我掩盖的不同名称。