Grep / etc / passwd和/ etc / group列出用户所属的所有用户和每个组

时间:2012-09-21 23:12:21

标签: linux bash shell scripting

我正在尝试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 个答案:

答案 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组的条目会列出该用户。

请注意,您可以在群组文件中找到各种奇怪的内容。

  • 同一GID的多个名称。
  • 具有不同GID值的同一组名称的多行。
  • /etc/passwd中列出的未列在/etc/group
  • 中的GID值
  • /etc/passwd文件中具有相同UID的多个名称(通常允许不同的人使用自己的凭据登录 - 名称和密码 - 但所有人都以UID 0运行,即root)。

处理所有这些是一场噩梦 - 是的,几乎所有这些都发生在我工作的系统上,但不管理。

在某个地方,我有代码来帮助进行这种分析;它是Perl,并使用getpwent()getgrent()来检索信息(来自管理员配置的文件或网络)。它不漂亮。


对于课堂练习分析,您可能需要:

来自/etc/passwd

  • 用户名
  • 用户ID(UID)
  • 主要群组ID(GID)

来自/etc/group

  • 主要GID的名称。
  • 列出用户名的所有其他组条目。

如果您只是简单地使用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函数,最终节省时间。