Linux Shell Scripting
The course is part of this learning path
In this course, we'll cover a range of topics designed to help you enhance your Linux scripts. We'll start off by looking at case statements, which are used to make a decision based on the value of a given variable. We'll cover functions before moving and then move on to how to process command-line options using the shell built-in getopts.
In the second part of the course, we'll look at managing users including how to disable, delete, and archive users on a Linux system. We'll then do a walkthrough exercise showing you how to delete a user, which you can follow along with.
This course is part of the Linux Shell Scripting learning path. To follow along with this course, you can download all the necessary resources here.
- Learn about case statements and functions to make your scripts more efficient
- Process command line options using getopts
- Manage users in Linux
- 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'll learn how to process command line options using the shell builtin getopts. If you want your shell scripts to behave like other Linux executables you're going to want to allow users to specify options that change the behavior of your scripts. For example, you might want to give the user a verbose option where the user sees a lot of output from your script, or maybe you want to give them a quiet option where your script does its work silently and then only reports if it has an error. Probably one of the first commands you learned how to use on a Linux system was ls. Right after learning that ls lists files, you probably learned about the -l option which makes ls list files in a long listing format giving you more information. Using getopts helps you not only provide such functionality in your scripts, but also helps you to conform to Linux programming conventions. Knowing what you've learned so far you could use an if statement or a case statement to check for individual options like -l or -a. However, you'll want to allow the user to combine those short options together so that -l -a performs exactly like -la or -al. Doing that with a simple case statement is actually kind of hard and getopts handles this and other little gotchas when working with options. And that's one of the reasons why we're going to use it here today. Okay, let's get to it. I've got a terminal open on my local machine and I'm going to go into our class folder of shell class and then we're still working on the local users vagrant project. And I'm going to bring up the system with vagrant up. Okay, let's move into our shared folder of /vagrant. Now, first off getopts is a shell built in and we can tell this by running the type command against it and we'll do type dash a G-E-T-O-P-T-S. And sure enough it is a shell built in. As you already know I recommend using shell built-ins whenever possible because it makes your script more portable. That's one of the reasons why we're going to be using getopts. There is a very similar command called getopt, that singular, that you might see in some older scripts. Getopt is an executable on the file system. So we'll do type dash a getopt and sure enough that is located on this particular system in usr/bin/getopt. Getopt is a very similar to get ops, but it has its limitations and its own set of unique quirks. If you're interested in learning more about it, or you end up having to dive into the details of it at some point because you run across a script that has getopt in it then just read the man page on getopt and you'll be well on your way. Now let's get some help on the getopts built in function here. So we'll do help getopts. Actually let me pipe that to less so we can actually read what it says here on the screen. And it's usage is getopts optstring name. And as we know anything in brackets is optional so you have an optional argument there on the end. Now, instead of me reading this entire help section to you just let me quickly summarize it and we'll start using it right away. So the optstring is something that you are going to provide. These are the options that your script is going to recognize and accept. It's just a series of letters that are going to be your options. If you wanna make an option mandatory then follow that letter option with a colon. Next you're going to provide a name. It's a variable that's going to get populated with an option. That way you can do something based on that option because you have no idea what a user is going to do and how many options they may try to supply. You'll need to use getopts in a while loop. Getopts returns zero as long as it finds an option to process, otherwise it returns one, which will cause the while loop to exit. Finally getopts parses positional parameters by default, but you can make it parse something else entirely by providing it with an argument at the end. All right, so let's write a script that uses getopts. Let's make this script generate a password. Let's also give the user some control over what happens here with our script. We'll let them specify a password length with a -l option and whether or not to use a special character with the -s option. And let's let them control the verbosity or level of output that is going to be displayed to the screen. Because a user can optionally specify a password length let's go ahead and set a default value for that. We'll use the variable length and we'll set it to 48 characters long. This default value will be overridden with whatever the user provides if they decide to specify a password length with the -l option, which we're going to account for in just a second. Now, what we're going to do is use a while loop in conjunction with a getopts command. In the while loop we're going to use a case statement to do something based on the option provided. This is the structure you're going to see and use when processing command line options. All right, I'll just start the while command here. We'll do while getopts, this is our optstring and this is our variable name. Now remember we have to tell getopts what options are valid. Here we're going to allow the -v option, the -l option, and the -s option, that's why we have the characters vl s and the optstring. If you want to make an option have a mandatory value follow that option with a colon. By using vl:s we are saying that the -l option must be followed with an argument of its own. For this script we want the user to supply a length to the -l option. Now we are going to use a case statement to process our options. When an option requires an argument getopts places that argument into the shell variable optarg. Here we're signing that argument's value to the length variable. If a user were to execute our script with -l4 then four will be assigned to optarg. And here we're assigning that value of four into the length of variable that we'll be using later on in the screen. By the way you could have used an asterisk as the catchall pattern in the case statement, but getopts is giving us a single character option at a time so I decided to use the pattern for a single character, which is the question mark. We'll come back to this section in a second but first let's see what we have so far. Okay, that's -v for the verbose option and that seems to work. Dash s doesn't display any output because we didn't tell it to. Let's see what happens when we run it with -l, we get an error. So getopts is taking care of this situation for us. It's saying, hey, this option requires an argument. So we'll go ahead and give it an argument. And that looks good. Again, we didn't print anything or do anything in that case statement. So this is expected behavior at this point. So let's do this. Let's give it an option that is invalid. So we'll hit that here and it says illegal option x. Okay, so this is a good basis for our script. Now let's jump back in and continue building this out. Instead of echoing invalid option to the screen and exiting I want to teach the user how to use this script. In theory, I could place that code right in the case statement. However, I think it's going to be several lines long, so I'm going to put it into a function. This makes the case statement easier to read. Again, this is a pure judgment call. You can do this without a function if you want to. So here, I'm going to do this. I'm gonna get rid of this. And instead, I'm going to say usage. Now we have to go back to the top of our script and define this usage function. Again, it's a best practice to place all your functions at the very top of your script. So I'll go up here and I will say usage and we'll do this here. So here we have our function that displays the usage. We're modeling our usage here after man pages and the help built-in command. We're putting optional options within brackets and so on and so forth. We're giving you some information about what those options are. And then we're going to exit the script with an exit status of one, because if they've ended up here that means they've used an invalid option or something like that. So we're going to exit with a non-zero exit status. So let's try this out. Luser-demo11 -x and it says illegal option x, that's coming from getopts. And then what has happened is our catchall of the question mark in our case statement has been executed or that has been matched and the code underneath it, which is usage, causes us to execute the code in the usage of function that we declared. And then we get this information to our screen. Now our exit status should be one because that is what's going on in that function. So let's just check it real quick. And sure enough, the exit status is one. If we're in verbose mode we want to tell the user everything that is happening in the script. So let's do a little check. Let's go down here after the case statement and the while loop and we'll do this. So this is the type of check we need, but if we need to check the verbose variable every time we want to display something to the screen that is going to be a lot of duplicated code and repeated work. So in the spirit of keeping things DRY, don't repeat yourself, we're going to move this check into a function. So I'll just actually delete this and move it back up to the top here. So what we'll do is call it log. Now I'm not the biggest fan of that name because we're not actually going to write anything to a log file in this particular script, but it works for demonstration purposes here. Gives you an idea of what is happening or what this function does. Now, perhaps we could have named this function something like local echo or display or say, or probably, something that I like better, something more verbose like send to standard out or something else that you dream up. Anyway, I'm gonna go with log here for now. It's gonna work for our purposes. So what we're going to do is have a local variable called message. And we're going to assign that anything that has been passed into this function. Now we'll just do our check here. If verbose is true then we'll echo whatever that message is to the screen. Okay, and that wraps up our little function. So now that we have our function we need to call it instead. So we'll go down to the bottom of our script here. And instead of doing an echo we'll do log generating a password. Also we replace the echo command with the log function for the initial verbose message above. So we have echo here, so we can do the same thing. We'll just do log here to be consistent. Okay, let's see if this works. So we'll do our script name here and we'll do a -v option. Okay, sure enough, verbose mode gets printed to the screen. Generating a password also gets displayed to the screen. Well, let's see what happens if we don't use verbose mode. Okay, nothing gets echoed to the screen, even though we had that log that says generating a password. Well, that log function checked if verbose mode was set and it wasn't, so it did not display it to the screen. So this is doing exactly what we want it to do. So now let's actually generate a password. I just stole that little line of code from one of our earlier lessons. The main thing to point out here is that we're using the variable length. We set the default value to be 48, but a user can change that with a -l option. Now let's see if we need to append a special character to the password or not. And we can do it simple check with an if statement. Again, the couple of lines of code here that do the actual work or covered in an earlier lesson. Let's tell the user that we're done and then just give them the password. I wanted to point out that the reason I'm using the echo statement directly to display the password is that I want to display that password no matter if the verbose mode is on or not. So let's check our work at the command line. Okay, we get a random password that's 48 characters in length by default, so that looks good. Let's append a -s and sure enough you can see that a special character is appended to the end. Let's do this with verbose mode. Sure enough, now it says verbose mode is on. We're generating a password. We're selecting a random character. We're all done and here's your password. So that's very, very wordy or very verbose. So obviously the verbose mode is doing its job. Now, by the way, something that getopts handles for us here is a convention where you can not only do sv, but you can do -vs, which is the same as -v -s or -s -v. Okay, now let's try specifying a length. So we'll do -l dash eight and sure enough we get a password that has eight characters in it. And let's also combine this with a -s option to append a special character. All right, so we have a password that's eight in length plus one, plus the special character. Again, we could go back and really revise this script and get specific and really make sure that if we're adding a special character that we chop off a character from the password before it so it's actually truly eight characters. Again, I'm of the mind that this probably is close enough. This gets us like 90% of the way there without going in and doing some even more complex work that in the end, probably truly doesn't matter for our practical use case here. Okay, so at this point we could really stop here because our script does exactly what we want it to do. However, I wanna show you how to handle additional command line arguments beyond the options that you are taking care of with getopts. In order to do that we need to talk about arithmetic expansion and arithmetic evaluation.
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.