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?
Best Answer
One common way to do this is to test if the program you want exists and is available from your
PATH
. For example:Because POSIX
id
doesn't support UID arguments, theelif
clause forid
has to test not only whetherid
is in the PATH, but also whether it will run without error. This means it may runid
twice, which fortunately will not have a noticeable impact on performance. It is also possible that bothid
andawk
will be run, with the same negligible performance hit.BTW, with this method, there's no need to store the output. Only one of them will be run, so only one will print output for the function to return.