User and group info

getgrent#

Read the next entry from the system group database.

getgrent walks the host’s group database one entry at a time, the way a plain loop over /etc/group would read it line by line, and returns a Perl-shaped view of each record. It is the iterator form of the group lookups — use it when you want every group, or every group matching some predicate. For single-entry lookup by name or gid, reach for getgrnam or getgrgid instead.

A first call to getgrent implicitly opens the database. Iteration continues across further calls until the database is exhausted, at which point getgrent returns the empty list (or undef in scalar context). Call setgrent to rewind to the start, or endgrent to close the database and free any resources the C library is holding.

Synopsis#

my ($name, $passwd, $gid, $members) = getgrent;
my $name                            = getgrent;        # scalar context

What you get back#

In list context, a four-element list — one entry per group:

Index

Field

Meaning

0

$name

Group name (e.g. "wheel")

1

$passwd

Group password, typically "x" on shadow-password hosts

2

$gid

Numeric group id

3

$members

Space-separated string of member login names

When iteration runs off the end, the return is the empty list. If you write if (my @gr = getgrent) the loop terminates cleanly on end-of-database.

In scalar context, getgrent returns just $name. When the database is exhausted, it returns undef — which is the signal most scripts test against to end a while loop.

$members is a plain string, not an array. Split it on whitespace when you need the list:

my @logins = split ' ', $members;

Global state it touches#

getgrent keeps per-process iteration state inside the C library (conceptually a file position inside the group database). Two calls in the same process share that state; a nested loop that calls getgrent inside another getgrent loop will interleave records and miss most of them. Serialise the passes, or capture all entries into an array first:

my @all;
while (my @gr = getgrent) { push @all, [ @gr ] }
endgrent;

The iterator is not reset when a subroutine returns. If library code you do not control calls getgrent, it moves your position. Guard with setgrent when you need a clean start.

Examples#

Walk every group and print the name and member count:

while (my ($name, undef, $gid, $members) = getgrent) {
    my $n = length $members ? scalar(split ' ', $members) : 0;
    printf "%-16s gid=%d members=%d\n", $name, $gid, $n;
}
endgrent;

Collect all groups a given login belongs to:

my $user = 'alice';
my @groups;
while (my ($name, undef, undef, $members) = getgrent) {
    push @groups, $name
        if grep { $_ eq $user } split ' ', $members;
}
endgrent;

Scalar context — list every group name, one per line:

while (defined(my $name = getgrent)) {
    print $name, "\n";
}
endgrent;

Rewind and re-read, for example to build two indexes in one pass each:

setgrent;
my %by_name = map { $_->[0] => $_ }
              map { [ getgrent ] } 1 .. ();   # collect…
# …or just loop twice with an explicit setgrent before each pass.
setgrent;
while (my @gr = getgrent) { ... }
endgrent;

Convert a numeric gid from stat to a group name without a second lookup by caching the iterator output:

my %name_of;
while (my ($name, undef, $gid) = getgrent) {
    $name_of{$gid} = $name;
}
endgrent;

my $gid = (stat $path)[5];
print "group: $name_of{$gid}\n";

Edge cases#

  • End of iteration vs. a real entry: in list context, an exhausted database returns the empty list — which assigns as zero elements, so my @gr = getgrent; @gr or last; is the idiomatic termination test. In scalar context, test with defined rather than truthiness, since a legitimate group name could in principle be the string "0".

  • No password field on modern Linux: $passwd is almost always "x" (real hashes live in /etc/gshadow). Do not treat a non-"x" value as “password set” without consulting the shadow database.

  • Empty $members: groups with no explicit members (users listed only by primary gid) have $members as the empty string. split ' ', "" returns an empty list — the idiomatic way to get members as an array without a special case.

  • Implicit primary-gid membership is not reported: $members lists only the users explicitly named in the group’s /etc/group record. A user whose primary gid matches this group but who is not also listed in the members field does not appear. Cross-check with getpwent if you need both kinds of membership.

  • Nested iteration clobbers position: a second getgrent loop started before the first one finishes shares the same cursor. Use setgrent between passes, or snapshot the database into memory up front.

  • Taintedness: the $passwd field is tainted under -T, same as for getpwent — treat it as untrusted input even though the usual value is "x".

  • Process-wide, not thread-wide: on systems without thread-safe variants the iteration state is per process, not per thread. Two threads walking getgrent at once will miss records.

  • endgrent is optional but polite: leaving the database open to program exit works, but if you are done iterating, call endgrent so the C library can drop its buffers.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • getgrnam — single lookup by group name; use when you already know the name and want just one record

  • getgrgid — single lookup by numeric gid; the usual partner of stat when you need a group name for a file

  • setgrent — rewind the iterator to the start of the group database before another full-scan pass

  • endgrent — close the database and release any resources the C library holds open

  • getpwent — the parallel iterator over the passwd database; pair with getgrent when resolving full user-to-group relationships

  • scalar — force scalar context when you only need the group name and want to skip the other fields