In this course, you'll learn how to create new user accounts. In that script, you're going to make sure the person who executes the script has the proper privileges to do so and you're going to make sure the specified account was created successfully. After that, you're going to expand it and make it better by automatically generating random passwords, accepting command line arguments, and so on. You'll learn all the concepts and techniques to build that script and complete your first project.
This course is part of the Linux Shell Scripting learning path. To follow along with this course, you can find all the necessary resources here.
Learning Objectives
- Get started with naming, permissions, variables, and built-ins
- Learn about special variables created by the shell, how to store the output of a command in a variable, and learn about if statements
- Learn about exit Statuses, return codes, and string test conditionals
- Learn how to get input from the person executing the script and how to create an account on a Linux system
Intended Audience
- Anyone who wants to learn Linux shell scripting
- Linux system administrators, developers, or programmers
Prerequisites
To get the most out of this course, you should have a basic understanding of the Linux command line.
In this lesson you're going to learn some very important details when it comes to getting started with shell scripting you're going to learn about shebangs, comments shell builtins, variables and more. So there's really a lot in this first lesson. If you have shell scripting experience and those topics seem basic, are old hat to you, just be patient with me, bear with me for just a moment. We'll be getting to some new material for you soon enough. You can either follow along right now with me or wait until the practice exercise that's coming up very soon. In the practice exercise you'll get to apply the concepts that you learn in this lesson. The first thing I'm going to do is open up a terminal on my local machine, here I am on my Mac, and this process is the same whether you're using Mac or Linux or Windows as your primary OS. Now, what I wanna do is move into our working directory which is shell class. So I'll cd into shell class and now I'm going to make a new directory that's going to house our Vagrant project for a virtual machine that we're gonna create and work on for the next few sessions and for the first couple of practice exercises. So we'll get into the show class director here we'll create local users, mkdir local users will change into that directory. The reason why I'm calling this directory local users is that we're building toward creating a script that is going to create local users or users that are local to system. Later on in the course, we'll be creating a script that will add remote users or users to systems other than the one that your script is executing on. But for now we're focusing on local users. Now let's create the Vagrant file with Vagrant init, and we'll specify the class image that we're using. In order to run and execute the shell scripts that you and I are going to be creating we need to be inside the virtual machine inside the Linux system that we've created. So we'll get in there by connecting over SSH. So we'll run Vagrant SSH, and that will connect us to the virtual machine that we just created. Let's change into the forward slash Vagrant directory with cd/vagrant and press enter. I'm going to run ls-l here and that is a long listing format of the LS command the dash L and it just displays the files that are in the directory. Now it's important to note that we have the Vagrant file. So on our current system in forward slash Vagrant it's the same as our local system as in your home directory shell class, local users. So as you can see here, this is my home directory it mounted slash Vagrant to this local system. So whatever we create inside the VM and the Vagrant directory ends up on our local machine and that local user's directory and vice versa. So here you really have a choice to make. You can either edit your scripts inside the virtual machine using Nano, Emacs or Vim or you can use the editor on your local system. So for me, I'm going to use Vim because that's my favorite editor I've done a class on it I could talk hours and hours about Vim I totally love it so that's what I'm going to do. But I will demonstrate what it looks like from the other side here. So I'm gonna go ahead and start creating the script. I'm going to use my Vim command Vim and I'm going to name the script luser-demo01.sh Now I wanna point out here that file names or shell scripts really don't matter we'll get into what does matter in just a second. So by convention, shell scripts can end in dot SH but they're not significant. The file extension is not significant like it is for example, on Windows when something has a dot exe folic cinch that's not the case on Linux. We could name this anything we could just name it luser-demo01 or .sh or Bob or Fred or any name that is a valid file name and that will work as a shell script. So let me edit this file real quick. And I'm just going to put a little bit of information in here A pound sign, an exclamation mark, forward slash bin forward slash bash. And I'm going to save my changes here and what I'm gonna do to show you what happens with the file sharing I'm just gonna do an LS in this directory and you can see that the luser-demo01.sh file was created but I'm going to open up that file in an editor on my local Mac system and so I'm gonna use Atom in my case. So here I have that same file open in the Atom editor but as you can see, it's in the shell class, local users. So that's where it's at on your local system. I'm just gonna add another line of text here I'll say "hello from the main OS" and I'm going to save my changes. Now, if I go back to the terminal where I'm logged into the virtual machine and look at this file, those changes will be there. Let me go ahead and just close out this file to make sure nothing is going to interfere with that. We'll go back to our terminal and if we display the contents of that file, luser-demo01.sh you can see that this new line of text has been added. So that's what I'm talking about. Your decision of whether to edit from your local machine or whether to edit inside the virtual machine. Again, I'm going to primarily edit inside the virtual machine here and I'm gonna get back into that file, vim luser-demo01 and by the way luser is just my personal shorthand for local user, so that's what that's about. Now, let's talk about this very first line in this shell script. It's a pound sign followed by an exclamation point and then a path to bash which is /bin/bash. This is called a shebang. The reason why it's called a shebang is the pound sign is sometimes called a sharp. If you've ever played music or read music you'll know that the pound sign looks very, very similar to a sharp sign in music. So a lot of people call the hash mark or hash symbol or pound sign, sharp. As far as the exclamation point, slang for that is bang. So we have sharp, bang but an inexact contraction of those two words is shebang. So that's where shebang comes from. And you wanna start out each shell script with the shebang, sharp bang, and then a path to the interpreter. So what will happen when you execute this script is that the commands inside the script will be interpreted by /bin/bash. Now, if you had put in another interpreter, for example let's say usr/bin/python or /ruby or whatever. Now that's a Ruby script, but we're doing shell so we'll keep it as bash. So really what happens is when you execute this script /bin/bash will get executed and the file name actually gets passed to that script. And what happens is bash then executes everything in this file I'll demonstrate that later in the course by showing the process table and so on. But for now, just know that the first line, the shebang, determines what is going to execute the commands listed in the file that you're creating. If you do not supply a shebang and specify an interpreter on the first line of the script, the commands in the script will be executed using your current shell and that may not be what you want. For example, if you have some bash specific items in your shell script and you happen to be using the CSH shell when you execute it then you're going to end up with some errors. And this is really important, especially if you're working with a team, you're writing a shell script and you have no idea what shell the other person will be using when they execute this. So you wanna be very explicit, use the shebang and make sure you specify which interpreter that you want to use for the script. So I wanna remove this line we added in the other editor. And what I like to do at the top of my shell scripts is to give a goal or a comment about what's gonna happen when the shell script is executed. So when you start a line with a pound sign it's actually a comment, comments are not executed they're just simply there for us humans only with the exception of course of the first line, which is a special line. But in all other cases that information there is just for us humans. So we can put anything after the pound sign, after the comment and it's not gonna affect the functioning of our program. And so here at the very top of the script I'm gonna tell what it does. So this script just displays various information to the screen, that's all this script does and so that's what I'm gonna put at the top of the file. Now, when you start making very long and complex scripts and you come back to it a year later or someone else looks at it and they're not sure what it does, just having a few notes at the top of this script can really help someone out there. I'm going to add another comment. We're going to say, what we wanna do is display the text 'Hello' And we're going to use a command called echo and then supply the text we want to display to the screen, which is 'hello'. And we'll put that in single quotes. I'm going to save my changes and then I'm going to exit out of the editor. So just a minute ago, I talked about file extensions and how they weren't relevant. Now, what is relevant is an execute bit on the script. Now, hopefully by the time you've got to this course you understand file permissions, but very quickly, I'm going to do a quick review of them. We'll just take a look at our previous LS output here as a demonstration. So these RW characters R stands for read W stands for write and what goes here is X for executable. And there will be three sets of these three permissions. So we have rw-r--r--. So the first three characters here represents the permissions of the owner of the file. The next three characters represents the permissions that are granted to the group of the file. And the last three characters here represent the permissions that everyone else on the system has to this file. So in this case, the Vagrant user this is the user, has read, write permissions the Vagrant group as read permissions and then everyone else who is not the Vagrant user or in the Vagrant group has read permissions to this file. What we wanna do is add execute permission so that I, you and anyone else can actually execute this file and how we do that is with chmod. So do chmod 755 luser-demo01.sh and now let's look at the permissions. Now they are our RWX, which is read, write, execute. Read, execute for the group. Read, execute for everyone else that's not the user or the owner of the file and in the Vagrant group. So what this does is if you have read permission obviously you can look at the contents of the file. If you have write permission that means you can change the contents of the file. And if you have X permission that means you can execute the file. Typically, we want to read, write and execute for ourselves and read and execute for other people. By the way, if you can't read the file then you can't execute it because you can't see the contents that are going to be read in by the interpreter and executer and so on. So typically the default permission you want to put on your shell scripts 755. So really quickly review where does this 755 come from? Well read, you can think of as four, write as two, and execute as a one and you add those together. So 7=4+2+1, which is read plus write plus execute so four, two, one is seven, and that is for the owner of the file. Five is read, which is four plus one execute so that's five, and that is for the group. So this corresponds to this middle set here five again is the same thing, 4+1=5 and that is for everyone else. So if this is a little bit confusing for you or you don't want to remember the specifics, just remember that the default permission we're going to be using is chmod 755 on the name of your script. Okay, so now we have a script it's executable so how do we execute that script? Well, the first thing you wanna do is type in period forward slash and the name of the script and then press enter. So the output of this script is simply the word, hello and that is because of the command echo 'Hello' that we placed in the script. Now, I wanna point out here that dot represents this directory. So for example if we did CD space dot, we wouldn't go anywhere we would still be in the same directory. Dot, dot represents the pair directory. So if we did see dot dot we move to the directory above the directory we were in. Let me go back into the Vagrant directory. So dot is this directory. And as you can see the forward slash is the directory separator. So dot forward slash means this directory is dot forward slash is a directory separator and then the name of the script is the file. So that's how you execute a file in your current directory dot forward slash and then the name of the file. This is equivalent to the full path. So we could actually run /vagrant/luser-demo01.sh and hit enter as well. And again, the reason why this dot slash works is because dot represents the current directory which is /vagrant and then we specify a forward slash, which is the same as this forward slash and then the name of the file which is the same as the name of this file so that's why that works so that's how you do it. So just remember chmod 755 dot forward slash and then the name of the file. By the way we could have named this file anything again, the .sh doesn't matter. As a matter of fact I'm going to rename the file really quick. luser-demo01.sh Jaosn As a matter of fact, that's a typo I even misspelled my own name incorrectly but it doesn't matter I'm gonna executed anyway, dot forward slash J-A-O-S-N and we get the same results. So I'm gonna move that back. Let me show you what it looks like when you don't have the execute bit set. Let's just use the touch command to create an empty file that's what touch does. Touch either creates an empty file if one doesn't exist or it updates the lax access timestamp of a file. Let's do an LS here in this directory. So we have blah.sh there is no executable bit set on it so if we do ./blah.sh and hit enter we get permission denied because we do not have execute permission on that file. So that is what's gonna happen when you don't have the proper permission set. So again, make sure you do chmod 755 in your file and you'll be good to go. Let's continue working on our script. We'll use Vim and edit our file here. So let's look at this echo command I kind of gloss over it to just get us started here and show the execute permissions and talk about file extensions and that sort of thing. So the echo command just displays whatever is passed to it but where does echo come from? Well, let's check it out back on the command line. Echo is actually a shell builtin that means it's a command that's built within the shell it doesn't require any external programs to execute it's really just part of bash. So how do we know it's a shell builtin? Well, you can use the type command which is also a shell builtin to show you if it's a shell builtin or not. So if you do type, space, the command you're interested about which we are interested in echo, it says echo is a shell builtin. So if you wanna see all instances of echo on your system you do type dash A for all, and then type echo. And so it lists them in the order that they will execute. Echo is a shell builtin so really that's what's gonna execute when you type echo, we'll just type echo now on the command line, echo 'Hello' just like we had on the script and press enter and the shell builtin is what is executed. Now we can force the execution of this by using the full path /usr/bin/echo and then pass something to it to display. What you wanna do is actually anytime there is a shell builtin to use the shell builtin. So don't specify the full path, just use echo and it's a little more efficient because again it's built into the shell, the shell doesn't have to request an external program to do some extra processing and what have you. So just use the shell builtin when it's available. Another advantage of using shell builtins is that it's more portable. For example, if the echo command is on /bin/echo on some systems and it's on usr/bin on another system then you might run into some issues there. But if you use the shell builtin you avoid all of that. So you can kind of think of these builtins as free stuff you get by using bash. And there's more than just these builtin commands and functions there are builtin variables and all sorts of things which we'll be getting into soon enough. By the way, if you wanna get help on a shell builtin use the help command. So we'll do help echo and a lot of the information scroll that first screen quickly so I'm gonna pipe it to "less" it's a pager so we can page up and down through this output. So I'm gonna pipe it to less, hit enter. And it says, echo rights arguments to standard output. Display the arguments on the standard output followed by a new line. So that's what the echo builtin does. Let's look at an example of a command that is not a shell builtin. One command that comes to mind is uptime. So uptime just displays how long the system that you're logged into has been up. We'll do type dash A pass in uptime to that. And it says uptime is actually usr/bin/uptime and that's when it gets executed when you run uptime. So if you run help uptime you're not gonna get any help because it's not a shell builtin. However, if you wanted to get help on uptime you would use the man command, which is short for man pages. So man or manual, man uptime and press enter. So that's how you get information on how to use commands that are not part of shell builtins. And we'll be looking at man pages along the way as well as help for builtin shell commands. By the way, to exit out of man type Q. And it's the same way with less if I didn't mention that as well, you just type Q to exit that pagination system there as well. Remember that a shell script just executes every command inside that script just as if you were typing it on the command line. So if we look at the contents of our script here if we were to do this, $ Display 'Hello' and press enter nothing happens because the pound sign is a comment and comments aren't executed. And if we type in the next line in the script, echo 'Hello' and press enter, then we get 'hello' displayed to the screen and that's exactly what happens when we execute the script. By the way, this simple echo command can come in really handy when you're debugging or writing scripts, you can use it to show exactly what is going on inside the script along the way. Now let's get back to editing the script. Let's assign a value to a variable. Let's call this variable, WORD we'll follow that with an equal sign, a single quote script and end it with a single quote. I'm going to save my changes. Now variables are simply storage locations that have a name. And this example, the name is 'WORD'. You can think of variables as name value pairs. So if we were to access the variable of word the value that would be returned would be script. Unlike some other programming languages when you're creating variables you don't have to specify its type whether it's a string or an integer or anything like that you just assign it a value. By the way, I wanna point out that it's very important that there's no space in between the equal sign. So it's the variable name followed by an equal sign without any space, again, without any space then the assignment. In this case, we're assigning a string and strings are enclosed in quotation marks either single quotation marks or double quotation marks. By the way, I recommend that you use a descriptive name for your variables, although it's not required. This simply just makes the script easier to read. Now, think about someone else who's going to be reading the script or maybe even yourself, if it's a year later or a long time after you originally written the script it would be easier for you to tell what's going on if the variables had meaningful names. Now, as a quick aside, I wanna tell you about someone I worked with whose last name started with Z. And so everything he created contained a Z. If you looked at his home directory he had a bunch of shell scripts. One shell script was named Z. The next shell script was named ZZ. The next shell script was named ZZZ. And there was a shell script named Z1 one named Z2 one named ZZZZZZ112, et cetera, it just went crazy. Like he had no imagination other than the letter Z. And then when you opened up one of his scripts and looked at them all the variables were the same Z equals blah Z1 equals blah, ZZ equals blah. He was the only person that had any kind of clue of what was going on with his scripts because they were almost impossible to read. So again, using something like that works it's just not recommended. By the way, there are some rules around variable names. Variable names can contain letters, digits and underscores and they can start with letters or underscores but they cannot start with a digit. So for example, "WORD" is a valid variable name, "WORD1" is a valid variable name, "_WORD" is a valid variable name. However, "3WORD" is not a valid variable name. Also, you cannot use dashes. So something like this "A-WORD" is not a valid variable name. It's invalid, you can't use that. One more example would be an @ sign you can't use that as well. "e@mail=asdf" something is not going to work. Let me get rid of these example names here. Now that we've assigned a value to our variable let's go ahead and use that variable. So we'll just do echo "$WORD" So when you reference the variable by name you get its value back. Now to do that you have to precede the variable name with the dollar sign. So when we want the value of word, we use "$WORD". Now you may have noticed that in the previous echo statement I used single quotes but in this echo statement, I use double quotes. single quotes prevent the expansion of variables. So if you want echo to display exactly what you specify put it in single quotes. If you want variables to be interpreted make sure that you use double quotes. By the way, we're just using the echo shell builtin for now but this concept of single quotes and double quotes apply to other commands as well. To say that another way, it's not because we're using echo that this expansion happens with double quotes and it doesn't with single quotes it's the quotes themselves that is causing this expansion or not. And then you can use this functionality with other commands not just with echo. So let's execute this script and see what happens? Okay we get Hello, which is from echo hello. Then we set the word variable to equal script. And then we displayed that word variable which actually in turn displays the word script. By the way, you can do what I'm doing here as far as your workflow goes which is to open your editor, edit the file, exit your editor and then execute the script. Another thing you could do is actually open up a new terminal window connect to the Vagrant system, get into that directory and then execute that script with the other terminal so you just switch back and forth between terminals versus entering and exiting your editor all the time. Just use whatever method works best for you. But now for the rest of this lesson I'm just going to edit the script, exit out, execute the script and so on. So let's return to editing our script. To add a comment here of what we're about to do Okay, we're putting '$WORD' which is our variable inside single quotes. Now let's see what effect that has. So as you can see, using single quotes displays exactly what is in the single quotes it doesn't do any expanding or interpreting of the variables it's exactly what you specify. So just keep that in mind, if you're using variables use double quotes if you wanna be very specific and make sure that nothing gets changed, use single quotes. Okay, let's do some more editing here. So you can use variables with hard coded texts. So here we have some texts that we just manually typed in this is a shell and then we use a variable which is $WORD so we want the value of that variable to be returned. Let's exit out and execute this script and see what happens. So it gets interpreted and the output is this is a shell script. You can also enclose the variable name in curly braces and perceive the opening brace with a dollar sign. So the syntax is dollar sign, opening curly brace then the variable name followed by a closing curly brace. By the way, when you're reading scripts written by others you'll see both types of syntax being used. You'll see the dollar sign with the variable name as well as the dollar sign including the braces and the variable name. Now let's execute that script and see what happens. So we get the same exact output as the preceding echo command it's we're just using the different syntax. Now, if we wanna append text to the variable we have to use this dollar sign brace syntax, let me show an example. So if we want to append ING directly after the output of the variable, we have to use the curly braces otherwise bash doesn't know that the variable is actually WORD, W-O-R-D and that the part that's not the variable is ING it has no way of knowing that unless you use the braces to separate the variable from the preceding or following text, now let's execute this. Okay, it says, scripting is fun! Again, script is what was stored in the word variable ING is what we hard coded in the echo statement there. So let's demonstrate how not to append text to a variable I was just talking about this, let's do this. So this is not gonna work echo "$WORDing is fun!". Again, dash doesn't know that ING is not part of the variable name. By the way, I didn't necessarily mention this but by convention not by rule or syntax variables are in all uppercase. That's why I used uppercase word as a variable name and that way you can typically easily spot what is a variable and what is not. However, since that's only a convention or a best practice and it's not enforceable by syntax, then dash isn't going, oh, ING is lowercase so therefore it's not part of the variable name. No, that's not how it works. You can actually use lowercase variables and some people do. So this will not work, let's prove it here. You get blank is fun for the last bit of output that's because "$WORDing", that variable doesn't exist so it doesn't expand to anything so there's nothing there and it displays nothing. And that was followed by a "space is space fun! so that is what it gets displayed on the screen "space is space fun!. Now let's create another variable for our script here. We're going to name this variable ending again, we're going to use all uppercase letters because that is the convention or best practice, not required but that's how we're going to do it. We're not going to use spaces because that will cause an error so we wanna make sure there are no spaces here. We're going to use single quotes and type in ED and close it with single quotes because we're not doing any variable expansion. Here, we're just doing a strict assignment of ED to this variable. Now let's combine these two variables that we've created. So as you can see, we use this dollar sign, brace syntax so this variable of word will get interpreted and then dollar sign, ending here will get interpreted as well. So we'll get, this is script and this should expand to ed, E-D here. So let's prove this by executing it on the command line. Sure enough, we get this is scripted because the word variable contains the value of script and the ending variable contains the value of E-D. Now let's change the value that's stored in the ending variable. This is called reassignment, by the way. Now we're going to change the value that's stored in ending to ING. By the way, you can override the contents of the variables just using assignments or these reassignments. So the first time ending is specified it's equal to ED when you access the ending variable that's what gets displayed. But since the scripts are read from the top down just as if you were typing them in at the command line when you change the value of ending then anything that follows that will use that new value. And let's prove that here we'll do echo "${WORD}${ENDING} is fun!" save our changes and execute our script. So we have scripting is fun. So word expanded to script the ending since we changed it to ING expanded to ING so scripting is what you get when you combine word and ending together. Again, we use that special brace syntax so that that could tell where one variable started and ended as well as the other variable where it started and ended. Let's do one more reassignment. We'll assign S to ending Okay, let's save our changes and execute our script. You are going to write many scripts in this class. All right, this brings us to the end of this lesson. In this lesson, you've learned a lot. You've learned about file naming, file naming conventions, how file names don't determine if something is a script or executable or not it's actually the execute bit on the file itself that determines if you can execute it as a script or not. You also learned about shebangs. You learned how to use comments effectively, create a header in your file and comment the script along the way. You learned about the difference between a shell builtin versus a normal command on the system. How to get help with shell builtins which is to specify that word help followed by the name of the shell builtin. You also learned that you can get, help about general commands on the system using man pages. So man followed by the command, again, man and a shell builtin will not provide any help but you can get help from man bash or help and the shell builtin. Finally, we talked about variables, how to assign them, how to reassign them. We talked about single quotes and how nothing inside single quotes gets expanded or interpreted and how things inside double quotes do get expanded and interpreted such as variables. We talked about the two different syntaxes you can use with variables. One is just pre pending a dollar sign to the variable name. And the other is pre pending a dollar sign and surrounding the variable in braces. You learned that that syntax is required if you want to immediately precede or immediately follow that variable with some additional data. Even though using the dollar sign and brace encapsulation method is a best practice when working with variables, I showed you the other methods so that if you see other people's scripts you know what they're doing and it doesn't come as a surprise to you. Okay, that wraps it up for this lesson.
Jason is the founder of the Linux Training Academy as well as the author of "Linux for Beginners" and "Command Line Kung Fu." He has over 20 years of professional Linux experience, having worked for industry leaders such as Hewlett-Packard, Xerox, UPS, FireEye, and Amazon.com. Nothing gives him more satisfaction than knowing he has helped thousands of IT professionals level up their careers through his many books and courses.