27

I'm attempting to code a script that outputs each user and their group on their own line like so:

user1 group1  
user2 group1  
user3 group2  
...  
user10 group6

etc.

I'm writing up a script in python for this but was wondering how SO might do this.

p.s. Take a whack at it in any language but I'd prefer python.

EDIT: I'm working on Linux. Ubuntu 8.10 or CentOS =)

ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
Derek
  • 982
  • 1
  • 8
  • 19

5 Answers5

32

For *nix, you have the pwd and grp modules. You iterate through pwd.getpwall() to get all users. You look up their group names with grp.getgrgid(gid).

import pwd, grp
for p in pwd.getpwall():
    print p[0], grp.getgrgid(p[3])[0]
Day
  • 9,465
  • 6
  • 57
  • 93
S.Lott
  • 384,516
  • 81
  • 508
  • 779
17

the grp module is your friend. Look at grp.getgrall() to get a list of all groups and their members.

EDIT example:

import grp
groups = grp.getgrall()
for group in groups:
    for user in group[3]:
        print user, group[0]
d0k
  • 2,605
  • 19
  • 16
  • This worked, but it seems, that its not listing all the users. Any thoughts? $ ./script.py | wc -l 81 $ sudo cat /etc/shadow | wc -l 406 – Derek Jan 07 '09 at 19:33
  • I think what i'm seeing is that the server I'm using this on has a ton of groups with no users in them (old, old groups) that happen to still be around. This script ignores groups with no users in them, which is fine, that's what I need! =) – Derek Jan 07 '09 at 19:39
  • 2
    This solution only lists the users' *secondary* groups. My read of question was that it asks for "their group", ie the [singular] user's *primary* group? – Martin Carpenter Jan 07 '09 at 20:10
  • See S.Lott's answer for a solution which shows only the primary group – d0k Jan 08 '09 at 14:00
5

sh/bash:

getent passwd | cut -f1 -d: | while read name; do echo -n "$name " ; groups $name ; done
Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
5

The python call to grp.getgrall() only shows the local groups, unlike the call to getgrouplist c function which retruns all users, e.g. also users in sssd that is backed by an ldap but has enumeration turned off. (like in FreeIPA). After searching for the easiest way to get all groups a users belongs to in python the best way I found was to actually call the getgrouplist c function:

#!/usr/bin/python

import grp, pwd, os
from ctypes import *
from ctypes.util import find_library

libc = cdll.LoadLibrary(find_library('libc'))

getgrouplist = libc.getgrouplist
# 50 groups should be enought?
ngroups = 50
getgrouplist.argtypes = [c_char_p, c_uint, POINTER(c_uint * ngroups), POINTER(c_int)]
getgrouplist.restype = c_int32

grouplist = (c_uint * ngroups)()
ngrouplist = c_int(ngroups)

user = pwd.getpwuid(2540485)

ct = getgrouplist(user.pw_name, user.pw_gid, byref(grouplist), byref(ngrouplist))

# if 50 groups was not enough this will be -1, try again
# luckily the last call put the correct number of groups in ngrouplist
if ct < 0:
    getgrouplist.argtypes = [c_char_p, c_uint, POINTER(c_uint *int(ngrouplist.value)), POINTER(c_int)]
    grouplist = (c_uint * int(ngrouplist.value))()
    ct = getgrouplist(user.pw_name, user.pw_gid, byref(grouplist), byref(ngrouplist))

for i in xrange(0, ct):
    gid = grouplist[i]
    print grp.getgrgid(gid).gr_name

Getting a list of all users to run this function on similarly would require to figure out what c call is made by getent passwd and call that in python.

Jens Timmerman
  • 9,316
  • 1
  • 42
  • 48
  • To make this Python3 friendly, since bytes and strings are more separated and automatic encoding doesn't occur. `getgrouplist` should have a bytes string as its first argument like so: `getgrouplist(bytes(user.pw_name, 'UTF-8'), ...)`. Obviously `xrange` is `range` in Python3 as well. But that clears up any conversion issues from Python2 to Python3. – Torxed Jun 03 '20 at 07:44
  • On my system (rhel), `find_library('libc')` returned None. It seemed to work anyways, perhaps indicating that libc is the default, but I would still recommend using `find_library('c')` instead. – Quantum7 Dec 08 '20 at 09:37
1

a simple function which is capable to deal with the structure of any one of these files (/etc/passwd and /etc/group).

I believe that this code meets your needs, with Python built-in functions and no additional module:

#!/usr/bin/python


def read_and_parse(filename):
    """
        Reads and parses lines from /etc/passwd and /etc/group.

        Parameters

          filename : str
            Full path for filename.
    """
    data = []
    with open(filename, "r") as f:
        for line in f.readlines():
            data.append(line.split(":")[0])
        data.sort()
        for item in data:
            print("- " + item)


read_and_parse("/etc/group")
read_and_parse("/etc/passwd")
ivanleoncz
  • 9,070
  • 7
  • 57
  • 49