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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 14:25:52  来源:igfitidea点击:

Grep /etc/passwd and /etc/group to list all users and each group the user belongs to

linuxbashshellscripting

提问by Clu

I'm trying to grep/etc/passwdand /etc/groupto 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 grepfor 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/passwdfile. There can be an arbitrary number of other groups listed in /etc/groupfor 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/groupwill 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/passwdthat are not listed in /etc/group.
  • Multiple names with the same UID in the /etc/passwdfile (often to allow different people to login with their own credentials —?name and password — but to all run as UID 0, aka root).
  • 同一个 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 idcommand to do most of the work for you. Trawl through /etc/passwdwith grepto get user names, and then use idto specify the groups the user belongs to. See the answerby gvalkovfor an answer which is likely to be sufficient for sane systems.

如果您只是躲避并使用该id命令为您完成大部分工作,那就简单多了。通过/etc/passwdwithgrep来获取用户名,然后使用id来指定用户所属的组。请参阅gvalkov答案获取可能足以满足健全系统的答案。

grep -o '^[^:]*' /etc/passwd |
xargs -L1 id

This gives you names and numbers; tweak the options to idto suit your requirements. This is using GNU grep; the -ooption 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 perlsince 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/passwdand /etc/groupis 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_printand 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 fsoption defines the input field separator ":" or "separator" to be the regular expression fs. this could avoid using the catfunction and eventually save time .

awk 是处理文件的命令的名称,该-F fs选项将输入字段分隔符“:”或“分隔符”定义为正则表达式fs。这可以避免使用该cat功能并最终节省时间。