1

I'm trying to write a script as a part of my registration form. Everything is set, however, I would like to implement a "UID" function similar to how UID works in the bash. However here's the tricky part: the UID that I would be implementing would increase by 1 every time a user is added and if that user deletes his / her account his account would be removed from the database thus removing the UID that was used for him. I would like the script that I would be making to check for the existing UIDs and if there's a discrepancy it would fill it.

Example

Name:UID

Apple:0001
Bag:0002
Cat:0003
Dog:0004

Cat deletes his account so the new list would be

Apple:0001
Bag:0002
Dog:0004

and if a new user creates an account it should be

Apple:0001
Bag:0002
NEWUSER:0003
Dog:0004

Everything is already fixed including the deletion of the account from the database I'm just having trouble implementing both the increment of UID and its ability to fill in missing numbers. Much help would be very much appreciated! Thank you so much!

Edit: I have a rough idea of what to use such as sed and cmp while putting them in a looping construct but not good enough to turn them into what I would want.

3
  • What are the reasons behind UID reuse? Commented Aug 25, 2017 at 10:52
  • Efficiency! Incrementing numbers infinitely as users register would be a waste of UID whenever someone deletes their account. Commented Aug 25, 2017 at 10:53
  • it's less effort to create a new id than it is to scan all the ids for an empty slot. Commented Aug 25, 2017 at 11:39

5 Answers 5

1

Maintain a list (an array) of free indexes an use it to find a free index if there is one:

declare -a free_indexes=()
...
if [[ action == "remove" ]]; then
    free_indexes=( ${free_indexes[@]} $removed_index )
