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 |
|
Group name (e.g. |
1 |
|
Group password, typically |
2 |
|
Numeric group id |
3 |
|
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 withdefinedrather than truthiness, since a legitimate group name could in principle be the string"0".No password field on modern Linux:
$passwdis 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$membersas 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:
$memberslists only the users explicitly named in the group’s/etc/grouprecord. A user whose primary gid matches this group but who is not also listed in themembersfield does not appear. Cross-check withgetpwentif you need both kinds of membership.Nested iteration clobbers position: a second
getgrentloop started before the first one finishes shares the same cursor. Usesetgrentbetween passes, or snapshot the database into memory up front.Taintedness: the
$passwdfield is tainted under-T, same as forgetpwent— 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
getgrentat once will miss records.endgrentis optional but polite: leaving the database open to program exit works, but if you are done iterating, callendgrentso 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 recordgetgrgid— single lookup by numeric gid; the usual partner ofstatwhen you need a group name for a filesetgrent— rewind the iterator to the start of the group database before another full-scan passendgrent— close the database and release any resources the C library holds opengetpwent— the parallel iterator over the passwd database; pair withgetgrentwhen resolving full user-to-group relationshipsscalar— force scalar context when you only need the group name and want to skip the other fields