0

My /etc/passwd has a list of users in a format that looks like this:

username:password:uid:gid:firstname.lastname, somenumber:/...

Goal : I want to see only the first names and than sort them having the most common name appear first, 2nd most common appear 2nd etc....

I saw some solutions as to how to do the 2nd part, although they are relevant to working with a text file and not to reading from a map.

In regards to the first part, I really don't know how to approach this. I know that there are some solutions but don't really know how to do them.

5

One way to do it:

cut -d: -f5 /etc/passwd | \
    sed 's/\..*//' | \
    sort -i | \
    uniq -ci | \
    sort -rn
  • Great answer, but I think he'll be in need of using uniq without -i, since there should be difference between X and x in name, we only need --ignore-case option for sort as you've used. In addition, using the sed command you've added in your answer, seems irrelevant, if there is any reason, please explain. – FarazX Aug 9 '16 at 8:07
  • @FarazX Re: -i: John.Doe should be the same as john.doe. Re: sed: from the OP: I want to see only the first names. – Satō Katsura Aug 9 '16 at 8:13
  • Oh you're right, sorry I didn't notice. So voila! Thanks for your explanation, and your great way of using cut ;) – FarazX Aug 9 '16 at 8:14
  • cut + sed is too much sed '/\n/{P;d};s/:/\n/4;s/\./\n/;D' or sed 's/[^.]*:\(\w\+\).*/\1/' – Costas Aug 9 '16 at 8:23
  • @Costas Too much compared to what? For me, total time spent thinking about getting the 5th field portably with sed >> the time gained by not using cut. BTW, your second recipe assumes GNU sed (\w). – Satō Katsura Aug 9 '16 at 8:31
2

Using awk and sorting to have the most common name first:

awk -F: '{sub(/[.].*/, "", $5); a[$5]++} END{for (n in a)print a[n],n}' /etc/passwd | sort -nr

For a case-insensitive version:

awk -F: '{sub(/[. ,].*/, "", $5); a[tolower($5)]++} END{for (n in a)print a[n],n}' /etc/passwd | sort -nr

For those who prefer their commands spread over multiple lines:

awk -F: '
  {
    sub(/[.].*/, "", $5)
    a[$5]++
  }

  END{
    for (n in a)
      print a[n],n
  }
  ' /etc/passwd | sort -nr

How it works

  • -F:

    This makes : the field separator.

  • sub(/[.].*/, "", $5)

    This removes everything after the first period from field 5.

  • a[$5]++

    The count for the number of times this name has appeared is stored in associative array a. This increments the counter. For the case-insensitive version, this is replaced with a[tolower($5)]++.

  • END{for (n in a)print a[n],n}

    This prints the count and name for all the results that we have in array a.

  • sort -nr

    This sorts the output numerically in descending order.

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.