我正在尝试grep
/etc/passwd
和/etc/group
列出所有用户和用户所属的每个群组。
IE:
Jon Doe, root:randomgroup:randomgroup2:randomgroup3
Billy Bob, admin:apache:backups
Timmy Tim, root:www
无论如何,这是我到目前为止所做的,但我并不完全确定如何让它成为grep组并匹配用户字段。
cat /etc/passwd | cut -d: -f1,5
这仅显示具有“:”和全名
的用户我将如何做到这一点?
答案 0 :(得分:6)
如果在/etc/passwd
和/etc/group
上使用grep的要求不是很难,请考虑以下解决方案:
for user in $(getent passwd | cut -d: -f1); do
printf "%s: %s\n" $user "$(id -nG $user)"
done
请注意@ JonathanLeffler的建议,因为这个问题并不像看起来那么简单。
答案 1 :(得分:4)
即使忽略了文件的网络数据库的可能性,你也真的无法使用grep
来完成这项工作,至少,如果你真的要彻底完成这项工作的话。您可能应该使用Perl或Python或您选择的其他类似脚本语言。
请注意,/etc/passwd
文件中的用户有一个由编号指定的组。 /etc/group
中可以为同一用户列出任意数量的其他组。假设用户的主要GID'在/etc/passwd
中为23。无法保证/etc/group
中第23组的条目会列出该用户。
请注意,您可以在群组文件中找到各种奇怪的内容。
/etc/passwd
中列出的未列在/etc/group
。/etc/passwd
文件中具有相同UID的多个名称(通常允许不同的人使用自己的凭据登录 - 名称和密码 - 但所有人都以UID 0运行,即root
)。 处理所有这些是一场噩梦 - 是的,几乎所有这些都发生在我工作的系统上,但不管理。
在某个地方,我有代码来帮助进行这种分析;它是Perl,并使用getpwent()
和getgrent()
来检索信息(来自管理员配置的文件或网络)。它不漂亮。
对于课堂练习分析,您可能需要:
来自/etc/passwd
:
来自/etc/group
:
如果您只是简单地使用id
命令为您完成大部分工作,那就简单多了。使用/etc/passwd
跟踪grep
以获取用户名,然后使用id
指定用户所属的组。请参阅answer旁边的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的字符串是我掩盖的不同名称。
答案 2 :(得分:2)
我今天只是自己需要它,并在大约2分钟的时间里提出了以下内容:
for i in $(cat /etc/passwd | awk --field-separator=":" '{print $1}'); do id $i; done
也可以稍微缩短:
for i in $(cat /etc/passwd | cut -d: -f1); do id $i; done
输出看起来像连续几个 id 命令:
uid=34(backup) gid=34(backup) groups=34(backup)
uid=1000(bobby) gid=1000(bobby) groups=1000(bobby),27(sudo)
...
结果不是最具可读性,但它非常简单易记。 @gvalkov在这方面的答案要清楚得多。
答案 3 :(得分:1)
我使用awk命令传递了相同的问题,上面看到的一些回复看起来几乎相同
awk -F ":" '{print $value_of_field_to_print}' /path_of_file_containing_fields
例如,如果我想要多个字段,我将只添加另一个打印$value_of_field_to_print
,并在它们之间添加一个分隔符
这看起来像
awk -F ":" '{print $value_of_field_to_print print "separator" print
$value_of_field_to_print2}' /path_of_file_containing_fields`
awk是处理文件的命令的名称,-F fs
选项定义输入字段分隔符“:”或“ separator”
成为正则表达式fs
。
这样可以避免使用cat
函数,最终节省时间。