Linux grep /etc/passwd 和 /etc/group 列出所有用户和用户所属的每个组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12539272/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Grep /etc/passwd and /etc/group to list all users and each group the user belongs to
提问by Clu
I'm trying to grep
/etc/passwd
and /etc/group
to list ALL users and each group the user belongs to.
我正在尝试grep
/etc/passwd
并/etc/group
列出所有用户以及用户所属的每个组。
IE:
IE:
Jon Doe, root:randomgroup:randomgroup2:randomgroup3
Billy Bob, admin:apache:backups
Timmy Tim, root:www
Anyhow, this is what I have so far but I'm not entirely sure how to get it to grep groups and match on the user field.
无论如何,这就是我到目前为止所拥有的,但我不完全确定如何将其获取到 grep 组并在用户字段上进行匹配。
cat /etc/passwd | cut -d: -f1,5
This only displays the user with a ":" and the Full Name
这仅显示带有“:”和全名的用户
How would I go about doing this?
我该怎么做呢?
采纳答案by Jonathan Leffler
Even ignoring the possibility of network databases for your files, you really can't use grep
for this job, at least, not if you're going to do it really thoroughly. You probably should use Perl or Python or another similar scripting language of your choice.
即使忽略文件的网络数据库的可能性,你真的不能grep
用于这项工作,至少,如果你真的要彻底完成它的话。您可能应该使用 Perl 或 Python 或您选择的其他类似脚本语言。
Note that there is one group specified by number for the user in the /etc/passwd
file. There can be an arbitrary number of other groups listed in /etc/group
for the same user. Suppose the users 'primary GID' is 23 in /etc/passwd
. There is no guarantee that the entry for group 23 in /etc/group
will list that user.
请注意,/etc/passwd
文件中的用户通过编号指定了一个组。可以/etc/group
为同一用户列出任意数量的其他组。假设用户的“主要 GID”是 23 in /etc/passwd
。不能保证组 23 中的条目/etc/group
会列出该用户。
Note that you can have all sorts of weirdnesses turn up in the group file.
请注意,您可以在组文件中出现各种奇怪的东西。
- Multiple names for the same GID.
- Multiple lines for the same group name with different GID values.
- GID values listed in
/etc/passwd
that are not listed in/etc/group
. - Multiple names with the same UID in the
/etc/passwd
file (often to allow different people to login with their own credentials —?name and password — but to all run as UID 0, akaroot
).
- 同一个 GID 的多个名称。
- 具有不同 GID 值的相同组名的多行。
- 中列出的 GID 值
/etc/passwd
未在 中列出/etc/group
。 /etc/passwd
文件中具有相同 UID 的多个名称(通常允许不同的人使用他们自己的凭据登录 -?名称和密码 - 但都以 UID 0 运行,又名root
)。
Dealing with all of these is a nightmare — and yes, almost all of these occur on the systems I work on, but do not administer.
处理所有这些是一场噩梦——是的,几乎所有这些都发生在我工作的系统上,但不管理。
Somewhere, I have code to help with this analysis; it is Perl, and uses getpwent()
and getgrent()
to retrieve the information (from files or network as configured by the admins). It isn't pretty.
在某处,我有代码可以帮助进行此分析;它是 Perl,并使用getpwent()
和getgrent()
检索信息(从管理员配置的文件或网络中)。它不漂亮。
For a classroom exercise analysis, you probably need:
对于课堂练习分析,您可能需要:
From /etc/passwd
:
来自/etc/passwd
:
- user name
- user ID (UID)
- primary group ID (GID)
- 用户名
- 用户 ID (UID)
- 主要组 ID (GID)
From /etc/group
:
来自/etc/group
:
- Name for primary GID.
- All other group entries that list the user name.
- 主要 GID 的名称。
- 列出用户名的所有其他组条目。
It is much simpler if you simply duck and use the id
command to do most of the work for you. Trawl through /etc/passwd
with grep
to get user names, and then use id
to specify the groups the user belongs to. See the answerby gvalkovfor an answer which is likely to be sufficient for sane systems.
如果您只是躲避并使用该id
命令为您完成大部分工作,那就简单多了。通过/etc/passwd
withgrep
来获取用户名,然后使用id
来指定用户所属的组。请参阅gvalkov的答案以获取可能足以满足健全系统的答案。
grep -o '^[^:]*' /etc/passwd |
xargs -L1 id
This gives you names and numbers; tweak the options to id
to suit your requirements. This is using GNU grep
; the -o
option is convenient but not portable. Fortunately, Unix user names do no contain newlines (or colons); this simplifies the use of xargs
.
这会给你名字和号码;调整选项以id
满足您的要求。这是使用 GNU grep
;该-o
选项方便但不便携。幸运的是,Unix 用户名不包含换行符(或冒号);这简化了xargs
.
User Group Map
用户组图
243 lines or Perl, some of it comment. Watch the shebang line; the code is three years old and I've taken to using #!/usr/bin/env perl
since this was written; I then have use warnings;
at the top of the body of code.
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__
Some of the summary output:
一些摘要输出:
...
-- 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
The strings of x's are distinct names that I've masked.
x 的字符串是我屏蔽的不同名称。
回答by gvalkov
If the requirement to use grep on /etc/passwd
and /etc/group
is not a hard one, consider the following solution:
如果要求对使用grep/etc/passwd
和/etc/group
是不是一个很难,考虑以下方案:
for user in $(getent passwd | cut -d: -f1); do
printf "%s: %s\n" $user "$(id -nG $user)"
done
Please, heed @JonathanLeffler's advice as this problem is not as straightforward as it seems.
请注意@JonathanLeffler 的建议,因为这个问题并不像看起来那么简单。
回答by cellover
I just needed it myself today and came up with the following in around 2 minutes of fiddling with man pages:
我今天只是需要它,并在大约 2 分钟的时间摆弄手册页中提出了以下内容:
for i in $(cat /etc/passwd | awk --field-separator=":" '{print }'); do id $i; done
Can also be shortened slightly with:
也可以稍微缩短:
for i in $(cat /etc/passwd | cut -d: -f1); do id $i; done
The output looks like several idcommands in a row:
输出看起来像连续的几个id命令:
uid=34(backup) gid=34(backup) groups=34(backup)
uid=1000(bobby) gid=1000(bobby) groups=1000(bobby),27(sudo)
...
The result is not be the most readable but it is quite simple and easy to remember. @gvalkov answer is much more clear in that aspect.
结果不是最易读的,但它非常简单易记。@gvalkov 在这方面的回答要清楚得多。
回答by fratardi
I passed by the same issue using a awk command, I saw some replies above that looks almost the same
awk -F ":" '{print $value_of_field_to_print}' /path_of_file_containing_fields
我使用 awk 命令解决了同样的问题,我看到上面的一些回复看起来几乎一样
awk -F ":" '{print $value_of_field_to_print}' /path_of_file_containing_fields
For instance if I want multiple fields, I'll just add another print $value_of_field_to_print
and between them add a separator
例如,如果我想要多个字段,我将添加另一个打印$value_of_field_to_print
并在它们之间添加一个分隔符
This would look like
这看起来像
awk -F ":" '{print $value_of_field_to_print print "separator" print
$value_of_field_to_print2}' /path_of_file_containing_fields`
awk is the name of the command that handles files, the -F fs
option defines the input field separator ":" or "separator"
to be the regular expression fs
.
this could avoid using the cat
function and eventually save time .
awk 是处理文件的命令的名称,该-F fs
选项将输入字段分隔符“:”或“分隔符”定义为正则表达式fs
。这可以避免使用该cat
功能并最终节省时间。