To show some of the basic components of shell scripting, we are going to take a common task, creating a new user, and design a script to help automate this process. Original Script Display commands to manually creating an account Program: makeuser Usage: makeuser Purpose: To automate the creation of a user account History: Created Edit system files echo "passwd:" echo "smo1:x:1000:100:scott1:/root/n321/home/smo1:/bin/bash" echo "shadow:" echo "smo1:*:::::::" Create home directories echo "cp -r /etc/skel /root/n321/home/smo1" echo "chown -R 1000:100 /root/n321/home/smo1" echo "chmod -R go-rwx /root/n321/home/smo1" done
Revision 1 Write to temporary data files and directories. Never write to live (production) areas when debugging scripts. Note: code segments that are in bold indicate changes from the previous example. Program: makeuser Usage: makeuser Purpose: To automate the creation of a user account History: Created Wrote to local files Edit system files echo "smo1:x:1000:100:scott1:/root/n321/home/smo1:/bin/bash" >> passwd echo "smo1:*:::::::" >> shadow Create home directories cp -r /etc/skel /root/n321/home/smo1 chown -R 1000:100 /root/n321/home/smo1 chmod -R go-rwx /root/n321/home/smo1 done
Revision 2: As scripts become more complex, it is sometimes difficult to find all the occurrences of hard coded data (i.e. file names) when you need to make a change. A way to minimize this is to create a number of global variables at the beginning of the script for those items. You only need to make the change in one place. Program: makeuser Usage: makeuser Purpose: To automate the creation of a user account History: Created Wrote to local files Added global variables Account Variables USERNAME=smo2 PASSWORD="*" UUID=1002 UGID=100 GCOS=Scott2 HOME=/root/n321/home/smo2 SHELL=/bin/bash File variables PASSWD=passwd SHADOW=shadow SKEL=/etc/skel Edit system files echo "$USERNAME:x:$UUID:$UGID:$GCOS:$HOME:$SHELL" >> $PASSWD echo "$USERNAME:*:::::::" >> $SHADOW Create home directories cp -r $SKEL $HOME chown -R $UUID:$UGID $HOME chmod -R go-rwx $HOME done
Revision 3: Thus far, we have hard coded user account information directly into the script. Unfortunately, every time we need to create a new user, we would have to edit the script. A better approach would be to be able to include key pieces of information directly on the command line. Program: makeuser Usage: makeuser <username> <uid> <gcos> Purpose: To automate the creation of a user account History: Created Wrote to local files Added global variables Added command line arguments Account Variables USERNAME=$1 PASSWORD="*" UUID=$2 UGID=100 GCOS=$3 HOME=/root/n321/home/$USERNAME SHELL=/bin/bash File variables PASSWD=passwd SHADOW=shadow SKEL=/etc/skel Edit system files echo "$USERNAME:x:$UUID:$UGID:$GCOS:$HOME:$SHELL" >> $PASSWD echo "$USERNAME:*:::::::" >> $SHADOW Create home directories cp -r $SKEL $HOME chown -R $UUID:$UGID $HOME chmod -R go-rwx $HOME done
Revision 4: While script variables tend to be used as strings, sometimes it is helpful to treat them as integers so that we can basic mathematical operations on them (i.e. loop counters). It is also convenient to be able to set variables to the output of a UNIX command line. Program: makeuser Usage: makeuser <username> <gcos> Purpose: To automate the creation of a user account History: Created Wrote to local files Added global variables Added command line arguments Automatically generate UID Account Variables USERNAME=$1 PASSWORD="*" UGID=100 GCOS=$2 HOME=/root/n321/home/$USERNAME SHELL=/bin/bash File variables PASSWD=passwd SHADOW=shadow SKEL=/etc/skel Get new UID LUID=`tail -1 $PASSWD cut -d: -f3` UUID=`expr 1 + $LUID` Edit system files echo "$USERNAME:x:$UUID:$UGID:$GCOS:$HOME:$SHELL" >> $PASSWD echo "$USERNAME:*:::::::" >> $SHADOW Create home directories cp -r $SKEL $HOME chown -R $UUID:$UGID $HOME chmod -R go-rwx $HOME done
Revision 5: Assuming we enter everything correctly, our script should create new user accounts. What happens if we make a mistake on the command line? Depending on what we type, we could create a lot of problems. A way to minimize this is to add code to make sure there are no errors. This revision shows an initial example. Program: makeuser Usage: makeuser <username> <gcos> Purpose: To automate the creation of a user account History: Created Wrote to local files Added global variables Added command line arguments Automatically generate UID Added some error checking Enough Arguments? if [ $ -ne 2 ] echo "Usage: $0 <username> <Full name>" else echo "Creating account for $2" fi Account Variables USERNAME=$1 PASSWORD="*" UGID=100 GCOS=$2 HOME=/root/n321/home/$USERNAME SHELL=/bin/bash File variables PASSWD=passwd SHADOW=shadow SKEL=/etc/skel Get new UID LUID=`tail -1 $PASSWD cut -d: -f3` UUID=`expr 1 + $LUID` Edit system files
echo "$USERNAME:x:$UUID:$UGID:$GCOS:$HOME:$SHELL" >> $PASSWD echo "$USERNAME:*:::::::" >> $SHADOW Create home directories cp -r $SKEL $HOME chown -R $UUID:$UGID $HOME chmod -R go-rwx $HOME done
Revision 6: Script output has already been addressed in the first version of our script but what if the script needs some information from the user running it that wasn t provided on the command line. There needs to be a method to enable a user to enter it if prompted. In this revision, we had a new command line option, -v, that, if present, will prompt the user to verify that the new account should be created. Also introduced in this revision is code that allows us to different things based on a range of conditions. Program: makeuser Usage: makeuser [-v] <username> <gcos> Purpose: To automate the creation of a user account History: Created Wrote to local files Added global variables Added command line arguments Automatically generate UID Added some error checking Add verification before creating account Enough Arguments? case $ in 2) 3) *) USERNAME=$1 GCOS=$2 if [ $1!= "-v" ] echo "Usage: $0 <username> <Full name>" else echo -n "Are you sure? (Y N): " read ANSWER if [ $ANSWER!= "Y" -a $ANSWER!= "y" ] exit 3 fi fi USERNAME=$2 GCOS=$3 echo "Usage: $0 <username> <Full name>"
esac Account Variables PASSWORD="*" UGID=100 HOME=/root/n321/home/$USERNAME SHELL=/bin/bash File variables PASSWD=passwd SHADOW=shadow SKEL=/etc/skel Get new UID LUID=`tail -1 $PASSWD cut -d: -f3` UUID=`expr 1 + $LUID` Edit system files echo "$USERNAME:x:$UUID:$UGID:$GCOS:$HOME:$SHELL" >> $PASSWD echo "$USERNAME:*:::::::" >> $SHADOW Create home directories cp -r $SKEL $HOME chown -R $UUID:$UGID $HOME chmod -R go-rwx $HOME done
Revision 7: The final revision of our code provides a additional error checking if the script is run with the create user verification option. In the previous revision, the account is only created if Y or y is entered. If anything else is typed, it is assumed that it was the same as typing N or n. In this revision, we want to make sure that only yes or no responses are entered. Program: makeuser Usage: makeuser [-v] <username> <gcos> Purpose: To automate the creation of a user account History: Created Wrote to local files Added global variables Added command line arguments Automatically generate UID Added some error checking Add verification before creating account Enough Arguments? case $ in 2) 3) USERNAME=$1 GCOS=$2 if [ $1!= "-v" ] echo "Usage: $0 <username> <Full name>" else while [ 1 ] do echo -n "Are you sure? (Y N): " read ANSWER case $ANSWER in Y y) break N n) exit 3 *) esac done
esac *) fi USERNAME=$2 GCOS=$3 echo "Usage: $0 <username> <Full name>" Account Variables PASSWORD="*" UGID=100 HOME=/root/n321/home/$USERNAME SHELL=/bin/bash File variables PASSWD=passwd SHADOW=shadow SKEL=/etc/skel Get new UID LUID=`tail -1 $PASSWD cut -d: -f3` UUID=`expr 1 + $LUID` Edit system files echo "$USERNAME:x:$UUID:$UGID:$GCOS:$HOME:$SHELL" >> $PASSWD echo "$USERNAME:*:::::::" >> $SHADOW Create home directories cp -r $SKEL $HOME chown -R $UUID:$UGID $HOME chmod -R go-rwx $HOME done
Now that we have a working makeuser script, we can create a second one called makeclass that will enable us to create a group of users at one time. This script will read a file and parse it line by line pass the appropriate arguments to makeuser. The format of the input file is username:gcos. Original Script: We can pass the output of a command that can be used to feed a for loop. Program: makeclass Usage: makeclass <user-file> Purpose: Create a group of users History: Created USERFILE=$1 if [ $ -ne 1 ] echo "Usage: $0 <user-file-list>" elif [! -f $USERFILE ] echo "$USERFILE not found" exit 2 fi for NEWUSER in `cat $USERFILE` do NEWUSERNAME=`echo $NEWUSER cut -d: -f1` NEWGCOS=`echo $NEWUSER cut -d: -f2` done./makeuser8 $NEWUSERNAME $NEWGCOS done
Revision 1: One problem with the original approach is that any spaces on a line will be seen as an item separator. While this is exactly what we want when parsing most commands, it is not necessarily good when dealing with files. This revision includes a method to specifically read a file for input. Program: makeclass Usage: makeclass <user-file> Purpose: Create a group of users History: Created USERFILE=$1 if [ $ -ne 1 ] echo "Usage: $0 <user-file-list>" elif [! -f $USERFILE ] echo "$USERFILE not found" exit 2 fi while read NEWUSER do NEWUSERNAME=`echo $NEWUSER cut -d: -f1` NEWGCOS=\"`echo $NEWUSER cut -d: -f2`\"./makeuser8 "$NEWUSERNAME \"$NEWGCOS\"" done < $USERFILE done