6

I'm trying to write a bash script that shows a user's name based on the uid the user provides :

#!/bin/bash

read -p "donner l UID" cheruid

 if [ $(grep -w $cheruid) -n ]
 then
    grep -w $cheruid /etc/passwd | cut -d ":" -f "1" | xargs echo "user is : "
else
    echo "user not found"

fi

when I execute this the terminal only shows the prompt message then stops working.

Am I missing something ?

  • I don’t know of a good shell command, but not all users will always be in /etc/passwd, to be sure you would have to use getpwent System library function instead (some script environments like Perl and Python offer a wrapper for it) 
    – eckes
     Jan 26, 2019 at 21:11

4 Answers   正确答案

14

With GNU id, you can do:

id -un -- "$cheruid"

That will query the account database (whether it's stored in /etc/passwd, LDAP, NIS+, a RDBMS...) for the first user name with that uid.

Generally, there's only one user name per uid, but that's not guaranteed, the key in the user account database is the username, not user id.

If you want to know all the user names for a given uid, you can do:

getent passwd | ID=$cheruid awk -F: '$3 == ENVIRON["ID"] {print $1}'

But that may not work for some account databases that are not enumerable (as sometimes the case for large LDAP-based ones).

5
$ printf 'User is %s\n' "$( getent passwd 1001 | cut -d : -f 1 )"
User is myself

(there is a myself user with UID 1001 on my system)

This would query the passwd database for the given UID, strip out the username from that response and use the result as an argument to printf. This would not work on macOS.

To catch unknown UIDs:

$ printf 'User is %s\n' "$( { getent passwd 1001 || echo 'UNKNOWN USER'; } | cut -d : -f 1 )"
User is myself
$ printf 'User is %s\n' "$( { getent passwd 1002 || echo 'UNKNOWN USER'; } | cut -d : -f 1 )"
User is UNKNOWN USER

As a script taking any number of UIDs on the command line (with slightly altered output):

#!/bin/sh

for uid do
    printf 'User with UID %d is %s\n' "$uid" \
        "$( { getent passwd "$uid" || echo 'NOT KNOWN'; } | cut -d : -f 1 )"
done

Testing (this would go through some service accounts' UIDs on my OpenBSD system):

$ sh ./script.sh {110..115}
User with UID 110 is _sndiop
User with UID 111 is NOT KNOWN
User with UID 112 is _syspatch
User with UID 113 is NOT KNOWN
User with UID 114 is NOT KNOWN
User with UID 115 is _slaacd
4

Help with the original shellscript

The test in the if statement is causing the problem. I suggest the following shellscript,

#!/bin/bash

read -p "donner l UID " cheruid

 if [ "$(grep -w "^$cheruid" /etc/passwd)" != "" ]
 then
    grep -w "$cheruid" /etc/passwd | cut -d ":" -f "1" | xargs echo "user is : "
else
    echo "user not found"
fi

Edit1: I added a ^ in the test to only look for matches at the beginning of the line.

Edit2: since : is a separator, you can remove it and everything after, and use the result directly in the echo line, if accepted in the test, and simplify the shellscript to

#!/bin/bash

read -p "donner l UID " cheruid

cheruid=${cheruid%%:*}
user=$(grep -wo "^$cheruid" /etc/passwd)

if [ "$user" != "" ]
then
    echo "user is : $user"
else
    echo "user not found"
fi

This helps you make a good bash shellscript.

Efficient and more general method to find user IDs with id

If you want an efficient way to check if a particular user name exists, <test-name>, you can use id, as indicated in another answer to your question by Stéphane Chazelas:

id -un -- <test-name>

for example

id -un -- mehdi

id will find not only user IDs stored in /etc/passwd but also those managed and stored in other ways, for example LDAP, which is common in professional server systems as commented by Matteo Italia.


If you want to scan all users

  • system users, in many linux systems with user numbers < 1000

  • 'human' users, in many linux systems with user numbers >= 1000

  • and assuming that all users have number < 2000 (modify if you need)

you can use the following bash one-liner, which uses a loop with id,

for ((i=0;i<2000;i++));do name=$(id -un $i 2>/dev/null);if [ $? -eq 0 ];then echo "$i: $name" ;fi;done

If you want to see only the 'human' users and assume that no user ID number is skipped (no deleted user), the following bash oneliner is very fast,

i=1000;while true;do name=$(id -un $i 2>/dev/null);if [ $? -eq 0 ];then echo "$name" ;else break;fi;i=$((i+1));done

but it is more reliable to assume that some ID numbers may be skipped (deleted users),

for ((i=1000;i<2000;i++));do name=$(id -un $i 2>/dev/null);if [ $? -eq 0 ];then echo "$i: $name" ;fi;done

There are also users and who which print the user names of users currently logged in to the current host.

  • 1
    @mehdizidani, You are welcome, I'm glad that I could help :-) 
    – sudodus
     Jan 26, 2019 at 12:56
  • This will not work for any user not in /etc/passwd - for example, on pretty much any server in my company (and many others), where user login is centralized using LDAP.  Jan 26, 2019 at 22:12
  • @MatteoItalia, Yes, it is obvious because the solution is based on /etc/passwd. I helped by repairing and improving the OP's shellscript, and I think it is a solution that works for most home computer desktop/laptop systems, but, as you write, not for professional servers, where the user names are managed and stored in other ways. And I have mentioned 'a more efficient way' with id. I can add that id will also find user IDs that are stored in other ways into the answer. 
    – sudodus
     Jan 26, 2019 at 23:26 
  • Sorry but: Grep could not match a number at the start of a line in /etc/passwd. That is where a user name (in text) exists. 
    – user232326
     Jan 27, 2019 at 7:02
  • @Isaac, Is that a problem, when we are checking for a name? 
    – sudodus
     Jan 27, 2019 at 10:39 
0

Using grep means that any number will be matched. Either as a user id or group id:

$ grep '109' /etc/passwd
uuidd:x:105:109::/run/uuidd:/bin/false
usbmux:x:109:46:usbmux daemon,,,:/var/lib/usbmux:/bin/false

And it may also match 1109. Even if called as grep -w, which will avoid matching 1109. That is an important problem.


Use getent:

$ getent passwd 109
usbmux:x:109:46:usbmux daemon,,,:/var/lib/usbmux:/bin/false

Which will match only the numeric uid, not the gid (if given a number, and the name if given text) of the account database (either /etc/passwdLDAPNIS+, ...) for the first user entry found. Won't work on macOS.

So, the script could be:

#!/bin/bash
read -p "donner l UID: " cheruid

if     name=$(getent passwd "$cheruid")
then   name=${name%%:*}
       echo "user is : $name"
else   echo "user not found"
fi

no sed, awk, cut needed to get the name.

Your Answer


来自  https://unix.stackexchange.com/questions/496868/how-to-get-users-name-from-uid