1

This script takes a .txt file with four columns- which contains LastName FirstName MiddleInitial Group-as an argument and needs to create a unique username and password for each person; and then assign each user the appropriate directory depending on their group: i.e. If "John Doe" is in "mgmt" group, and his username is jdoe1234, then his directory would be /home/mgmt/jdoe1234. It should then generate a .txt file which contains the following columns- LastName FirstName UID(userid) Password- .

I have the following:

#!/bin/bash
IFS=$'\n';
for i in `cat $1`;
do
    last=`echo $i|cut -f 1 -d ' '`;
    first=`echo $i|cut -f 2 -d ' '`;
    middle=`echo $i|cut -f 3 -d ' '`;
    groups=`echo $i|cut -f 4 -d ' '`;
    r=$(( $RANDOM % 10 ));
    s=$(( $RANDOM % 10 ));
    y=$(( $RANDOM % 10 ));
    username=`echo $first| head -c 1 && echo $last| head -c 3 && echo $r$s$y`
    echo $username
done
#check if group exists, if not then create one
for group in ${groups[*]}
do
    grep -q "^$group" /etc/group ; let x=$?
    if [ $x -eq 1 ]
    then
            groupadd "$group"
    fi
done

#try to add user to correct group
x=0
created=0
for user in ${username[*]}
do
    useradd -n -g "{groups[$x]}" -m $user 2> /dev/null
done

I want the username to contain: 1st letter of firstName, first 3 letters of the lastName, the middle initial, and then 3 randomly generated numbers. So not exactly the same as the above example with John Doe but similar. It can't be any more than 8 characters. I'm not sure if I'm creating the usernames properly.

Of course, I'm having trouble with the password too; not sure if it needs to be created alongside the username or after.

After the first 'for loop' I first try to add a group if it doesn't already exist, and then I make an attempt at putting the usernames into the correct groups. I got the syntax off a Youtube video but he was working with it as arrays and I'm not sure if I'm doing that or not.

If it helps, let's say the .txt file contains:

doe john a mgmt
lee amy f temp
smith tracy s empl

If you have time, any help at all would be appreciated. Thank you.

Jay
  • 309
  • 2
  • 5
  • 11
  • Requiring people to have a middle initial is an incredibly silly faux pas. If you have a lot of spare time, http://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/ has a more detailed discussion. – tripleee Mar 10 '15 at 13:47
  • Putting groups in an array suggests that there can be multiple groups, but your other requirements seem to be based on a single group per user. Which way is it? – tripleee Mar 10 '15 at 13:51

2 Answers2

1

Depending on which distro you are using, there are a few alterations to your script which will help. I have not had time to actually test this, so use with caution:

#!/bin/bash

# This will loop through the argumented txt file, and create the users as necessary.
# You will need to run this as root, so be very careful. Start with a small user file.

line=1 # Initiate variable for counting.
count="$(cat $1 | wc -l)"  # This counts the number of lines, which will decide how many times the loop itterates.
until [ $line -gt $count ]
do
    # Begin by grabbing one line (user to be added)
    newuser="$(head -$line $1 | tail -1)" # This gets just the one line at a time.
    # Now split the data as needed according to your original post:
    fname="$(echo $newuser | awk '{print $2}')" # AWK to get First Name
    lname="$(echo $newuser | awk '{print $1}')" # AWK to get Last Name
    minit="$(echo $newuser | awk '{print $3}')" # AWK to get middle initial
    group="$(echo $newuser | awk '{print $4}')" # AWK to get Group
    # Now create the random username.
    initial="$(echo $fname | head -c 1)"
    random="$(shuf -i 1-9 -z -n 3)"
    shortname="$(echo $lname | head -c 3)"
    username="$initial$shortname$random" # This will output exactly what is needed, although your example does not stick to what you want...
    # Now create the user.
    # Does group exist?
    if egrep -i "^$group" /etc/group
    then
        true # This is not the best way to do this, but my toddler kept me up all night...
    else
        groupadd $group
    fi
    if [ ! -d "/home/$group" ]
    then
        mkdir -m 774 /home/$group
    fi
    # Make the home dir.
    mkdir -m 777 /home/$group/$username

    # Actual useradd command
    useradd -g $group -d /home/$group/$username -p $(openssl passwd -1 $username) $username
    if [ $? = 0 ]
    then
       echo "User $username has been successfully created."
       chown $username:$group /home/$group/$username
       chmod 755 /home/$group/$username
    else
       echo "Something went wrong, user $username was NOT created."
       rm /home/$group/$username
    fi

    # Now generate line for txt output file
    output="$lname $fname $(id -u $username) $username"
    printf "\n$output" >>./output.txt
    # Now increment the counter
    line="$((line +1))"
done

exit

Like I said in the beginning, I haven't tested this properly, because I don't really want to create a bunch of users :-) so be careful - test the useradd line first. The rest should be solid.

