I’ve made some life updates and am gradually updating my website to reflect them, so please excuse any messiness.

How to write and run a shell script

The blog post I wish I’d found about writing and running shell scripts.


A particular barrier keeping me from writing my own shell scripts for a long time was that I didn’t know how to run, or execute, them. At the time, this seemed like something obvious that I should know, and I felt ashamed. Now, I know that, depending on how someone has arrived at programming, this information might not be obvious at all e.g. for folks like me, who entered programming through front-end development and never formally studied computer science, I suspect this is not obvious.

In the spirit of writing the blog post I wish I found, here is how to write and run a shell script, followed by an explanation of the script written for readers who are brand new to shell scripting. Feel free to skip around this post as you please.

Table of Contents

    In this post, I assume that readers have the following knowledge:

    • An idea of what it means to run, or execute, a script (a relevant talk by Susie Grange about teaching concepts like “run”)
    • Ability to cd into a directory from the command line
    • Understanding of the difference between Node.js and client-side JavaScript

    In this post, when I refer to JavaScript, I am referring to Node.js or server-side JavaScript.

    Steps to write and run a shell script

    1. Create a new file in a location that is easy to access (e.g. a directory where you save coding projects)
    2. Name it example.sh (shell scripts use the .sh file extension)
    3. In the file, add the following:
      #!/bin/bash
      NAME=$1
      echo "Hello, $NAME!"
    4. Open a command prompt (i.e. Terminal)
    5. cd to the directory where you created the file in Step 1.
    6. Run the command sh example.sh Lara (notice the sh before the file path)
    7. You should see the output, “Hello, Lara!”

    Jargon Definitions

    Shell – A shell is a program that allows a user to interact with the operating system. It’s called a shell because it’s “built around” the inner part of the operating system, often called the kernel. The console in the browser developer tools can be seen as a shell for the browser.

    BashBash is a Unix shell and scripting language, accessible in the command line (i.e. the Terminal or abbreviated CLI). Mac operating systems are based on Unix, so this is usually the shell that opens by default in the Terminal on a Mac.

    Scripting language – A scripting language is a programming language designed for a particular environment, usually used to accomplish specific tasks. Programs that are written in a scripting language execute as if run line-by-line by a human. Examples are shell languages, like Bash, as well as other, general-purpose languages like JavaScript, PHP, Python, and Ruby. When “scripting language” is used to describe these general-purpose languages, it indicates that the language is interpreted vs. compiled i.e. the program is read line by line by a computer vs. read all at once.

    Shell script – A shell script is a program, usually written in a scripting language, that is run by a shell. It might be as simple as a sequence of commands that are commonly run together, or it could include logic, functions, API requests, and more.

    Shebang – The syntax #! is called a shebang. A shell script must begin with this syntax so the machine knows which language you are using for your script. The #! is followed by the path to the interpreter for the scripting language. For bash (on Unix operating systems), this is #!/bin/bash.

    Explanation of our script

    In the steps above, we added the following to our shell script, example.sh:

    #!/bin/bash
    
    NAME=$1
    echo "Hello, $NAME!"

    Notice the shebang at the top of the file.

    If your script was written in another language, for example, Node.js, the file would be named example.js and the shebang would point to a different interpreter:

    #!/usr/bin/env node

    Notice the slight difference in the paths to each interpreter (bash vs. node). What differences do you notice, and what do you think they mean?

    Now, read the next two lines of our script after the shebang:

    #!/bin/bash
    
    NAME=$1
    echo "Hello, $NAME!"

    Recall how we ran our script and what the output was (note that the $ at the beginning of the following lines indicates we are using the command line):

    $ sh example.sh Lara
    Hello, Lara!

    What do you think $1 is doing in our script? Let’s expand the script a bit more:

    #!/bin/bash
    
    FIRST_NAME=$1
    LAST_NAME=$2
    echo "Hello, $FIRST_NAME $LAST_NAME!"

    Now, let’s run the script a couple of times:

    $ sh example.sh Lara Schenck
    Hello, Lara Schenck!
    $ sh example.sh Rajanraj Siwakoti
    Hello, Rajanraj Siwakoti!

    Looking at these two blocks of code – the script, and the output when it is run – what do you think $1 and $2 represent?

    Stare at it until it makes sense.

    This is some of my favorite programming advice. I read it in a book ironically titled Zero Bugs and Program Faster by Kate Thompson. She writes about how our brains are wonderful at recognizing patterns – code is full of patterns, so one way to learn new programming skills is to read the code and allow your brain to make the connections. Sometimes this means stepping away for a while, forgetting about it, then coming back. Sometimes we try too hard when what we really need to do is get out of our own way – I did not understand that earlier in my career, and it’s still hard to do.

    Why create a shell script?

    For those of us who work on front-end code, we likely run commands in the command line to do things like build our JavaScript or CSS assets, start a local server, or install npm dependencies. Often, these are scripts run through npm and registered in package.json’s scripts object. As commands become more complex, using package.json to lump several commands into a single script entry can become cumbersome, and it is difficult to use any logic or data. A shell script can be used to store these commands in their own file where you may use logic and any control structures (i.e. for loops) supported by the scripting language.

    These scripts can be useful for a wide variety of contexts, not just running front-end scripts. For example, a specific use case I came across at my job was when I needed to run several commands to deploy code to a staging environment. At the time, this needed to be done for about 25 environments. Every time I had to do the task, I would forget what I did before and have to figure it out all over again. Storing these commands as a shell script was a nice way 1) to run several commands with a single command, and 2) to record the steps I took to accomplish the task so that I could do it more easily next time.

    Learning shell scripting is a powerful tool in your workflow, and it can be a way to increase your confidence using the command line.

    Why Bash?

    In this article, we will only be looking at Bash scripts. A couple of reasons why I like Bash for authoring shell scripts that interact with the file system (e.g. copying, renaming, or deleting files and folders):

    1. It is installed by default on Unix machines which, in my experience, is usually the operating system used on an external server (i.e. using ssh to access the command line on another computer). So, if your computer and a server are both Unix, the Bash knowledge applies to both.
    2. Bash syntax doesn’t seem to be dependent on versions (it’s been around much longer than JavaScript, for example). Actually, I’m not aware of Bash versions at all which is a positive thing, in my opinion.
    3. Bash is a declarative language specifically created for shell scripting, meaning there is common functionality built into the language. For example, in Node.js you need to require a module to rename a file, in Bash, that functionality is part of the language via the mv command.

    All that said, this is surely a developer preference and I am at the beginning of my shell scripts journey. I imagine for scripts that interact with data and perform API requests, JavaScript or another language might be preferred because Bash, the language, does not have built-in support for this functionality. Like with JavaScript, you may need to require external modules.