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
#!/bin/bash
# Author: N321 Class # 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.
#!/bin/bash
# Author: N321 Class # 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.
#!/bin/bash
# Author: N321 Class # 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.
#!/bin/bash
# Author: N321 Class # Program: makeuser # Usage: makeuser
# 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.
#!/bin/bash
# Author: N321 Class # Program: makeuser # Usage: makeuser
# 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.
#!/bin/bash
# Author: N321 Class # Program: makeuser # Usage: makeuser
# Enough Arguments? if [ $# -ne 2 ] then echo "Usage: $0
# 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.
#!/bin/bash
# Author: N321 Class # Program: makeuser # Usage: makeuser [-v]
# Enough Arguments? case $# in 2) USERNAME=$1 GCOS=$2 ;; 3) if [ $1 != "-v" ] then echo "Usage: $0
USERNAME=$2 GCOS=$3 fi ;; *) echo "Usage: $0
# 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.
#!/bin/bash
# Author: N321 Class # Program: makeuser # Usage: makeuser [-v]
# Enough Arguments? case $# in 2) USERNAME=$1 GCOS=$2 ;; 3) if [ $1 != "-v" ] then echo "Usage: $0
done
USERNAME=$2 GCOS=$3 fi ;; *) echo "Usage: $0
# 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 then 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.
#!/bin/bash
# Author: N321 Class # Program: makeclass # Usage: makeclass
USERFILE=$1 if [ $# -ne 1 ] then echo "Usage: $0
./makeuser8 $NEWUSERNAME $NEWGCOS done
# 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.
#!/bin/bash
# Author: N321 Class # Program: makeclass # Usage: makeclass
USERFILE=$1 if [ $# -ne 1 ] then echo "Usage: $0
./makeuser8 "$NEWUSERNAME \"$NEWGCOS\"" done < $USERFILE
# done