Creating basic shell scripts

How to wrap your code into a file and run it from the command-line

When we start to learn the command-line interface, we generally learn and explore it interactively. That is, we enter one command at a time so that we can see the results of each command.

Here is a GIF of me using the command line to explore a directory of Shakespearean plays, counting the number of words and the number of times the word "murder" appears, both in King Lear and in all of Shakespeare's plays:

img

Using the command-line interface in this interactive fashion is fine, when trying things out. However, as you've likely noticed, typing is a very error-prone activity. So for complex tasks that we want to repeat, the best practice is not to retype their code from the beginning, but to create a self-contained shell script that can be run as a one-liner.

Our first shell script

Let's start off with something easy. Make a junk directory somewhere, such as /tmp/my-playground and change into it – we don't need to litter our actual workspace with test code.

A shell script is nothing more than a text file, which should make sense, as all of our command-line scripting has so far been, well, text.

Let's use the nano text editor to create a shell script named hello.sh. Follow these steps:

  1. Run nano hello.sh
  2. nano should open up and present an empty file for you to work in. Type in the shell command:

     echo "Hello world"
    
  3. Then press Ctrl-X on your keyboard to Exit nano
  4. nano will ask you if you want to save the modified file. Hit the y key (for "yes").
  5. nano will then confirm if you want to save to the file named hello.sh. Hit Enter to confirm this.
  6. Run the hello.sh script with this command:

     bash hello.sh
    

Here's what the steps look like as a GIF:

img

So hello.sh is not very impressive, but it captures the gist of what we want to do: wrap up a series of commands into a file, i.e. a script, so that we can re-run that script as much as we'd like.

We not only eliminate the prospect of typographic errors that occur when retyping commands, we also open up the door to making the script reusable in different contexts.

A re-usable shell script with arguments

Let's make hello.sh more complicated. Instead of just echoing Hello world, let's design the script so that it will say Hello to a specific value (i.e. someone's name). And we'll make the script sound more excited.

Here's the proposed usage:

bash hello.sh Dan
HELLO DAN

And here's a GIF:

img

First of all, how do we customize echo "Hello world"? By replacing world with a variable. Let's try it interactively from the command-line:

yourname=Dan
echo "Hello $yourname"

Output:

Hello Dan

So the question is: how do we get the hello.sh script to read in the argument (in this case, someone's name) that we pass into it?

bash hello.sh George

We do this via a special variable in bash. The variables, $1, $2, $3 (and so on), refer to the first, second, and third arguments that were passed into the script from the command line.

Thus, in the above example, George will be stored in the variable $1 when hello.sh begins running.

So reopen hello.sh with nano and change the code to this:

$yourname=$1
echo "Hello $yourname"

Save the changes and run bash hello.sh Mary to see how the output changes.

Want to return the output in all caps? Then modify hello.sh like so, piping the output through tr to replace lowercase letters with uppercase letters:

$yourname=$1
echo "Hello $yourname" | tr '[[:lower:]]' '[[:upper:]]'

And if you want to be concise, you've probably noticed that the variable, $yourname, isn't really needed. The code could be simplified to this:

echo "Hello $1" | tr '[[:lower:]]' '[[:upper:]]'

Conclusion

Time to slow down. If you can make a script that you can execute like this:

   bash hello.sh

Then you have already learn a major concept. At the very least, you have learned the method by which programmers stuff a bunch of complicated into a "container" that anyone else can run in a single line.

In the next tutorial in this series, I've taken this same idea except to go more indepth in making a script "reusable". Do you need to make a reusable script, right now? Do you even know what "reusable" means? If not, then you don't need to read that tutorial, just yet.