I often want to get the login name associated with a user ID and because it’s proven to be a common use case, I decided to write a shell function to do this. While I primarily use GNU/Linux distributions, I try to write my scripts to be as portable as possible and to check that what I’m doing is POSIX-compatible.
Parse /etc/passwd
The first approach I tried was to parse /etc/passwd
(using awk
).
awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
However, the problem with this approach is that the logins may not be local, e.g., user authentication could be via NIS or LDAP.
Use the getent
command
Using getent passwd
is more portable than parsing /etc/passwd
as this also queries non-local NIS or LDAP databases.
getent passwd "$uid" | cut -d: -f1
Unfortunately, the getent
utility does not seem to be specified by POSIX.
Use the id
command
id
is the POSIX-standardised utility for getting data about a user’s identity.
BSD and GNU implementations accept a User ID as an operand:
This means it can be used to print the login name associated with a User ID:
id -nu "$uid"
However, providing User IDs as the operand is not specified in POSIX; it only describes the use of a login name as the operand.
Combining all of the above
I considered combining the above three approaches into something like the following:
get_username(){
uid="$1"
# First try using getent
getent passwd "$uid" | cut -d: -f1 ||
# Next try using the UID as an operand to id.
id -nu "$uid" ||
# As a last resort, parse `/etc/passwd`.
awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
}
However, this is clunky, inelegant and – more importantly – not robust; it exits with a non-zero status if the User ID is invalid or does not exist. Before I write a longer and clunkier shell script that analyses and stores the exit status of each command invocation, I thought I’d ask here:
Is there a more elegant and portable (POSIX-compatible) way of getting the login name associated with a user ID?
getent
norid
will return anything past the first match; the only way to find them all is to enumerate all users, if the user database allows that. (Looking in/etc/passwd
works for users defined there, obviously.)/etc/passwd
and/etc/shadow
to test this scenario and verified that bothid
andgetent passwd
behave as you describe. If, at some stage, I end up using a system where a user has multiple names, I'll do the same as these system utilities and simply treat the first occurrence as the canonical name for that user.setuid(some_id)
, and there's no requirement thatsome_id
may be part of any user database. With such things as user namespaces on Linux this may turn to be a crippling assumption for your scripts.getpwuid()
function whichls
uses to translate UIDs to login names. Gilles’ answer is a more direct and efficient way of accomplishing this.