Robottinosino
Robottinosino

Reputation: 10882

Shell script to get list of defined users on Linux?

I put this together, but it sucks: (e.g. magic numbers in there, text parsing.. boo!)

awk -F: '{if($3 >= 1000 && $3 < 2**16-2) print $1}' /etc/passwd

What's the proper way to do this?

Upvotes: 9

Views: 10453

Answers (6)

tripleee
tripleee

Reputation: 189317

Here's a simplification of @StephenOstermiller's answer which gets it done with only two processes. I think it is easier to read, too (provided you are familiar with the awk NR==FNR idiom).

getent passwd |
awk 'NR==FNR { if ($1 ~ /^UID_(MIN|MAX)$/) m[$1] = $2; next }
{ split ($0, a, /:/);
  if (a[3] >= m["UID_MIN"] && a[3] <= m["UID_MAX"] && a[7] !~ /(false|nologin)$/)
    print a[1] }' /etc/login.defs -

The different split patterns in the two inputs is a bit of a wart; maybe you could fix that more elegantly somehow.

Upvotes: 1

Alex North-Keys
Alex North-Keys

Reputation: 4363

Here's another approach that only spawns one external program, getent (suggested by @AnsgarWiechers) so that both local and networked passwd databases will be used. This one reduces the number of forks to just one for getent itself. Its portability is limited somewhat by requiring bash4, however.

get_users ()
{ 
    local IFS=$' \t#'
    while read var val ; do
        case "$var" in 
            UID_MIN) min="$val" ;;
            UID_MAX) max="$val" ;;
        esac
    done < /etc/login.defs
    declare -A users
    local IFS=:
    while read user pass uid gid gecos home shell; do
        if (( min <= uid && uid <= max )) && [[ ! $shell =~ '/(nologin|false)$' ]]; then
            users[$user]=1
        fi
    done < <(getent passwd 2>/dev/null)
    echo ${!users[@]}
}

Upvotes: 2

Ansgar Wiechers
Ansgar Wiechers

Reputation: 200203

Extract the minimum and maximum user IDs from /etc/login.defs and then select the users with IDs between these margins from /etc/passwd:

UID_MIN=$(awk '/^UID_MIN/ {print $2}' /etc/login.defs)
UID_MAX=$(awk '/^UID_MAX/ {print $2}' /etc/login.defs)

awk -F: -v min=$UID_MIN -v max=$UID_MAX '$3 >= min && $3 <= max{print $1}' /etc/passwd

Upvotes: 2

Stephen Ostermiller
Stephen Ostermiller

Reputation: 25504

Some unix systems don't use /etc/passwd, or have users that are not specified there. You should use getent passwd instead of reading /etc/passwd.

My system also has users that are disabled and can lo longer login with their login command set to /bin/false or /usr/sbin/nologin. You probably want to exclude them as well.

Here is what works for me including arheops awk command and ansgar's code to get the min and max from login.defs:

getent passwd | \
grep -vE '(nologin|false)$' | \
awk -F: -v min=`awk '/^UID_MIN/ {print $2}' /etc/login.defs` \
-v max=`awk '/^UID_MAX/ {print $2}' /etc/login.defs` \
'{if(($3 >= min)&&($3 <= max)) print $1}' | \
sort -u

Upvotes: 13

arheops
arheops

Reputation: 15247

I am unsure why you do only > 1000, beacuase on redhat system it start from 500.

For me this awk script work ok:

awk -F: '{if(($3 >= 500)&&($3 <65534)) print $1}' /etc/passwd

Only uses with passwords:

awk -F: '{if(!(( $2 == "!!")||($2 == "*"))) print $1}' /etc/shadow 

Upvotes: 2

mart1n
mart1n

Reputation: 6203

So you're just trying to get a list of all users from /etc/passwd? If so, I believe this would be an easier solution:

cut -d":" -f1 /etc/passwd

Edit:

In case you only want a list of user-defined users (not the system users), you can use one of these:

grep -E ":[0-9]{4,6}:[0-9]{4,6}:" /etc/passwd | cut -d: -f1

^ This assumes your system uses 1000 and up for UID and GID for user-defined users

grep /home /etc/passwd | cut -d: -f1

^ This assumes every user-defined user has a home directory.

Other solutions depend on more detailed criteria and your system settings.

Upvotes: 1

Related Questions