asimovwasright
  • 838
  • 1
  • 11
  • 28
  • Also please note, that the password creation like this is not very safe. I made it create the password as the username - which should be changed. Creating any passwords inside a shell script like this is dangerous and easily eaves dropped on. – asimovwasright Mar 10 '15 at 11:54
  • Repeatedly running `head -n x | tail -n 1` on the file is really not the sane way to read a line at a time from a file. – tripleee Mar 10 '15 at 13:58
  • Ye, this is not the neatest script, and is using way more processor than necessary, but then again, it probably doesn't matter that much, seems the guy just needs it to work. But you are right, there are much more sane ways to do this. – asimovwasright Mar 10 '15 at 14:45
  • Thanks, I'll try this too as well as the other answer. I appreciate your help. As I said in my comment to the other answer, I'm not caring much about functionality as much as I am about getting it to work- so you assumed right. It's for an assignment and I'm going to test this on a local server where I can be the root user. – Jay Mar 10 '15 at 16:07
  • So I tried this at the local lab, and I'm pretty sure it worked! It seems the right groups were added (/home/empl/___ , /home/temp/___, /home/mgmt/____) with the correct usernames added. The output file you gave does not contain either a UID of each user or their password. How do I print the UID and passwords generated for each user? I noticed when I tried to print the UID of any of the usernames generated with 'id -u ' , it says that no user exists even though it clearly was created. – Jay Mar 10 '15 at 20:24
  • Sounds like the users were not really added then. Can you tell me what distro you are using in the lab? I will try to taylor the useradd command. And I can show you how to place conditional tests to debug. – asimovwasright Mar 10 '15 at 20:41
  • CentOS 6...sorry I my original comment was made on the wrong server. The distro is CentOS 6 – Jay Mar 10 '15 at 22:46
  • Ok, I have adapted the script to have more chance of actually working on CentOS - I have removed the -s option, which tells the useradd command which shell to assign the user, and in CentOS will not be necessary, as BASH is the default anyway. I removed -k - since I doubt you are using a skeleton in this assignment. I also placed a conditional test to check if the usseradd was successful each time, and echo to stdout something to say so. The test [ $? = 0 ] is very handy and can be used almost everywhere. $? is a variable which always holds the exit status of the preceding command, 0=success! – asimovwasright Mar 12 '15 at 07:50
  • I also changed the -p option, which I had not done correctly the first time. :-) There is a chance that it still won't work, so what you can do is place a -x after the shebang `#!/bin/bash -x` and redirect the stdout to a debug-file like, `$ sudo ./script.sh argument.txt >debug-file.txt` then share the contents of that file with us, and we should be able to isolate exactly what needs to change. – asimovwasright Mar 12 '15 at 07:56
  • I appreciate your help, @asimovwasright . With a few adjustments, I got this script to work exactly how I wanted it to. It was a tough assignment to say the least. You are smart. – Jay Mar 30 '15 at 01:09
  • @Jay You're most welcome! Could you accept the answer so I can be awarded for my smarts :-) – asimovwasright Mar 30 '15 at 07:30
1

Your syntax is rather clunky, I'm afraid. Refactoring to avoid dozens of superfluous external processes should also make the script more readable and maintainable, although you need to understand the new constructs.

Instead of doing a for loop over the output of cat, the usual idiom to read a file line by line is to use while read ...; do ...; done <file and this also buys you the significant simplification that read will split the input into tokens for you.

Instead of calling $RANDOM three times, it would seem a lot more straightforward to call it once with a modulo of 1000 and add leading zeros if necessary.

And no, your arrays were not working right, but you don't even really need arrays here -- just do the stuff you want to do inside the main loop for each user.

As ever, you should properly quote every string unless you specifically require the shell to perform wildcard expansion and token splitting on the value.

I also took the liberty to fix the grep; if [ $? = 1 ]; then... to just if ! grep; then... which is both simpler and more idiomatic, as well as more readable. But then we should not use grep for examining passwords, so I replaced that with getent instead. The construct getent || groupadd is basically shorthand for if ! getent; then groupadd; fi.

You were redirecting standard error from useradd to /dev/null but I took that away -- if there is a failure, you need to see the error message; otherwise you could spend hours debugging an error which would be obvious if you knew what's wrong. (We see that here on StackOverflow a lot more than we should.)

One last note -- Bash has a simple built-in syntax for substring extraction; ${string:0:3} extracts a substring of length 3 at offset 0. Similarly, ${string//foo/bar} returns the value of string with all occurrences of foo replaced with bar.

#!/bin/bash
while read last first middle groups; do
    rsy=$(prinf '%03i' $(($RANDOM % 1000)))
    username="${first:0:1}${last:0:3}$middle$rsy"
    echo "$username"
    for group in $groups; do
      getent group "$group" >/dev/null || groupadd "$group"
    done
    password=$(LC_ALL=C tr -dc '!-~' </dev/urandom | head -c 14)
    enc=$(openssl passwd -1 "$password")
    useradd -n -G "${groups// /,}" -m "$username" -p "$enc" -d "/home/${groups%% *}/$username" #2> /dev/null
    # Print generated user's first, last, UID, and password
    echo "$first $last $(id -u "$username") $password"
done <"$1"

I have not attempted to enhance the useradd command -- as noted in the answer by @asimovwasright you probably need to do additional things to perform this properly. If you are on a Debian-based distro, you should look into adduser as a higher-level replacement which takes care of many of these chores for you.

The password creation is a bit of a crock. I adapted one of the answers from How to automatically add user account AND password with a Bash script? but it's probably not optimal from either a usability or a security standpoint. But then, you should really not create passwords anyway -- just create users without passwords, put their SSH public key in place, and let them log in that way.

(I used /dev/random at first but it was taking forever in my tests so I switched to /dev/urandom. I'm hoping you are making your users change their password first thing when they log in, so this should be an acceptable compromise.)

Community
  • 1
  • 1
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • This allows multiple groups per user, which is probably an unnecessary concession? I'm not throwing that out in case it turns out to be useful after all, but that part of the code could be simplified. – tripleee Mar 10 '15 at 13:51
  • Okay, much of this looks foreign to me but I'll give it a try. I appreciate your help. I should note that this is just a school assignment and won't be implemented in real world use. I just need to test it on a local server where I have root access whereas I've been working on the school Linux server. I haven't even gotten the chance to test the groups/user adding part. I'll get back to ya! – Jay Mar 10 '15 at 15:58