BASH Programming

How to Execute Commands from Within a Shell Script

In bash, executing commands from a shell script can be a little intimidating at first and may require a leap of faith. After all, commands executed within a bash script are not interactive. Here we will lay down the foundation to execute commands from within a shell script answering the following questions about commands: Where do they come from? What are they? How do we use them in a script?

Where do commands come from?

Commands in bash come from any of the following categories:

Bash itself (see builtins)

Bash comes with its own set of commands to allow access to its built-in features such as declaring arrays, reading lines from a file, and other features builtin to bash. We call commands of this category, bash builtin commands, or builtins for short.

Executables in your environment (see external commands)

By default, bash will inherit certain variables by default. This is observed in the case of the PATH variable including locations for executables that are referenced as external commands in bash. That is, if the curl command is in your path, it may be executed from within a bash script the same way as in interactive mode. We call commands of this category, external commands, or commands for short.

User-defined function (see functions)

Before executing external commands and builtins, bash checks if a function is defined. If it is the function is executed as a command. If it doesn’t, it proceeds down the order of precedence for commands. In order to execute function defined outside of a script, they must be declared with the -x attribute; otherwise, they may be included using the . Command. We call commands of this category user-defined functions or functions for short.

What are commands

A command is any word to be treated as a single point of entry of a program within the shell environment. In the case that the command is executed, the command itself and optional arguments are passed in as positional parameters, ${0}, ${1}, ${2}, … The zeroth positional parameter (${0}) designates the command itself and is unchanged in context. That is, unlike positional parameters in functions, ${1}, ${2}, … that may change depending on the context, ${0} is unchanged between function calls.

Commands are scoped depending on the location of declaration and attributes assigned as global, built-in to bash, or local to your bash program.

Here is a list of command types to know.

Builtin commands

These are first-class citizens of the bash universe, including characters such as ‘.’ ‘:’ ‘[‘ ‘]’ and reserved words like declare in bash. You count on these commands, contained in the list of bash builtin commands, to be available to use within your bash script.

Depending on the designation and version number of your bash interpreter some commands may not be available.

External commands

External commands are executables accessible outside of a bash script like curl. Unlike functions, external commands are not stored as variables.

The lower the precedence of a command type, the later the command may be interpreted. External commands have the lowest order of precedence in bash. That is before running an external command, the interpreter bash, will look for functions, then builtins, and finally try to see if a command exists externally. If not, you should see the following error.

bash: unknown-command: command not found

In a bash script, functions may override external command behavior if sharing the same name such as we’ve seen previously in curl bash examples. An example of a custom external command using a function follows.

curl() {
command ${FUNCNAME} ...
}

This works because functions have higher precedence than external commands and even bash builtins. The limitation is the allowed characters in a function name.

Note that the above example may be accomplished using an alias as follows.

alias curl=’
{
curl ...
}

In the case of aliases, the type of command may differ depending on the context in execution, whereas in the case of the custom external commands using function method, the entry point is always a function.

Functions

Functions rule in bash. Before looking at builtins and external commands, bash checks if a function defined by a candidate function name, the first word appearing on a line or after the ; character designating the end of a command line. The only exception is bash variables written in all-caps such as ${FUNCNAME}.

alias() { FUNCNAME=asdf ; echo ${@,,} ; }
alias curl='TEST CURL ALIAS' # ?

Simple commands

Simple commands are defined in the bash man pages as a word followed by optional arguments. In context, a simple command may be either a builtin, external command, or function.

How to execute commands from within a bash script

Now that we know what types of commands are available, we can expand into how to use them in your scripts. First, we’ll need to know how command precedence works in bash.

Here are some ways to control precedence in a bash script.

Let bash decide

command_name

For the most part, especially in the beginning, we just let bash decide which command to use. However, there are cases when your intent may not be interpreted correctly by bash. Such is the case when function names and external commands or builtins overlap.

Execute an external command

command command_name

Suppose that there is an external command command_name that is available in interactive mode and you wish to use it in a bash script. We can explicitly tell bash that command_name is an external command using the command builtin.

External command examples

External command examples assuming the following are installed:

file
git
figlet
Example: Get file type and info
{ # get file type and info
file ${infile} # (1,2)
}
# (1) command, file
# (2) infile={Path to file}
Example: Stage modified and new files in git
{ # stage files in git
git add . # (1)
}
# (1) command, git
Example: Create ascii art using figlet
{ # create ascii art
figlet ${message} # (1,2)
}
# (1) command, figlet
# (2) message={Message to display as ascii art}

Execute a builtin command

builtin command_name

Suppose that command_name is one of the commands defined as a builtin in bash. To let bash know that we want to run command_name as a builtin we use the builtin builtin.

Builtin command examples
Example: How many builtins?
builtin{,}{,,}{,,,} # how many builtins?
Example: Phantom declare
{
declare() { echo oops! ; }
declare –xf declare # ?
}

Conclusion

It is fairly easy to execute a command from within a bash shell script. There are three main command types. Knowing how simple commands are interpreted in bash can lead to improved control over what type of command is executed in runtime.

About the author

Nicholas Shellabarger

Nicholas Shellabarger

A developer and advocate of shell scripting and vim. His works include automation tools, static site generators, and web crawlers written in bash. For work he tools with cloud computing, app development, and chatbots. He codes in bash, python, or php, but is open to offers.