3

I want to show the online users using the who command but I want to have a unique output and not show any duplicates. I piped the output into awk but I am not very familiar with that, is this the right way and how to proceed?

4

It's easier to just do it with sort:

 who | sort -u -k1,1

The -u flag asks for "unique" lines (suppress duplicates). The key flag (-k) says to only consider the first word in each line for purposes of sorting and uniqueness.

4

Using AWK

who | awk '!x[$1]++'

Command Break Down :

x[$1] is an associative array in an array whose index is a string. All arrays in AWK are associative. In this case, the index into the array is the first field of the "who" command, which is the username. If the user is repeated, the main loop increments the count per user by effectively executing.

What is the meaning of the preceding ! sign?

The NOT operator is exclusionary – it excludes username which is already in associative array.

  • What is the meaning of the preceding ! sign? – mkc Dec 8 '13 at 21:44
  • Same question thanks for your answer and your edit. – Phil_Charly Dec 9 '13 at 16:33
2

alexis' answer is probably the one you want. It's simple, short, and works. But in the interest of showing that there are many ways to skin a cat, I'd like to present an alternative:

who | awk '{print $1}' | uniq

This uses awk to print out only the first token from who, the username, and then uniq to merge repeated entries. There is a problem with this though: uniq only merges consecutive repeated terms. On my laptop I'm the only person using the machine, but if I run who -a to show all of the extra things, like login processes running on all the ttys, not all the instances of my user are listed right after each other. So, the list from who needs to be sorted. If we add another pipe to the command:

who | sort | awk '{print $1}' | uniq

This should do the trick.

Of course at this point we're using four programs to achieve what alexis already did with two. Besides, we're not even avoiding using the program sort that alexis used. If we want to have an alternate method that is truly an alternative, we can't use sort. Let's try using only awk, like the original poster seemed to be trying to do:

who | awk '
{
   array[$1]=$1
}
END{
   for(i in array) {
      print array[i]
   }
}
'

This little awk snippet puts all of the usernames into an array at a location indexed by the username itself. This ensures that if a username comes up twice, it'll just overwrite the same username that was already in that spot. Then it just prints out all the entries in the array, which is a unique list of all the usernames. To save space we could put it on one line:

who | awk '{array[$1]=$1} END {for(i in array) {print array[i]}}'

This is getting a little long, but it could use one more thing: The names really ought to be sorted. Let's run the array through an insertion sort so that the output is a little more predictable:

who | awk '
{
   # Read the usernames into an associative array
   array[$1]=$1
}
END{
   # Put the usernames into an indexed array and note the length
   len = 1
   for(j in array) {
      indexed[len] = array[j]
      ++len
   }

   # Sort the array
   for (i=2; i < len; ++i) {
      for (j=i; indexed[j-1] > indexed[j]; --j) {
            temp = indexed[j]
            indexed[j] = indexed[j-1]
            indexed[j-1] = temp
      }
   }

   # Print the unique usernames in order
   for(i=1; i< len; ++i) {
      print indexed[i]
   }
}
'

This reads in the usernames like before, but before printing them out, we move them to an indexed array and sort this indexed array. The output will be only the usernames (none of the extra information from who), sorted, with no duplicates. Something like this:

Betty
John
Ziggy
albert
james
nicola


Now, you can make the point that alexis and Rahul have provided one line answers, however, there's nothing to stop you from writing this on one line as well:

who | awk '{array[$1]=$1} END {len = 1; for(j in array) {indexed[len] = array[j]; ++len} for (i=2; i < len; ++i) { for (j=i; indexed[j-1] > indexed[j]; --j) {temp = indexed[j]; indexed[j] = indexed[j-1]; indexed[j-1] = temp }} for(i=1; i< len; ++i) {print indexed[i]}}'

Hope that helps.

  • Thanks very much for your explaining it means a lot to me for the awk command ,appreciate that thanks – Phil_Charly Dec 9 '13 at 16:32

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.