Shellscripting

Darkø Tasevski
InterVenture Tech
Published in
5 min readAug 22, 2018

Introduction to Shell programming

Photo by Adam Littman Davis - Unsplash

Small intro to shell programming

The Shell is the standard interface to every Unix and Linux system; users and administrators alike have experience with the shell, and combining commands into scripts is a natural progression. However, this is just a tip of the iceberg.

I’ve spent some time lately learning about shell and writing scripts, and I’ve realized that the shell is actually a full programming language with variables and functions, and also more advanced structures such as arrays (including associative arrays) and being so directly linked to the kernel it has native IO primitives built into it’s very syntax, as well as process and job control.

I’ve planned this as a series of a few posts, and I’ll try to be concise and on the point. Also, this article was originally published on DEV.

So, what is shellscripting?

Shellscripting is writing a series of commands for the shell to execute. It can combine lengthy and repetitive sequences of commands into a single and simple script, which can be stored and executed anytime, which is great for automating tasks. This reduces the effort required by the end user.

Script’s commands are being executed by the interpreter (shell), one by one, and everything you can type in the command line you can also put in the script.

Before running scripts, we need to set up permissions for execution with:

chmod 755 script.sh

We can then run the script (if in same directory) with ./script.sh via command line.

Variables and Shebang #

The hash-bang (also known as shebang) is a special pair of characters at the very start of the file, indicating that the executable that follows is the interpreter (and optional arguments) by which the script is to be executed, for example:
#! /bin/bash, #! /bin/zsh or for the best portability #! /bin/sh (this will run system shell, but won’t guarantee some advanced features).

Note that the most of code in this series is tested only with `bash` and `zsh` shell, most sh scripts can be run by Bash without modification, but some stuff wont work.

#! /bin/sh
sleep 90

When we execute the script that contains #! what actually happens is that interpreter is executed and the path used to call the script is passed as an argument. To confirm that, let’s say we have sleepy.sh script, then we can run the script with ./sleepy.sh &, where & is used to run the script as background process and returns the PID of the script execution process. We can then run ps -fp [PID] to see process info:

UID PID PPID C STIME TTY TIME CMD
505 65418 59985 0 7:09PM ttys000 0:00.01 /bin/zsh ./sleepy.sh
```
We can see here that under CMD ./sleepy.sh is passed to my /bin/zsh binary as an argument.

If a script doesn’t contain #! commands are executed with default shell, but it’s the best practice to be explicit as different shells have slightly varying syntax.

Also, we don’t have to use only shells as interpreters for scripts. We can also use other binaries like python:

#! /usr/bin/pythonprint “This is a Python script”$ chmod 755 hi.py
$ ./hi.py
This is a Python script

Variables

Without variables it is difficult to get much done: You can’t count, loop, or read input from the user or the environment, and you can’t change anything much. A variable is a chunk of memory to which you can store arbitrary data, and retrieve it again, just by referencing its name.

Syntax used to create a variable is: VARIABLE_NAME=”Value”. It’s important to note that variable names are case sensitive, and that, by convention, variable names should be all in uppercase. Also make sure to not use spaces after and before = sign, when declaring a variable.

By default all variables are global, and they have to be defined before used.
Variables can be defined in the functions (we’ll talk about them eventually), but we cannot access them before a function is called.

function declareVar(){
FUNC_VAR=1
}
# FUNC_VAR is not defined at this point and this will not return anything
$ echo $FUNC_VAR
declareVar # This is how we call a function in the shell
# FUNC_VAR is now available because the function has been called
echo $FUNC_VAR # Output: 1

Valid variable names can consist of letters, numbers, and underscores, except that number cannot be the first char in the name.

# Valid names
DARK_JEDI=”Vader”
GR4Y_J3DI=”Ahsoka”
Regular_Jedi=”Obi-Wan”
# Invalid names
3DARK_LORDS=”Vader Sidius Plagueis”
TWO-REBELS=”Solo Leia”
ONE@SHIP=”Ebon Hawk”
#! /bin/bash
MY_SHELL=”zsh”
echo “I like the $MY_SHELL shell” # Output: I like the zsh shell

We can also enclose the variable name in curly braces:

MY_SHELL=”zsh”
echo “I like the ${MY_SHELL} shell” # Output: I like the zsh shell

Curly braces syntax is optional unless you need to precede or follow up the variable with additional data, like so:

MY_SHELL=”bash”
echo “I’m ${MY_SHELL}ing on my keyboard!”
# Output: I’m bashing on my keyboard.

Without curly braces this wouldn’t work as the interpreter will take that ing following the name variable as a part of the variable name.

Another best practice is to enclose variables in quotes, when working with them, to prevent some unexpected side effects.

We can also assign the output of the command to a variable:

SERVER_NAME=$(hostname)
echo “You are running this script on ${SERVER_NAME}”

Local variables

Local vars are created with local keyword, and only functions can have the local variables, so they can only be accessed within the function where they’re declared. I’ve said above that by convention variables should be all in uppercase, but, as one user pointed out, you should only use ALL_CAPS_NAMES for environment variables or other stuff that's part of the shell's world. Variables local to scripts should be lower_snake_case to avoid collisions and hint that they're not going to have any effects outside the script.

function myFunc(){
local local_var=” I’m locally scoped”
}

It’s the best practice to use only local variables inside functions.

The addition of the local keyword makes bash a practical shell for writing more complex functions and libraries.

That’s it for now, I’ll write a bit about tests and conditionals in the next one. Thanks for reading, and if you have any question, just ask away!

--

--

Darkø Tasevski
InterVenture Tech

Front End Engineer, book worm and little of a writer himself.