Is there any command that can find the number of users that are not logged into the system ?
I am trying to write a shell script that counts the number of users who are logged in and number of users that are not logged in.
Is there any command that can find the number of users that are not logged into the system ?
I am trying to write a shell script that counts the number of users who are logged in and number of users that are not logged in.
You can get a list of all your local users with this command:
$ getent passwd | awk -F: '{print $1}'
NOTE: getent
will return local users assuming you do not have sssd
(or some similar service running which pulls LDAP users in too) and your /etc/nsswitch.conf
is restricted to files, i.e. it's not including things like NIS or NIS+. For pure local user's only you can resort to awk -F: '{print $1}' /etc/passwd
.
A list of who's currently logged in:
$ who
A list of user's that are currently not loggd in:
$ grep -Fxvf <(who | awk '{print $1}' | sort -u) \
<(getent passwd | awk -F: '{print $1}')
This last one takes the list of users who are logged in and shows the list of all user's minus the logged in users, using grep -vf
.
To get counts, simply take a wc -l
on to the end of commands.
$ grep -Fxvf <(who | awk '{print $1}' | sort -u) \
<(getent passwd | awk -F: '{print $1}') | wc -l
53
logged in
$ who | awk '{print $1}' | sort -u | wc -l
1
-F, --fixed-strings
Interpret PATTERN as a list of fixed strings, separated by newlines,
any of which is to be matched.
-x, --line-regexp
Select only those matches that exactly match the whole line.
-v, --invert-match
Invert the sense of matching, to select non-matching lines.
-f FILE, --file=FILE
Obtain patterns from FILE, one per line. The empty file contains
zero patterns, and therefore matches nothing.
You could also forgo using grep
and use join
instead, since this type of problem is more in join
's wheelhouse. We'll need to use join -v 2
which means that we want to exclude matches and only show the uniques from our second argument, getent ...
.
-a FILENUM
also print unpairable lines from file FILENUM, where FILENUM is
1 or 2, corresponding to FILE1 or FILE2
-v FILENUM
like -a FILENUM, but suppress joined output lines
$ join -v 2 <(who | awk '{print $1}' | sort -u) \
<(getent passwd | awk -F: '{print $1}' | sort) | wc -l
53
NOTE: The only caveat with using join
is that both lists need to be sorted, so we have to add on a | sort
to getent ...
.
getent
, you need to make sure that /etc/nsswitch.conf
is also only restricted to files, if you truly want local files. I'll add this as a note in my A, thanks again.
– slm♦
Dec 6 '14 at 15:13
/etc/passwd
only option to show it. I hate to add that since it's generally discouraged to work against those files directly 8-)
– slm♦
Dec 6 '14 at 15:20
getent
does not provide some switch for this. I searched but did not find one.
– slm♦
Dec 6 '14 at 15:21
You may want to write a script that will store the number of users currently logged in a file and the total users in another file and do a comm
over them.
For example:
#!/bin/bash
w | awk 'NR > 2 {print $1}' | sort > logged.txt #sorted list of logged users
awk -F':' '{ print $1}' /etc/passwd | sort > allusers.txt #sorted list of all users
comm -23 allusers.txt logged.txt #find lines unique to allusers.txt
This one-liner will work:
grep -Ev $(users | sed 's/ /|/g') /etc/passwd | awk -F: {'print $1'}
How it works:
The output of users | sed 's/ /|/g'
will give you a |
separated list of logged in users.
The -E
option with grep will allow us to use more than one string (in OR condition) to match. The -v
option will print out what does not match given strings/pattern. Combining -E and -v
together will list all the lines that does not contain the pipe-separated strings from the output of users | sed 's/ /|/g'
. At the end use awk
to filter out only the usernames.