elif [[ action == "add" ]]; then
    if [[ ${#free_indexes[@]} -ne 0 ]]; then
        index=${free_indexes[0]}
        unset free_indexes[0]
        free_indexes=( ${free_indexes[@]} )
    else
        index=next_index
    fi
fi
Sign up to request clarification or add additional context in comments.

1 Comment

That was awesome! I would try this and let you know of my progress. Thank you so much!
0

This will work no matter the order of the input file or how many gaps it has or how big those gaps are:

$ cat addUser.awk
BEGIN { FS=OFS=":"; min=max=1 }
NR==1 { print; next }
{
    uid = $2+0
    uid2user[uid] = $1
    min = (min > uid ? uid : min)
    max = (max < uid ? uid : max)
}
END {
    fmt = "%s" OFS "%04d\n"
    for (uid=min; uid<=max; uid++) {
        if (uid in uid2user) {
            printf fmt, uid2user[uid], uid
        }
        else if (newUser != "") {
            printf fmt, newUser, uid
            newUser = ""
        }
    }
    if (newUser != "") {
        printf fmt, newUser, uid
    }
}

.

$ cat users1
Name:UID
Cat:0003
Apple:0001
Dog:0004
Bag:0002

$ awk -v newUser="NEWUSER" -f addUser.awk users1
Name:UID
Apple:0001
Bag:0002
Cat:0003
Dog:0004
NEWUSER:0005

$ cat users2
Name:UID
Bag:0002
Dog:0004
Apple:0001

$ awk -v newUser="NEWUSER" -f addUser.awk users2
Name:UID
Apple:0001
Bag:0002
NEWUSER:0003
Dog:0004

$ cat users3
Name:UID

$ awk -v newUser="NEWUSER" -f addUser.awk users3
Name:UID
NEWUSER:0001

.

$ cat users4
Name:UID
Bag:0002
Dog:0004
Apple:0001
Frog:0006

$ awk -v newUser="NEWUSER" -f addUser.awk users4
Name:UID
Apple:0001
Bag:0002
NEWUSER:0003
Dog:0004
Frog:0006

$ cat users5
Name:UID
Bag:0002
Apple:0001
Frog:0006

$ awk -v newUser="NEWUSER" -f addUser.awk users5
Name:UID
Apple:0001
Bag:0002
NEWUSER:0003
Frog:0006

3 Comments

Thank you so much for this! I'll be using your addUser.awk however my database is far much more complicated than what I showed as an example and I'm having difficulties following your format. My database follows the format name:uid:encrypted password:full name:address:email add:datecreated when feeding my database to your script it seems to give me only 1 value of 0001 however when I tried just the way you showed it, it gave me the correct response i'm guessing it's just a matter of formatting within the addUser.awk please help! thank you!
Yep! I adjusted my script programs accordingly to your solution. Thank you so much once again! This has helped me a lot.
Already marked the answer correct! I'd upvote it only if my account is already allowed to but it's fairly new as well.
0

awk solution:

Let's say the crucial file is called users and has the following contents after Cat deletes his account:

Name:UID
Apple:0001
Bag:0002
Dog:0004

awk -v nuser="NEWUSER" -F':' 'NR>1 && uid && (int($2)-uid>1){ 
           $0=sprintf("%s:%04d\n%s",nuser,uid+1,$0) }
           { uid=int($2) }1' users > tmp$$ && mv tmp$$ users

users file after processing:

Name:UID
Apple:0001
Bag:0002
NEWUSER:0003
Dog:0004

Comments

0

Assuming that uids.txt exists

cat uids.txt
Apple:0001
Bag:0002
Dog:0004

You can use bash-fun lib to do your job in functional way :)

source <(curl -Ls https://raw.githubusercontent.com/ssledz/bash-fun/master/src/fun.sh)

next_id() {
  prepend $(tup 0 0) | cut -d':' -f2 \
  | map lambda a . 'echo $((a))' |  sort \
  | scanl lambda acc el . 'cnt=$(tupl $acc); cnt=$((cnt+1)); tup $cnt $el' \
  | filter lambda a . 'cnt=$(tupl $a); el=$(tupr $a); [[ $cnt -ne $a ]] && ret true || ret false' \
  | tupl | fold -w 1 | append 0 0 0 0 | take 4 | revers | join ''
}

echo NEWUSER:$(cat uids.txt | next_id) | cat - uids.txt |sort -t ':' -n -k2

You should in return get

Apple:0001
Bag:0002
NEWUSER:0003
Dog:0004

Here you can find my presentation about this lib: fun.sh

3 Comments

This looks like an easier approach I'll give this one a spin as well. Thanks!
Sick presentation man. That's good however i'm having difficulties understanding your solution i'll try and analyze it better. Thanks!
You can split the next_id function into smaller parts, that can help to better understand what is it doing :) Have a nice fun with fun.sh !
0

Considering like your Input_file is same as shown sample, so I have considered by your statement 2 scenarios here. Let's say we have following Input_file:

cat Input_file
Name:UID
Apple:0001
Bag:0002
Dog:0004
Billi:0016
ascs:0018
ewdwdwd:0022
qce:0023
ooidw:24
fewfewf:30

1st: When only 1 user named NEW_USER you have to enter everywhere then following may help you.

awk -v new="NEW_USER" -F":" '
NR==$2+1;
NR!=$2+1{
   q=NR-1;
   while(q<$2){
     q=sprintf("%04d",q);
     print new":"q;
     q++
};
   NR=q+1;
   print
}'  Input_file

Output will be as follows.

Name:UID
Apple:0001
Bag:0002
NEW_USER:0003
Dog:0004
NEW_USER:0005
NEW_USER:0006
NEW_USER:0007
NEW_USER:0008
NEW_USER:0009
NEW_USER:0010
NEW_USER:0011
NEW_USER:0012
NEW_USER:0013
NEW_USER:0014
NEW_USER:0015
Billi:0016
NEW_USER:0017
ascs:0018
NEW_USER:0019
NEW_USER:0020
NEW_USER:0021
ewdwdwd:0022
qce:0023
ooidw:24
NEW_USER:0025
NEW_USER:0026
NEW_USER:0027
NEW_USER:0028
NEW_USER:0029
fewfewf:30

2nd: Where I am considering that if you have number of users like new_user1, new_user2 etc etc(I am predicting from your statement where you have mentioned many missing users, in case you have multiple users to be updated in your sheet).

awk -v new="NEW_USER,NEW_USER1,NEW_USER2,NEW_USER3" -F":" 'BEGIN{
len=split(new, a,",")
}
NR==$2+1;
NR!=$2+1{
   q=NR-1;
   while(q<$2){
      q=sprintf("%04d",q);
      if(i==len){
        i=""
};
      print a[++i]":"q;
      q++
};
   NR=q+1;
   i="";
   print
}'   Input_file

Output will be as follows.

Name:UID
Apple:0001
Bag:0002
NEW_USER:0003
Dog:0004
NEW_USER:0005
NEW_USER1:0006
NEW_USER2:0007
NEW_USER3:0008
NEW_USER:0009
NEW_USER1:0010
NEW_USER2:0011
NEW_USER3:0012
NEW_USER:0013
NEW_USER1:0014
NEW_USER2:0015
Billi:0016
NEW_USER:0017
ascs:0018
NEW_USER:0019
NEW_USER1:0020
NEW_USER2:0021
ewdwdwd:0022
qce:0023
ooidw:24
NEW_USER:0025
NEW_USER1:0026
NEW_USER2:0027
NEW_USER3:0028
NEW_USER:0029
fewfewf:30

2 Comments

This actually helped me out in formulating my logic thanks! However what i'm looking forward to do is to assign a uid to unique usernames so no same username would be the same! (i've already have that fixed just assigning them uid's is the job that's left to be done.)
Glad that it helped you, could you please edit your question as any request in comment is not proper way. Please let me know once you edit it with example, could take a look then.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.