开发者

How to get unique `uid`?

I'm making a bash script which should create an ftp user.

ftpasswd --passwd --file=/usr/local/etc/ftpd/passwd --name=$USER --uid=[xxx]
     --home=/media/part1/ftp/users/$USER --shell=/bin/false

The only supplied argument to script is user name. But ftpasswd also requires uid. How do I get this number? Is there an easy way to scan passwd file and get the max number, increment it and use it? Maybe it's 开发者_开发问答possible to obtain that number from the system?


Instead of reading /etc/passwd, you can also do it in a more nsswitch-friendly way:

getent passwd

Also don't forget that there isn't any guarantee that this sequence of UIDs will be already sorted.


To get UID given an user name "myuser":

 cat /etc/passwd | grep myuser | cut -d":" -f3

To get the greatest UID in passwd file:

 cat /etc/passwd | cut -d":" -f3 | sort -n | tail -1


To get a user's UID:

cat /etc/passwd | grep "^$usernamevariable:" | cut -d":" -f3

To add a new user to the system the best option is to use useradd, or adduser if you need a fine-grained control.

If you really need just to find the smallest free UID, here's a script that finds the smallest free UID value greater than 999 (UIDs 1-999 are usually reserved to system users):

#!/bin/bash

# return 1 if the Uid is already used, else 0
function usedUid()
{
    if [ -z "$1" ]
    then
    return
    fi
    for i in ${lines[@]} ; do 
        if [ $i == $1 ]
        then
        return 1
    fi
    done
return 0
}

i=0

# load all the UIDs from /etc/passwd
lines=( $( cat /etc/passwd | cut -d: -f3 | sort -n ) )

testuid=999

x=1

# search for a free uid greater than 999 (default behaviour of adduser)
while [ $x -eq 1 ] ; do
    testuid=$(( $testuid + 1))
    usedUid $testuid
    x=$?
done

# print the just found free uid
echo $testuid


I changed cat /etc/passwd to getent passwd for Giuseppe's answer.

#!/bin/bash
# From Stack Over Flow
# http://stackoverflow.com/questions/3649760/how-to-get-unique-uid
# return 1 if the Uid is already used, else 0
function usedUid()
{
    if [ -z "$1" ]
    then
    return
    fi
    for i in ${lines[@]} ; do
        if [ $i == $1 ]
        then
        return 1
    fi
    done
return 0
}

i=0

# load all the UIDs from /etc/passwd
lines=( $( getent passwd | cut -d: -f3 | sort -n ) )
testuid=999

x=1

# search for a free uid greater than 999 (default behaviour of adduser)
while [ $x -eq 1 ] ; do
    testuid=$(( $testuid + 1))
    usedUid $testuid
    x=$?
done

# print the just found free uid
echo $testuid


This is a much shorter approach:

#!/bin/bash
uids=$( cat /etc/passwd | cut -d: -f3 | sort -n )
uid=999

while true; do
    if ! echo $uids | grep -F -q -w "$uid"; then
        break;
    fi

    uid=$(( $uid + 1))
done

echo $uid


since this is bash things could get simpler

#!/bin/bash
get_available_uid_basic(){
    local uid_free=1000
    local uids_in_use=( $(cut -d: -f3 < /etc/passwd) )

    while [[ " ${uids_in_use[@]} " == *" $uid_free "* ]]; do
        (( uid_free++ ))
    done
    echo $uid_free
}

uid=$(get_available_uid_basic)
echo $uid

Explanation:
uids_in_use is an array to get rid of new-line characters
there is no "| sort" and it's useless in other answers too
${uids_in_use[@]} is uids_in_use array exploded with spaces as separators
there are spaces before and after first and last array entry, so each entry is separated by spaces on each side
bash's [[ ]] accepts glob character after '=='


System/User uid and first/last available

useradd/adduser has --system argument, this creates user with uid between 100-999
also, useradd does look for available uid starting at 999 going downwards.
In my case I needed both of these behaviors, this is a function which accepts "system" and "reverse" arguments

#!/bin/bash
get_available_uid(){
    local system_range
    [[ $* == *system* ]] && system_range=TRUE
    local reverse
    [[ $* == *reverse* ]] && reverse=TRUE

    local step
    local uid_free

    if [ -n "$system_range" ]; then
        if [ -n "$reverse" ]; then
            uid_free=999
            step=-1
        else
            uid_free=100
            step=1
        fi
    else
        if [ -n "$reverse" ]; then
            uid_free=9999
            step=-1
        else
            uid_free=1000
            step=1
        fi
    fi

    local uids_in_use=( $(cut -d: -f3 < /etc/passwd) )

    while [[ " ${uids_in_use[@]} " == *" $uid_free "* ]]; do
        (( uid_free+=step ))
    done
    
    if [ -n "$system_range" ]; then
        if (( uid_free < 100 )) || (( uid_free > 999 )); then
            echo "No more available uids in range" >&2
            return 1
        fi
    else
        if (( uid_free < 1000 )); then
            echo "No more available uids in range" >&2
            return 1
        fi
    fi

    echo $uid_free
    return 0
}


uid=$(get_available_uid)
echo "first available user uid: $uid"

uid=$(get_available_uid system)
echo "first available system uid: $uid"

uid=$(get_available_uid reverse)
echo "last available user uid: $uid"

uid=$(get_available_uid system reverse)
echo "last available system uid: $uid"


0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