2

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.

4

Listing out groups of users

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.

Getting counts

To get counts, simply take a wc -l on to the end of commands.

not logged in
$ 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

grep flags

-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.

Using join instead of grep

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 ....

join man page
-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 will also list entries from LDAP, etc. Do those count as local? – muru Dec 6 '14 at 5:17
  • @muru - thanks for the feedback. I'm not sure that's true. I think that's the case only if sssd is running. In addition, by using 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
  • @muru - yeah I used that one as an example, but I'm aware of there being others. – slm Dec 6 '14 at 15:17
  • 1
    @muru - I also added a pure /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
  • @muru - I find it a bit annoying that getent does not provide some switch for this. I searched but did not find one. – slm Dec 6 '14 at 15:21
1

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
0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.