User and group info

getgrgid#

Look up a group record by numeric group ID.

getgrgid asks the system group database for the entry whose gid is GID and returns what it finds. It is the numeric counterpart of getgrnam and mirrors the C library call getgrgid(3). In list context you get the unpacked record fields; in scalar context you get the group name.

Synopsis#

my ($name, $passwd, $gid, $members) = getgrgid($gid);
my  $name                           = getgrgid($gid);

What you get back#

In list context, four values unpacked from struct group:

#    0       1         2      3
my ( $name,  $passwd,  $gid,  $members ) = getgrgid($gid);
  • $name — the group name.

  • $passwd — the group password field. On modern systems this is typically "x" or empty; real hashes live in a shadow database.

  • $gid — the numeric group ID (the same number you passed in, echoed back from the record).

  • $members — a single space-separated string of member login names, not a list. Split it yourself when you need an array:

    my @users = split / /, (getgrgid($gid))[3];
    

In scalar context, just the group name:

my $name = getgrgid(0);    # "root" on most Linux systems

If no group with the given GID exists, the function returns the empty list in list context and undef in scalar context. A system-level failure (e.g. NSS backend unreachable) is reported the same way; check $! if you need to distinguish.

Global state it touches#

  • $! — set on system-level failure of the underlying getgrgid(3) call. Not set when the GID simply has no matching entry; that is a normal “not found”, not an error.

  • The process-wide group-database iteration cursor maintained by libc. getgrgid does not advance or reset the cursor used by getgrent, setgrent, and endgrent, but it shares the same underlying file descriptor on most implementations — interleaving direct lookups with iteration is portable in theory and surprising in practice.

Examples#

Look up the primary group name for the current process:

my $group = getgrgid($();           # $( is the real GID
print "running as group: $group\n";

Resolve a file’s group ID to a name, with a sensible fallback for orphaned GIDs (files owned by a group that no longer exists):

my $gid   = (stat $file)[5];
my $name  = getgrgid($gid) // "<unknown gid $gid>";

Unpack the full record and list the members:

if (my ($name, undef, $gid, $members) = getgrgid(100)) {
    my @users = split / /, $members;
    print "group $name ($gid) has ", scalar(@users), " members\n";
}

Guard against the “no such group” case explicitly — the empty list in boolean/list context is already falsy, so an if suffices:

unless (my @gr = getgrgid($gid)) {
    die "no group with gid $gid\n";
}

Pair with getpwuid to render a directory listing’s owner and group columns:

my @st   = stat $path                 or die "stat $path: $!";
my $user = getpwuid($st[4]) // $st[4];
my $grp  = getgrgid($st[5]) // $st[5];
printf "%-8s %-8s %s\n", $user, $grp, $path;

Edge cases#

  • Scalar vs list context matters. getgrgid($gid) in scalar context yields only the group name; wrap in parentheses or assign to a list to get the full record:

    my $name    = getgrgid(0);              # "root"
    my @record  = getgrgid(0);              # four-element list
    my $gid_out = (getgrgid(0))[2];         # the gid field
    
  • $members is a string, not an array. The whitespace-joined form is historical and matches perl5’s pp_gpwent / pp_ggrent output. Splitting on / / (single space) is the canonical idiom; splitting on /\s+/ is slightly more forgiving if a backend ever uses tabs.

  • Empty groups return $members as the empty string. split / /, "" yields the empty list, which is what callers usually want.

  • Numeric coercion. GID is passed to the C call as an integer. A non-numeric scalar is coerced: "0root" becomes 0. Pass a clean integer.

  • GID namespace overlap. Multiple group entries with the same gid are legal in /etc/group and in NSS. getgrgid returns one of them — which one is backend-defined. Do not rely on which name comes back when GIDs collide; use getgrnam for the reverse direction when the name matters.

  • NSS / nsswitch.conf. The lookup follows the system’s name service switch — files, sss, ldap, etc. A successful lookup in a test harness on one host may fail on another with a different NSS configuration. This is a property of the C library, not of Perl.

  • Large groups. Very large group-member lists (hundreds of members) can arrive truncated if the backend’s internal buffer is smaller than the rendered line. This is a libc / NSS limitation; Perl faithfully surfaces whatever the system returned.

  • Threaded builds. On systems that provide getgrgid_r, perl transparently calls the reentrant variant. Callers see no difference.

  • Taint. Under -T, $passwd and $members come back tainted — the first because it may encode credentials, the second because administrators can change it. $name and $gid are not tainted.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • getgrnam — the inverse lookup: group record by name rather than by gid

  • getgrent — iterate every group entry when you need to scan rather than look up

  • setgrent and endgrent — reset and release the iteration cursor used by getgrent

  • getpwuid — the passwd-side counterpart; pair with getgrgid to render owner/group columns

  • User::grent — by-name accessor object that overrides getgrgid when you prefer $gr->name over positional unpacking