User and group info

getpwnam#

Look up a user’s passwd record by login name.

getpwnam wraps the system C library’s getpwnam(3) call. Pass a login name like "root" or "alice"; you get back the passwd entry for that user, or an empty list / undef if no such user exists. The return shape depends on context: list context gives every field of the record; scalar context gives a single useful value — for getpwnam specifically, the numeric UID.

Synopsis#

getpwnam NAME

What you get back#

List context returns a 10-element list mirroring struct passwd augmented with the BSD extras:

my ($name,    $passwd, $uid,  $gid,   $quota,
    $comment, $gcos,   $dir,  $shell, $expire) = getpwnam($login);
  • $name — login name (same string you passed in)

  • $passwd — encrypted password, or "x" / "*" on shadow-password systems. Tainted under -T.

  • $uid — numeric user ID

  • $gid — numeric primary group ID

  • $quota — disk quota, or $change / $age on some systems. Empty string when unsupported.

  • $comment — administrative comment, sometimes $class. Empty string when unsupported.

  • $gcos — GECOS field (usually the real name plus other user info). Tainted under -T because users can edit it.

  • $dir — home directory

  • $shell — login shell. Tainted under -T.

  • $expire — account / password expiration, when supported.

Scalar context returns just $uid. For getpwnam — a lookup by name — scalar context gives you “the other thing,” matching the rule upstream documents for all getpw* / getgr* / gethost* / getnet* / getproto* / getserv* functions.

On lookup failure both contexts return the empty list / undef. Always check the return before unpacking.

Global state it touches#

  • Sets $! on system-call failure (rare — “user not found” is not a failure, it’s an empty return).

  • Under -T (taint mode), $passwd, $gcos, and $shell come back tainted. See perlsec for how to launder them.

  • Shares the same underlying iterator state as getpwent, setpwent, and endpwent. A getpwnam call does not disturb an in-progress getpwent loop on glibc, but portable code should not assume that.

Examples#

Basic name-to-UID lookup in scalar context:

my $uid = getpwnam("root");         # 0

Full record in list context, with a defensive check:

my @pw = getpwnam($user)
    or die "$user: not in passwd database";
my ($name, undef, $uid, $gid, undef, undef, $gcos, $home, $shell) = @pw;
print "$name ($uid) -> $home, shell $shell\n";

Assignment pattern from upstream — unpacking directly with or die:

my ($login, $pass, $uid, $gid) = getpwnam($user)
    or die "$user not in passwd file";

Comparing file ownership against a named user without hand-written field indexing, via the User::pwent override:

use User::pwent;
use File::stat;
my $is_theirs = (stat($filename)->uid == getpwnam($whoever)->uid);

use User::pwent replaces getpwnam with a version returning a blessed object with ->name, ->uid, ->gid, ->dir, ->shell accessors — handy when you want one field and don’t want to count commas. The two forms do not mix: inside one scope either use the override or the built-in, not both.

Fallback chain for “who am I running as”:

my $login = getlogin() || getpwuid($<) || "unknown";

getpwuid is the by-UID counterpart; getlogin reads the controlling-tty record and can lie on detached processes, so it’s the first choice, not the only one.

Edge cases#

  • No such user returns the empty list in list context and undef in scalar context. This is not an error — $! is not set. Distinguish “user missing” from “lookup failed” by checking $! only when you want to report an unexpected failure, not when deciding whether the user exists.

  • $passwd is rarely useful. On modern Linux and BSD systems the real hash lives in /etc/shadow, readable only by root. You get "x" or "*" here. Use PAM or the Authen::PAM module for authentication rather than comparing against this field.

  • $quota, $comment, $expire portability. These fields are empty or meaningless on Linux. The upstream POD lists Config values (d_pwquota, d_pwage, d_pwchange, d_pwcomment, d_pwexpire) you can check if you need to know at runtime whether the platform populates them.

  • Tainted fields under -T. $passwd, $gcos, $shell are tainted because users can modify them (via chsh, chfn, or direct /etc/passwd edits in privileged contexts). Pass them through a regex capture to launder before using in a system call.

  • Return value is a “single meaningless true value” in Boolean-ish scalar context when the entry exists but you wanted list context. The scalar-context documented return for getpwnam is the UID, which for root is 0 — a false value. if (getpwnam("root")) { ... } therefore fails for root. Use defined or list context:

    if (defined getpwnam("root"))     { ... }   # correct
    if (my @pw = getpwnam("root"))    { ... }   # also correct
    
  • Name is a string, not a UID. getpwnam(0) stringifies 0 and looks up the literal user named "0", which almost never exists. Use getpwuid for numeric lookups.

  • Large directories (LDAP / NIS / SSSD). getpwnam goes through NSS, so the cost scales with however the local resolver answers. A miss against a misconfigured LDAP server can block for seconds. Cache results in a hash if you are looking up many users in a tight loop.

  • Threads. Upstream notes that getpwnam_r is used automatically where the platform provides it; getpwent iteration, by contrast, remains per-process. Not a concern under pperl (no ithreads), but worth knowing if you share code with perl5.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • getpwuid — same record, looked up by numeric UID; pair with getpwnam when you need to go UID → name

  • getpwent — iterate the whole passwd database; use when you need every user, not a specific one

  • endpwent — close the passwd iterator after getpwent

  • getgrnam — group-database counterpart; needed to resolve the $gid field from this record back to a group name

  • getlogin — cheap “who is on this tty” lookup; fall back to getpwnam/getpwuid when it returns empty

  • User::pwent — object wrapper with named accessors; reach for it when you want ->uid instead of counting list positions