How do I extract the biggest UID value from /etc/passwd?

1k views Asked by At

I want to predict the next UID before creating a new user. Since the new one will take the biggest ID value yet and adds 1 to it, I thought of the following script:

biggestID=0
cat /etc/passwd | while read line
do
if test [$(echo $line | cut -d: -f3) > $biggestID]
then
biggestID=$(echo $line | cut -d: -f3)
fi
echo $biggestID
done
let biggestID=$biggestID+1
echo $biggestID

As a result I get 1. This confused me and I thought that the problem is with the loop, so I added the echo $biggestID just below fi to check if its value is truly changing and it turns out there is no problem with the loop as I got many values up to 1000. So why is biggestID's value returning to 0 after the loop?

3

There are 3 answers

1
e.dan On BEST ANSWER

It's because of this line:

cat /etc/passwd | while read line

That runs the while loop in a sub-shell, so biggestID is being set in the sub-shell, not in the parent shell.

If you change your loop to the following, it will work:

while read line
...
done < /etc/passwd

This is because the while loop is now running in the same shell as the main script, and you're just redirecting the contents of /etc/passwd into the loop.

0
Lars Fischer On

You could change the program to something like this:

newID=$(( $(cut -d: -f3 /etc/passwd | sort -n | tail -n 1 ) +1 ))
echo $newID
  • cut -d: -f3 /etc/passwd| sort -n | tail -n 1 fetches the biggest value from the third field in passwd
  • $( ... ) stands for the result of the command, here the biggest id
  • newID=$(( ... + 1 )) add 1 and stores the result in newID
0
Walter A On

With awk you do all calculations in one program:

awk -F: 'BEGIN {maxuid=0;} {if ($3 > maxuid) maxuid=$3;} END {print maxuid+1;}' /etc/passwd

When you not want to start with awk yet, some feedback on your code.

biggestID=0
# Do not use cat .. but while .. do .. done < input (do not open subshell)
# Use read -r line (so line is taken literally)
cat /etc/passwd | while read line
do
   # Do not calculate the uid twice (in test and assignment) but store in var
   # uid=$(cut -d: -f3 <<< "${line}")
   # Use space after "[" and before "]"
   # test is not needed, if [ .. ] already implicit says so
   # (I only use test for onelines like "test -f file || errorfunction")
   if test [$(echo $line | cut -d: -f3) > $biggestID]
   then
      biggestID=$(echo $line | cut -d: -f3)
   fi
   # Next line only for debugging
   echo $biggestID
done
# You can use (( biggestID = biggestID + 1 ))
# or (when adding one)
# (( ++biggestID ))
let biggestID=$biggestID+1
# Use double quotes to get the contents literally, and curly brackets
# for a nice style (nothing strang will happen if you add _happy right after the var)
# echo "${biggestID}" 
echo $biggestID