4

I'm using the following code to find the duplicate username. However, it gives an error.

#!/bin/bash
cat /etc/passwd | cut -f1 -d":" | /bin/sort -n | /usr/bin/uniq -c |\
    while read x; do [ -z "${x}" ] && break set - $x
        if [ $1 -gt 1 ]; then
            uids=`/bin/gawk -F: '($1 == n) { print $3 }' n=$2 \
            /etc/passwd | xargs`
            echo "Duplicate User Name ($2): ${uids}"
        fi
    done

I'm facing a syntax error near the token 'done' and numeric error. How can I fix this error?

  • What do you mean by "numberic error"? "numeric error"? – Peter Mortensen Jul 31 '16 at 11:37
7
$ cut -d: -f1 /etc/passwd | sort | uniq -d

This will extract the first field (the usernames) of the :-delimited /etc/passwd-file, sort the result and report any duplicates.

To also get the UID and the rest of the duplicated passwd entries:

cut -d: -f1 /etc/passwd | sort | uniq -d |
while read -r username; do
  grep "^$username:" /etc/passwd
done

To only get the duplicate usernames and their UID:

cut -d: -f1 /etc/passwd | sort | uniq -d |
while read -r username; do
  awk -F: -vu="$username" '$1 == u { print $1, $3 }' /etc/passwd
done

A short note on your script. The syntax looks mostly ok, but you need ; after break and there is a space after both \ (this may be a cut-and-paste error (now removed by an edit)). Also, I'd avoid giving full paths to standard utilities if there is no good reason for it, and the awk program does not require GNU awk so just awk will do.

7
  1. You need to have a do somewhere between the while and the done — typically, right after the read, or after the check that you got data.
  2. set - $x should be on a line by itself, or at least separated from the break with a semicolon (;).  (This is probably a good place to put the do.)

Suggestions:

  1. Rather than doing set - $x, consider changing read x to read count name.
  2. Just for clarity, you might want to change `…` to $(…) — see this, this, and this.
  • +1 for actually addressing the OP's script (which I did not do). There is a do after the while though. – Kusalananda Jul 31 '16 at 10:31
3

I would do something like that with awk (one-liner):

awk -F: '{if ($1 in users) print "Duplicate Username: "$1 ; else users[$1]}' /etc/passwd

find username in users array variable, if duplicate print msg else add the user to the array

  • (1) If a username appears a ridiculous number (e.g., 42) of times in the password file, your command will print that name 41 times.   (2) While not stated as a requirement, the OP’s current code reports all the UIDs mapped to the duplicated usernames in the password file.  As long as you’re handling every line in the file, and splitting it into fields, that isn’t hard to do — I’ve posted an example. – G-Man Says 'Reinstate Monica' Aug 2 '16 at 1:14
0

Inspired by ebal’s answer, this single awk command replicates the intended functionality of the OP’s script:

awk -F: '{ uids[$1] = uids[$1] " " $3; }
         END { for (u in uids) { if (uids[u] ~ /. /)
                         print "Duplicate User Name (" u "):" uids[u]; } }'

As in ebal’s answer, it creates an associative array, with an entry for each unique user name.  It sets the value of each entry to the concatenation of a space and the UID for each line for the user.  Then, after it’s read the entire file, it loops through the unique user names and checks whether it occurs more than once — recognized by the fact that the UID list has a space as other than the first character — and reports the desired information.

This will behave oddly if the UIDs in the file contain spaces.

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.