A friend recently told me of a small problem he was asked to resolve:
«Find all duplicate UIDs in your /etc/passwd file. Print them along with the associated usernames. You may use shell, perl or python».
My first, almost instant, response was the following
awk -F: '{print $1,$3}' /etc/passwd | \ sort | uniq -c | \ awk '{if ($1 > 1) {print $3, $2}}'
But using the passwd sample below, I found a bug.
root:x:0:0:root:/root:/bin/bash root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh unbound:x:1001:1001:Un Bound,,,:/home/unbound:/bin/bash nsd:x:1002:1002:,,,:/home/nsd:/bin/bash nsd:x:1002:1002:,,,:/home/nsd:/bin/bash nsd:x:1002:1002:,,,:/home/nsd:/bin/bash insd:x:1002:1002:,,,:/home/nsd:/bin/bash
It misses the «insd» username, because it only prints out lines that contain the same uid *and* username.
So my second attempt was using perl
#!/usr/bin/perl open(F, "</etc/passwd"); while (<F>) { @line = split(/:/, $_); $accounts{$line[2]} .= "$line[0] "; # build a hash, with uid as the key } close(F); for $uid ( keys %accounts ) { @usernames = split(/ /, $accounts{$uid}); if ($#usernames > 0) { print "$uid @usernames\n"; } }
Now that’s more like it :-)
cut -d: -f3 /etc/passwd | sort | uniq -d
:^)
You don’t get all the points, your solution only prints the duplicate uid, not the usernames :-p
You may try again :-)
Ah, I didn’t see that bit. Fine then. Here’s the relevant addition:
cut -d: -f3 /etc/passwd | sort | uniq -d | sed ‘s/.*/:x:&:/’ | egrep -f – /etc/passwd | cut -d: -f1,3
And if you want to avoid duplicates in the final output (like multiple instances of nsd, in your example):
cut -d: -f3 /etc/passwd | sort | uniq -d | sed ‘s/.*/:x:&:/’ | egrep -f – /etc/passwd | cut -d: -f1,3 | sort -t: -k2 -n | uniq
At this point I’d probably prefer perl too, but this is more fun. ;^)
Yeah! I went for readability too..
Here is a perl one-liner that also does the same thing (found in Linux Gazette #85) !
A solution in Python:
A similar python version was what I came up with too:
This one prints on my laptop: