1

我正在尝试列出所有用户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

这只显示带有“:”和全名的用户

我该怎么做呢?

4

4 回答 4

6

如果使用 grep 的要求/etc/passwd并不/etc/group难,请考虑以下解决方案:

for user in $(getent passwd | cut -d: -f1); do
    printf "%s: %s\n" $user "$(id -nG $user)"
done

请注意@JonathanLeffler 的建议,因为这个问题并不像看起来那么简单。

于 2012-09-21T23:37:54.053 回答
4

即使忽略文件的网络数据库的可能性,您也确实不能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/passwdwithgrep获取用户名,然后用于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 的字符串是我掩盖的不同名称。

于 2012-09-21T23:22:59.130 回答
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 的答案在这方面要清楚得多。

于 2014-07-04T14:48:13.390 回答
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选项将输入字段分隔符“:”或“分隔符”定义为正则表达式fs。这可以避免使用该cat功能并最终节省时间。

于 2019-01-24T12:40:18.953 回答