Running shell scripts is one of the essential everyday tasks we tackle as Linux users. With them, we install software, automate typical problem solutions, maintain and configure the system, collect statistics, etc.
In this tutorial, we’ll see what to do with scripts so that we can run them like any other programs, without using the sh or bash commands.
2. Shell Terminology
A Linux (or Unix) shell is a program for interpreting user input. With its help, we run other programs and see the results on the screen after completion of work. We need to know which shell we are using to run shell scripts because there are many of them.
Historically, the first one was c-shell, csh, replaced later by tcsh. Occasionally we meet the Korn ksh, which uses minimal resources. Zsh is getting more popular, too.
Modern Linux distros, however, use bash as the default shell. It’s a standard de-facto. While running, scripts execute built-in commands and programs located in different places or directories such as /bin, /usr/bin, etc.
A PATH is a shell variable to hold a string listing such directories. Each registered directory is separated from another one by a column. Each time we type a program name to run, the shell looks for it in the places pointed by PATH and starts the program if it finds it.
When we log in, the system assigns us a shell named a login shell. It’s the first process executed under our user ID when we log in for an interactive session.
3. Running Scripts
Generally, there are two ways of starting scripts, direct and indirect. If we need to execute the script only a few times, we do it with the so-called direct method. To run scripts as ordinary programs, we use the indirect method.
3.1. Direct Call From the Shell
The simplest method to run the script is to call it directly with the required shell. Assuming we have a bash script named runme, to run it, we simply specify it as a bash argument:
$ bash runme
Such a way of using scripts will always run without problems if we have the bash in our system installed. Otherwise, we have to install it.
3.2. Indirect Call From the Shell
More often, however, we start the script as follows from the directory where it’s present:
Linux kernel will recognize this file as a script and call the related shell to interpret it. The type of shell to execute is specified by the first line of the script itself. This line is often called ‘hashbang’, ‘shebang‘, or ‘sha-bang’.
Because the script is an ASCII file, we may use the command head to view this line:
$ head -1 runme #!/usr/bin/bash
This form of using scripts might sometimes cause several issues. The first one is “permission denied”. It means we’ve forgotten to make the script executable:
$ ./runme bash: /home/john/runme: Permission denied
To succeed in this case, we need to set up this script file as executable with the command chmod:
$ chmod +x runme
Here’s the second possible issue:
$ ./runme bash: ./runme: /bin/bash: bad interpreter: No such file or directory
Such output means the hashbang does not point to the right place. The bash is present, but /usr/bin/bash is missing, so the script cannot run. To correct the issue, we’ll first look for the right location of bash:
$ which bash /usr/bin/bash
Knowing the correct location, we may either fix the script’s hashbang with an editor or create a symbolic link to the actual bash with the path mentioned in the script:
$ sudo ln [-s] /usr/bin/bash /bin/bash
As a rule, we use symbolic links if /usr and /bin are not within the same partitions. Otherwise, a hard link is better.
4. Enhancing Script Usage
If we use the script quite often, we’ll prefer to call it without a leading path to its location. For example, instead of typing /home/my_scripts/runme, we want to just type runme. If we do this with joe, vi, etc., why not add our scripts to this set? Let’s see how to do this.
4.1. Setting the Script for All Users
We may start with the obvious by making the script executable, as we saw above.
Next, we need to verify which shell this script is using. Generally, if our login shell is the same as in the script, we can use it without the first line, but it’s better to have this shebang line in the script file. So, using our favorite editor, we’d edit this line if necessary.
For using scripts the same way we are running other programs, their location should be listed in $PATH. According to the Filesystem Hierarchy Standard, we can use the suggested directory /usr/local/bin or /opt/bin for keeping our scripts.
Firstly, we check the $PATH:
$ echo $PATH /usr/local/bin:/usr/bin:/bin
As the output displayed, we may use /usr/local/bin, so we’ll put our script in there, if, of course, we can use sudo:
$ sudo mv ./runme /usr/local/bin/
All done, now any user can execute the script runme as an ordinary program.
4.2. Setting the Script for Current User
If we cannot act as root, let’s install the script just for ourselves. No standard dictates the organization of the user-created files under $HOME, but to keep the things in the right place, we usually hold runnable programs in the directory $HOME/bin.
So, firstly we check if the directory bin exists under $HOME, and if not, we create it:
$ [ ! -d "$HOME/bin" ] && mkdir $HOME/bin
Secondly, we move the script to this directory:
$ mv ./runme $HOME/bin
Thirdly, we need to check that the directory is listed in our $PATH:
$ [[ $PATH =~ $HOME/bin ]] && echo 'yes' || echo 'no' yes
So, in case yes, we are lucky and have the script installed for simple usage. If the output is no, we have to correct our $PATH.
4.3. Correcting $PATH
Generally speaking, each distro has its policy of changing this variable, but we can always change or create the file $HOME/.bashrc and set this variable from there. We need to add the line EXPORT PATH=$HOME/bin:$PATH to that file with an editor. Or, we may do it just in one command line:
$ echo echo EXPORT PATH=$HOME/bin:$PATH >> $HOME/.bashrc
To make this change effective, we’ve to open another terminal or log out and log in again.
In this article, we discussed the basics of shell scripts and how to use them just as we use other programs. In all examples, we assumed to operate with bash, but it’s easy to extend all the shown tricks for other shells.