Linux Shell Scripting
The course is part of this learning path
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.
- 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
- Anyone who wants to learn Linux shell scripting
- Linux system administrators, developers, or programmers
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 about some special variables created by the shell, how to store the output of a command in a variable and we're gonna talk a little bit about the if statement. First thing I'm going to do here is open up a terminal on my local machine. I'm going to go into our class folder of shell class. From here, I'm going to go into local users, cd local users, we're still working on creating local users and I'm going to start this virtual machine. Once the machine is booted you can connect to it with vagrant ssh. Now let's move into our shared folder of /vagrant and we'll get there with a cd command. Now let's start working on the script for this lesson. I'm going to call this luser-demo02.sh. Remember, we always start our scripts with a #!, so I'm going to start out with a #!/bin/bash. As you probably remember from the last lesson, I like to put a header at the top of the file that just describes what the shell script is going to do. And I'm going to put that in right now. So these are the two main goals that I have for this script, which is to display the UID, which stands for the user ID or user identification number and the username of the user that's executing this particular script. And the last thing we want to do in this script is to display if the user that is executing the script is the root user or if they are some other user. They're either the root user executing the script or they're not. One of the things that can help you write shell scripts is to think about all the steps that it takes to accomplish your goal. We have the goal and the header. So now let's think about some steps that we need to take in order to accomplish our goal. A lot of people call this pseudo coding, and so what you do is you just write and play in English and in your language of what you want to accomplish as a comment, put those comments in your file and then go back and put in the exact shell commands and syntax that you'll need to accomplish each one of those tasks. So it's kind of like outlining before you write the shell script. So I'm going to go ahead and do that now. The first thing I want to do is display the UID. The next thing I want to do is display the username. And finally, what I want to do is display if the user is the root user or not. So now we have our header and our pseudo code. Now let's start filling in the code. So the first thing we want to do is display the UID, and of course, you know that the echo shell builtin displays information to the screen, and we're going to use something that the shell gives us for free. I'm going to show you here. Your UID is UID. As you know, if you perceive something with a dollar sign and enclose it in curly braces that it's a variable, this variable is UID but we didn't assign a variable or set of variable. So where is that variable going to come from? Well, remember that I said you get a lot of stuff quote stuff for free when you use bash. And one of the things you get for free are special preset variables. So let's go ahead and write and quit our script. Remember, before you execute the script for the first time you want to set the executable bit on it, and now let's execute our script and see what happens. Okay, it says your UID is 1000. So let's look at the bash man page to see where this UID comes from. By the way, to navigate a man page you're going to use VI or VIM key bindings. And so how to search for it, is forward slash and then the string you're searching for. We're looking for you UID, so we'll do that and hit Enter to take us to the first instance of that. If we want to keep going down the file, hit in to go to the next occurrence. And here you can see that the information about this as that, it expands to the ID of the current user initialized at shell startup, this variable is read only. That means that a user can't change it. So if we were to check against this UID, we can be fairly certain that it's going to be the actual source of truth. It's really going to have the UID in it. Now, let me back up a little bit. I'm going to hit Shift and to back up to the previous match. Now, there is a similar variable here called EUID which expands to the effective user ID of the current user initialize that shell startup. This variable is also read-only. So in reality, we could use EUID or UID. The only time that UID would be different from EUID is in the case of a set UID script. When a file has a special set UID permission set on it, the file actually gets executed as the owner of the file no matter what user is executing the file. So if it's set UID root and where the vagrant user and we execute the script, our UID is 1000 for vagrant but our EUID is zero for root. However, this is rarely seen modern Linux systems for security reasons do not allow scripts to be set UID. So this is just something to keep in mind. For example, if you're working on say a proprietary Unix system that's somewhat old, but again, if you're working on modern day Linux systems, you can safely use UID because EUID and UID are going to be the same. But just know if you see that somewhere else, that is where that comes from. Okay, let's back up even farther. I'm going to type question mark for a reverse search and type shell and hit Enter here. So they EUID and UID that we were just reading about happened to be in the shell variables section of the bash man page. So not only do you get EUID and UID, you get several other variables that anytime you start bash, that all of these are going to be set. So if you want, take a moment here and just read down these variables and see what's available to you. Okay, I'm just going to hit Q to quit since we're done looking at the man page and I quickly want to demonstrate that the UID is indeed read only. So if we do echo, UID, it's 1000. If we do UID equals, let's say we're going to set it to 1001, hit Enter. It says, no, you can't do that because it's a read-only variable. So again, we can trust the information that's in the UID variable. So if you remember from our script, the next thing we want to do is actually display the username. So how do we do that? Well, one way to do that is with the id command and let's see this type -a id. So it is actually a program and it's not a shell builtin. So since it's not a shell builtin, we can learn about it by using the man page man id. By the way, when you're looking at a man page if you see things that are enclosed in square brackets, that means they are optional. So here you can see id with optional option and optional user. So actually you can run this command on its own and it doesn't require any arguments because they're all optional. Additionally, if you see these three dots are three periods also called an ellipsis, I do believe, that you can specify multiple of whatever precedes it. So you can have the id command followed by multiple options and then one optional user. So the description here says print user and group information for the specified user or when user omitted for the current user. So I'm going to look at what our options are here, - n, print a name instead of a number. And so we're actually looking to print a name, so that's useful, - u, print only the effective user ID. So those look like the two things that we need to use to print a username would be -un. I'm going to hit Q here and just test out the id command with no options. Id, hit Enter and says, UID is 1000, vagrant is our username, gid is 1000, which is a group id and vagrant is the username of that group. And the groups that we're a member of is also called vagrant with a group id of 1000. So now let's specify id -u and that prints out the UID of 1000. Now, if we wanted to add the -n option, we could do this, id -u -n and it displays our username. Also want to point out that it doesn't matter about the order of these options. So I can do id -n -u and get the same result. Another thing I want to point out is when you have these single character options you can combine them. So we'll do id- and then we can list all the single character options that we need without any further dashes. So we'll do - nu and that will give us our username or it doesn't matter about the order. So we can do id -un, press Enter and we get the same result. For me, id un, is aesthetically pleasing because it's u for user and for username, so that makes sense to me that's the command I'm going to use but any of those variations work obviously. So if you see them, you'll know what's going on. By the way, some of you may be familiar with the whoami command, will execute it now. And that would be another option. Let's look at what type of command it is. It is a program, so I'm going to use the man page to learn more about it. Whoami print effective user id, whoami again, you can specify options. The options are optional and you can specify more than one of them. The description is print the username associated with the current effective user ID, same as id -un. So that's what we're going to use, id -un, you can use whoami, if you would like as well. Okay, let's return to editing the script. Before we display the username, let's assign the username to a variable. We'll call that variable USER_NAME. If you want to store the output of a command into a variable you use the syntax you see here on your screen which is variable name equals dollar sign opening parentheses, then the command and a closing parentheses. The value for the variable name USER_NAME will equal the output of id -un. Let's test this here. Let's echo it to our screen. Here I'm gonna save my changes and execute the script. Okay, so it says your UID is 1000, your user name is vagrant. I want to demonstrate an alternative syntax that you'll see sometimes as well. So let's edit our script. Let's comment out this line. I'm going to make a copy of it, and then instead of dollar sign opening parentheses, I'm going to replace that with a tick and the closing parentheses, I'm going to replace that with a tick as well. Going to execute this script and see if we get the same result and sure enough, we do. Let's get back into here. So that is an older style syntax. The tick marks that surround a command to assign that to a variable, that's the older syntax. So again, I like to show you these things because different people write shell scripts in different ways. and you'll be looking at shell scripts from perhaps earlier times that were written without these modern convention. So if you see these back ticks know that it's the same thing as the dollar sign parentheses syntax. So I'm going to remove that line, uncomment that other line and we should be back to where we were. First, I'm going to type out an if statement here and then we'll talk through it. So an if statement begins with the keyword if, followed by a space, then double opening brackets, followed by a space and then an expression. After that expression, you'll use the space and then the closing double brackets. On the next line, you'll use the keyword then, and then whatever follows that will get executed if the expression evaluates to true. So in our case, if the UID is equal to zero, then we'll execute the code that says, echo you are root. If you want to include an else statement, then you use the else key word and then the code that follows that will execute if the expression evaluates to false. So if UID is not equal to zero, then what's going to happen is echo will be run with you are not root displaying to the screen. You close your if statement with fi, which is the reverse of if and that is the general syntax for the if statement. I want to point out a couple of different things here. Again, the if keyword should be followed by a space. The opening double bracket should be followed by a space and the closing double bracket should have a space preceding it. When you are using variables inside of these double brackets as a test, you always want to enclose them in quotation marks. Okay, let's get back to the shell and learn a little bit more about this if statement. So let's do type -a if, it turns out that if is a shell keyword, so we can get help on that with help if. So here it says, if COMMANDS; then COMMANDS; and then as you already know, things that are in brackets are optional. So you can optionally use elif or else, and then what is not optional is the closing fi. So if you remember, I said, we were going to do this. We're going to use if and then a double brackets here and do a test. So what I want to point out is in the if description, it says, if COMMANDS; then COMMANDS; what the semi-colon is, it's the command separator. Another command separator is to hit Enter or begin on a new line. So if we bring this last command in our shell history back up, I'll just hit the up arrow. You can see that where there were line breaks when I entered them in on the command line, they are now replaced with semi-colon. So again, the semi-colon is a command separator. So anytime you see a semi-colon in some of these descriptions, know that you can use a new line. And I like to use the new line so it doesn't run altogether, and it looks visually better to me, and it's easier for me to read. However, it is correct to also use a semi-colon and keep it all on one line. I just want to point out that if your command is not completed, when you hit Enter, you'll get a different looking prompt here, which is the greater than sign. And then once the command is done, then you'll get your normal prompt back. I just wanted to point that out. So let's keep reading about the if statement. The, 'if COMMANDS' list is executed, you can think of list is a list of commands. If the 'if COMMANDS' list is executed, if its exit status is zero, then the 'then COMMANDS' list is executed. You can think of an exit status of zero being true. So if this is true, then do that. Else, if it's not true, then do something else. So you have a general idea of how the if statement works. However, in the if description, it says if commands, and if those commands have an exit status of zero, then execute some code. Well, where did we get these double brackets? Well, let me show you. Those double brackets are actually a shell keyword, so let's get help on those. So here it says double brackets expression, closing double brackets. It also says it returns a status of zero or one, depending on the evaluation of the conditional expression EXPRESSION. Expressions are composed of the same primaries used by the test builtin and may be combined using the following operators. We're not going to worry about those operators for a moment but the thing to keep in mind here is that it says you can use the same things that you can use with a test builtin to generate your expressions. So, test is indeed a shell builtin as well as a command. We're interested in using the builtin, so we'll get help with it. We'll do help test, and I know from experience that a lot of information is going to scroll off the screen. So one way to prevent that is to use a pager. So we'll use a pipe to pipe the output of help test into our pager of less. And let's hit Enter and see what happens. So the test builtin evaluates conditional expressions. Here, again, to just reiterate what you already know, zero is true, one is false. So here are all the tests that you can do. You can do file operators like -a file to test if a file exists, -b if it's a block special device and so on, -d if it's a directory. So there are all these types of tests you can use against files. Additionally, there are string operators. So -z string returns true if the string is empty or -n string returns true if the string is not empty. You can compare strings with the equal sign or the not equal, the less than and greater than et cetera. This is actually what we're doing in our shell script. We're comparing the number of the UID to zero. We're just saying is the UID -eq, which stands for equal to zero. If it is equal, then that's true and it returns to zero. I'll hit Q to exit out of the pager. Okay, let's execute our script and see what result we get. It says your UID is 1000, your username is vagrant, you are not root. By the way, if I failed to mention it, the UID of zero is always assigned to the root user. Root always has the UID of zero on every Linux system that you'll ever encounter. Every Unix system that you'll ever encounter, root is always the UID of zero. That's why we check against it because it's a guarantee you could in theory test against the username, but it's probably a little bit more safe and secure to test against UID zero, because again, that is always a hundred percent always assigned to the root user. Speaking of root, let's execute this program as the root user. One way to do that is use the sudo command, which stands for super user do. So we'll do sudo luser-demo02.sh and hit Enter. So now, since we're using the sudo command, this command actually gets executed as root. So it says your UID is zero, your username is root, you are root. We could have also switched to the root user and done this as well. We can do su and by the way, the password for the root user on this system is vagrant. And then now we can execute that as the root user. And it says your UID zero, your username is root. You are definitely root. Okay, I'm going to exit back out to the vagrant user. Let's look at the contents of our file one more time. I want to point out that this double bracket syntax we're using with the if statement is a bash specific thing. So this may not be portable to all shells. For example, if you're using CSH script it's probably not going to work for you but if you're using bash, it will work. Another thing to note about the double bracket syntax is that it really replaced the single bracket syntax. So actually you may see some shell scripts that look like this. If with a single opening bracket then a conditional expression and a single closing bracket. So again, just know that the double brackets is the new way to do it. You can also use the single that's an older way to do it. Let's get out to the shell here and we can actually do this. The single opening bracket is a shell builtin but it's actually also a command on the file system. So on older Unix systems, this is how it would work. So sure enough, that's a file that's located at user bin opening bracket. It's a file and you can execute it like that. So if you're using a really slim down shell that doesn't have the opening bracket builtin, the shelf script will default to using user bin and the opening bracket. However we're using bash, we don't have to worry about that. And again, we're going to use the newer syntax of the double brackets. Okay, let's look at our script one last time. Now this last block of code, this if statement, if the UID equals zero, then you are root, else, you are not root. Some people call this type of checking idiot checking and it's to help people from doing idiotic things. Other people call it sanity checking. And by the way, it's a good idea to check for any requirements in your shell script. If you know something that is required for the script to work properly, check for it, never assume things are just gonna be the way that you want them to be. For example, if a script adds users and adding users requires root privileges, then you want to check for root privileges. Don't assume that the user knows that they need to execute it with root privileges. They may execute it as their own user. So you want to do some sanity checking or some idiot checking if you will to make sure that that happens as needed and you'll want to do this type of sanity checking throughout the script whenever it's needed. Okay, that brings us to the end of this lesson where you learned about some special variables that are created by the shell, how you can look up those variables by using man bash. You also learned how to store the output of a command in a variable using the dollar sign parentheses syntax. You also learned about how to use an if statement or in our specific case, the if else statement, and we talked a little bit about conditionals. You also learned that you could use help tests from the command line to get some information about possible tests or expressions or conditions that you can check for. Finally, you learned about some of the older syntax options such as using back ticks when assigning the output of a command to a variable and using a single bracket when doing tests versus the double bracket.
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.