1

How can we write a script around dscl to loop over the currently listed IDs in use and then spit out the first id under 500 that is not in use yet?

Update # 1 (Feb 17th 2013)

I found some very helpful scripts on http://wiki.awkwardtv.org/wiki/Manage_users_and_groups_scripts which I was able to water down to the point where I could get the first available ID higher than a given number but I still don't have a way of scripting it to stop looking beyond a certain upper limit like 500.

#!/bin/sh
continue="no"
number_used="dontknow"
fnumber=300
user_id=0
until [ $continue = "yes" ] ; do
  if [ `dscl . -list /Users UniqueID | awk '{print $2, "\t", $1}' | sort -ug | grep -c "$fnumber"` -gt 0 ] ; then
    number_used=true
  else
    number_used=false
  fi
  if [ $number_used = "true" ] ; then
    fnumber=`expr $fnumber + 1`
  else
    user_id="$fnumber"
    continue="yes"
  fi
done;
echo "Next available user_id: $user_id"

Update # 2 (Feb 17th 2013)

I suppose I could work backwards but still what if every userid from 500 to 0 is taken? I still need to set a lowerbound to get out of a messy negative ID situation.

#!/bin/sh
continue="no"
number_used="dontknow"
fnumber_work_backwards_from=500
fnumber=$fnumber_work_backwards_from
user_id=0
until [ $continue = "yes" ] ; do
  if [ `dscl . -list /Users UniqueID | awk '{print $2, "\t", $1}' | sort -ug | grep -c "$fnumber"` -gt 0 ] ; then
    number_used=true
  else
    number_used=false
  fi
  if [ $number_used = "true" ] ; then
    fnumber=`expr $fnumber - 1`
  else
    user_id="$fnumber"
    continue="yes"
  fi
done;
echo "First available user_id which is closest to and lower than $fnumber_work_backwards_from: $user_id"
1
  • Did you see my answer when you updated the question? (I undeleted it, maybe it didn't show up)
    – slhck
    Feb 17, 2013 at 15:56

2 Answers 2

0

This can be done with Ruby, for example, which is much more concise than any Bash script you could possibly find for this.

dscl . -list /Users UniqueID | awk '{print $2}' | 
ruby -e 'puts ((0..500).to_a - STDIN.readlines.map(&:to_i)).first'

We simply subtract the actual IDs (as an array) from another array consisting of the numbers 0 through 500. This gives us all unused IDs in an array, and from that we take the first, since it's already sorted.

2
  • Not a language I can use in my infrastructure but you answered the Q :) Feb 18, 2013 at 15:46
  • Oh, I didn't know that, sorry. OS X comes with Ruby though, or can't you use it for some other reason? Note that going with a set-based approach is often easier than lower level stuff—problem domain thinking, so to say :)
    – slhck
    Feb 18, 2013 at 17:01
0

This should work to find what you need. The function find_next_userid below will search between two numbers, inclusively, to find the first unused userid. For your case, just use 0 and 499, shown below. The function returns an empty string if it couldn't find any empty slots, so you need to check for that before using the echoed value (shown below as well).

I realize this does not use dscl, but this should work and be more portable. [Update: Added version using dscl.]

Technically, I am using bash, so I have noted that; I'm not sure if would work in sh or not.

Tested on OS X 10.9.4.

#!/bin/bash
user_exists()
{
    username=$1

    if id -u $username >/dev/null 2>&1; then
        return 0
    else
        return -1
    fi
}

user_exists_dscl()
{
    username=$1

    user_found=`dscl . -search /Users UniqueID $username | awk 'FNR == 2 {print $1}'`
    if [ "${user_found}" != "" ] ; then
        return 0
    else
        return -1
    fi
}

find_next_userid()
{
    low=$1
    hi=$2

    for curr in `seq $low $hi`;
    do
        if ! (user_exists $curr) ; then
            echo $curr
            break
        fi
    done

    echo ""
}

FIRST_UNUSED_USERID=`find_next_userid 0 499`

if [ "$FIRST_UNUSED_USERID" != "" ]; then
    echo "Do your stuff here!"
else
    echo "No unused userid found!"
fi

Edit: Forgot to include user_exists(). Updated to note that this solution doesn't use dscl but still works. I think I might have a solution using dscl shortly.

Edit 2: Added user_exists_dscl. Replace calls to user_exists with user_exists_dscl if you really really need to use dscl instead of id.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .