How to convert Bash code into custom commands


In this quick note I will be reviewing how to add custom commands in Bash for a better user experience.

The idea is the following: I have a script written in Bash and I am interested in being able to use it as another command. My goal is to understand how to do that.

Before I start: this text is part of my “Basic guide to learning how to program in Bash.” It is my learning journal, in which I am creating notes to better understand the things I am studying in a self-taught way.

The problem:

I have a program called “phrase.sh”, and the content of this program is as follows:

#!/bin/bash
echo "This text is a draft".

The program “phrase.sh” is located in the “home” directory, inside the “example” folder. I can realize that with the “ls” command:

gus@apunte:~/example$ ls
phrase.sh

Now, the first thing I have to do if I want to invoke this program is to give it permission to run.

chmod +rx phrase.sh

However… Can I execute this program from anywhere?

At first this code can only be executed if the working directory, the directory where the program is stored.

In my example, phrase can only be invoked when the terminal is open in the “example” folder.

And I do this by typing:

./phrase.sh

In the following way:

gus@apunte:~/example$ ./phrase.sh
This text is a draft

I have to remember that the “$” sign indicates that I am positioned in that directory.

The problem is that although the program is working, it is impractical to be able to use it only in this way.

To compare it with a system script, I can use the “date” command from any directory. No matter what directory I am in, if I type “date” I get the date and time in the terminal.

In contrast, if I type “phrase.sh” in a directory other than “/home/example”, I get an error. It tells me that the command does not exist.

gus@apunte:~$ ./phrase.sh
bash: ./phrase.sh: No such file or directory

There is a quick way to solve this, but it is an impractical way. I can write all the way to the directory where the program is hosted.

Let’s say that knowing that my program is in the “example” folder, I can invoke it by typing:

~/example/phrase.sh
This text is a draft

If I write the path all the way to the program, I can run the script from any directory. But it is a difficult solution to remember and certainly much less concrete than just typing something like “date”.

And it can become more difficult if the script is inside several folders.

But there is an easier way to do it, and that is what I am going to detail below.

What am I looking for now? Basically I want to be able to run these new programs from any directory, just as I do with other system commands. That without having to clarify each time I do it the path to the directory where the code is located.

How to add new commands in Linux

What is the command path?

Command Path is a system environment variable: a text file containing a list, which tells the system in which directories to look for commands that can be executed.

When I invoke a command, the shell looks for that command in the different directories defined in the Command Path. If the command is in one of those directories, the shell can use it.

On the other hand, if the command does not appear anywhere in the list, the terminal returns a “command not found” error.

The benefit of adding a script to the Command Path is that our program can then be found from any directory, just like a regular command.

I can find this list from the terminal with:

echo $PATH

What appears on the screen is a series of directories included within the path file, each separated by a colon ” : ” like this:

/home/gus/.local/bin:/usr/local/bin:/usr/sbin:/usr/bin:

When I invoke a command, the system looks in each of these (for now four) folders. And if it finds the command, it goes on to execute it.

This search starts from the first directory (from left to right) until it reaches the last one. It does not matter if there is more than one command with the same name in more than one folder, it always executes the first one it finds following that order.

Solution 1: move the program

The simplest solution is to add my program in one of the directories that already exist in the Command Path.

If I want the command to be accessible to other system accounts I can use the directory “/usr/local/bin”.

And if I need the program to be invoked only by the root account, I need to save the program in the “/usr/local/sbin” directory.

But in my particular case I can add “phrase.sh” in the directory “/home/gus/.local/bin” which is already inside the command search path for the system. This directory is not global, and the new command is going to apply only to my account.

So, after moving the program to that folder, I can now invoke my program no matter what directory the terminal is open in:

gus@apunte:~$ phrase.sh
The example works

Is it necessary to save the bash script with the “.sh” extension?

As far as I could notice, in Linux it is not necessary to add the extension “.sh” in the program file so that the system understands it as executable.

Understood that way, I can name my program as “phrase”, without the extension, so that typing it in the terminal becomes even simpler:

gus@apunte:~$ phrase
The example works

Solution 2: Modify the Command Path

Well, I have my example directory. In that folder I keep the scripts I create while writing these notes and there is the program I want to run now.

Maybe it is not the best idea, but I can add this “examples” directory inside the command path.

To edit the command path, I have to go first to my home directory (in my case that is “/Home/gus”).

There I will find a file called “.profile” which is hidden at first. To see that file I have to right click inside the folder and activate “Show Hidden Files”.

To modify “.profile” it is necessary to open it with a plain text editor. I can also edit it directly from the terminal, using a program like Nano.

Now I have to go to the end of the document. I am going to update it by adding the path to the new directory, preceded by:

export PATH=$PATH:/home/directory

In my case this path looks like:

export PATH=$PATH:/home/gus/example.

The idea is to put that line of text at the end of the document. And that’s it, I can save it.

For the change to take effect it is necessary to reboot the system. I can also use the source command to have the System Path read/updated again, making the new modification take effect.

So, instead of rebooting the machine, I open a new terminal and then i type:

source ~/.profile

Now when I use the command

echo $PATH

I will be able to test if the modification took effect, the new appended folder should appear at the end of the list.

The best thing about adding a new path at the end of the document is that, as this list is read in order, I will avoid making the mistake of jumping ahead of an existing address or command.

Likewise, the best thing is that the new folder and my custom programs do not have names that are the same as commands already used in the system.

But if something went wrong and I want to see what the command path looked like in its original values, I can use the command:

source /etc/environment

Also that last command will restore my current terminal to the original command path values, at least until I close it again. If I need to delete this new directory, I simply reopen the “.profile” file and delete the line I put before.

And if I want to know the path to the directory of a particular command I can do it with the command “which” as follows:

which commandname

Conclusion:

With this I end the note to understand how to convert Bash code into custom commands.

This is the expanded and corrected version of an old note I had written. I hope this new version is better than the previous one.

If you find any errors in the text or you think I can improve it, I would like to read your comments. You can also find my e-mail address in the contacts section.