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?
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?
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
| sort -u.awk command.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
&& does have a lower precedence than >= and <.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
if is superfluous...just do '$3 >= min && $3 <= max{print $1}'.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[@]}
}
bash4, so is not a very portable solution.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.
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.