46

I could grep through /etc/passwd but that seems onerous. 'finger' isn't installed and I'd like to avoid that dependency. This is for a program so it would be nice if there was some command that let you just access user info.

bignose
  • 30,281
  • 14
  • 77
  • 110
Josh Gibson
  • 21,808
  • 28
  • 67
  • 63

10 Answers10

65

You don't specify a programming language, so I'll assume you want to use the shell; here's an answer for Posix shells.

Two steps to this: get the appropriate record, then get the field you want from that record.

First, getting the account record is done by querying the passwd table:

$ user_name=foo
$ user_record="$(getent passwd $user_name)"
$ echo "$user_record"
foo:x:1023:1025:Fred Nurk,,,:/home/foo:/bin/bash

For hysterical raisins, the full name of the user is recorded in a field called the “GECOS” field; to complicate matters, this field often has its own structure with the full name as just one of several optional sub-fields. So anything that wants to get the full name from the account record needs to parse both these levels.

$ user_record="$(getent passwd $user_name)"
$ user_gecos_field="$(echo "$user_record" | cut -d ':' -f 5)"
$ user_full_name="$(echo "$user_gecos_field" | cut -d ',' -f 1)"
$ echo "$user_full_name"
Fred Nurk

Your programming language probably has a library function to do this in fewer steps. In C, you'd use the ‘getpwnam’ function and then parse the GECOS field.

bignose
  • 30,281
  • 14
  • 77
  • 110
  • 24
    hysterical raisins? – dat Apr 10 '16 at 05:18
  • 2
    Didn't work for me. But this did: `getent passwd | grep "$USER" | cut -d":" -f5 | cut -d"," -f1` – Dean Rather May 24 '16 at 00:48
  • 2
    Use grep "^$USER:" or getent passwd $USER to not match all usernames containing the name of the current user – nvrandow Jul 22 '16 at 16:13
  • Great answer. Here's another one-liner based on your answer that's suitable to insert somewhere in eg. /etc/profile.d export FULL_USER_NAME="$(getent passwd $USER | cut -d ':' -f 5 | cut -d ',' -f 1)" – Neil Stephens Jun 20 '23 at 01:39
33

On a modern glibc system, use this command:

getent passwd "username" | cut -d ':' -f 5

That'll get you the passwd entry of the specified user, independent of the underlying NSS module.

Read the manpage of getent.


If you're already programming, you can use the getpwnam() C-Function:

struct passwd *getpwnam(const char *name);

The passwd struct has a pw_gecos member which should contain the full name of the user.

Read the manpage of getpwnam().


Be aware that many systems use this field for more than the full name of the user. The most common convention is to use a comma (,) as separator within the field and place the users real name first.

David Schmitt
  • 58,259
  • 26
  • 121
  • 165
  • 3
    So typically the command to get only the user's real name is `getent passwd $USER | cut -d ':' -f 5 | cut -d ',' -f 1` – jpyams Aug 03 '18 at 17:01
  • 1
    With double quotes around `$USER`, in case it contains special characters, and without quotes around `:` and `,`: `getent passwd "$USER" | cut -d: -f5 | cut -d, -f1` – Alexander Feb 01 '19 at 12:33
  • does not work `getent passwd "${USER}" | cut -d: -f5` will return `,,,` on a non sudo. EDIT: I think because the created did not have name. – MaXi32 Dec 29 '22 at 09:49
11

Combination of other answers, tested on minimal Debian/Ubuntu installations:

getent passwd "$USER" | cut -d ':' -f 5 | cut -d ',' -f 1
Tombart
  • 30,520
  • 16
  • 123
  • 136
  • There are no practical reasons to invoke a subshell for `whoami`, you can use any username directly as an argument. And if _must_ use a subshell, please don't advocate backsticks, use `$()`. But in this case `"$USER"` makes much more sene – MestreLion Apr 20 '21 at 13:23
9

Just in case you want to do this from C, try something like this:

#include <sys/types.h>
#include <pwd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

/* Get full name of a user, given their username. Return 0 for not found,
   -1 for error, or 1 for success. Copy name to `fullname`, but only up
   to max-1 chars (max includes trailing '\0'). Note that if the GECOS
   field contains commas, only up to to (but not including) the first comma
   is copied, since the commas are a convention to add more than just the
   name into the field, e.g., room number, phone number, etc. */
static int getfullname(const char *username, char *fullname, size_t max)
{
    struct passwd *p;
    size_t n;

    errno = 0;
    p = getpwnam(username);
    if (p == NULL && errno == 0)
        return 0;
    if (p == NULL)
        return -1;
    if (max == 0)
        return 1;
    n = strcspn(p->pw_gecos, ",");
    if (n > max - 1)
        n = max - 1;
    memcpy(fullname, p->pw_gecos, n);
    fullname[n] = '\0';
    return 1;
}

int main(int argc, char **argv)
{
    int i;
    int ret;
    char fullname[1024];

    for (i = 1; i < argc; ++i) {
        ret = getfullname(argv[i], fullname, sizeof fullname);
        if (ret == -1)
            printf("ERROR: %s: %s\n", argv[i], strerror(errno));
        else if (ret == 0)
            printf("UNKONWN: %s\n", argv[i]);
        else
            printf("%s: %s\n", argv[i], fullname);
    }
    return 0;
}
8

Try this:

getent passwd eutl420 | awk -F':' '{gsub(",", "",$5); print $5}'
Jason Plank
  • 2,336
  • 5
  • 31
  • 40
Rob L
  • 81
  • 1
  • 1
4

The top two answers can be combined in one line:

getent passwd <username> | cut -d ':' -f 5 | cut -d ',' -f 1
AstroFloyd
  • 405
  • 5
  • 14
2

My code works in bash and ksh, but not dash or old Bourne shell. It reads the other fields too, in case you might want them.

IFS=: read user x uid gid gecos hm sh < <( getent passwd $USER )
name=${gecos%%,*}
echo "$name"

You could also scan the whole /etc/passwd file. This works in plain Bourne shell, in 1 process, but not so much with LDAP or what.

while IFS=: read user x uid gid gecos hm sh; do
  name=${gecos%%,*}
  [ $uid -ge 1000 -a $uid -lt 60000 ] && echo "$name"
done < /etc/passwd

On the other hand, using tools is good. And C is good too.

Sam Watkins
  • 7,819
  • 3
  • 38
  • 38
0

The way that I figured it on Linux to get the full name into a variable was:

u_id=`id -u`
uname=`awk -F: -vid=$u_id '{if ($3 == id) print $5}' /etc/passwd`

Then just simple use the variable, ex: $ echo $uname

-1

Take 1:

$ user_name=sshd
$ awk -F: "\$1 == \"$user_name\" { print \$5 }" /etc/passwd
Secure Shell Daemon

However, passwd database supports special character '&' in the gecos, which should replaced with capitalized value of user name:

$ user_name=operator
$ awk -F: "\$1 == \"$user_name\" { print \$5 }" /etc/passwd
System &

Most of answers here (except for finger solution) do not respect &. If you want to support this case, then you'll need a more complicated script.

Take 2:

$ user_name=operator
$ awk -F: "\$1 == \"$user_name\" { u=\$1; sub(/./, toupper(substr(u,1,1)), u);
    gsub(/&/, u, \$5); print \$5 }" /etc/passwd
System Operator
abufct
  • 71
  • 4
-2

The good old finger may also help :-)

finger $USER |head -n1 |cut -d : -f3
H.-Dirk Schmitt
  • 1,159
  • 8
  • 14