Prerequisites:
To perform the steps that are demonstrated in this guide, you need the following components:
- A functional Linux system. Learn more about setting up an Ubuntu VM using VirtualBox.
- Access to a non-root user with sudo privilege.
- A suitable text editor. For example: Vim/NeoVim, Nano, Sublime Text, VSCodium, etc.
The Exec Command
The exec command isn’t a separate tool by itself:
Rather, it’s an internal command of the Bash shell:
As the description from the man page suggests, if a command is specified, exec replaces the shell with it, spawning no additional process. There are a handful of options available that modify the behavior of the exec command.
Basic Usage
By default, whenever running a command, Bash spawns a subshell and forks the command.
Here, the echo command prints the PID of the current shell. The Bash shell (PID: 978) spawns a new child process to work with the sleep command (PID: 8369).
Now, what if we run the sleep command using exec?
The parent Bash process is replaced by the sleep command. Upon successful execution, it does not return to the shell. Instead, the session is terminated.
Clean Environment
The default Bash configuration comes with a bunch of tweaks and environment variables. In certain scenario (debugging, for example), you may want to run your script/program in a clean environment. With the help of exec, we can launch a clean shell instance in place of the current one.
First, use the printenv command to list all the environment variables that are currently configured:
Now, use exec to launch a clean instance:
$ printenv
Launching a Different Shell
Besides Bash and “sh”, there are multiple other shell programs available, each with their unique perks. If a program/script requires a specific shell, you can use exec to replace the current Bash shell with the desired one.
In the following example, we replace Bash with “sh”:
$ exec sh
$ pstree -p
Using Exec in Scripts
With the basics out of the way, we can now start using exec in our shell scripts.
Example 1: Working with Different Shells
Check out the following script:
echo $SHELL
echo "echo zsh launched successfully" > zsh.sh
exec zsh zsh.sh
Here, the first echo command prints the current shell. By default, it should be Bash. Then, the exec command launches “zsh” to execute the “zsh.sh” script.
Run the following script:
Example 2: Overriding the Existing Process
Whenever calling a command/program, Bash spawns a new process. In most situations, it’s not a matter of concern. However, when working with a system with very limited resource (embedded hardware, for example), using exec to override the existing process in memory can help.
Check out the following script:
pstree -p
exec pstree -p
echo "hello world"
Here, the first pstree command shows the original layout of the process tree. Once the exec command is executed, the second pstree command replaces the running shell. The echo command on the last line didn’t execute.
Run the following script:
Since it was a part of the script, we return to the original shell upon successful execution.
As the exec command replaces the parent shell with a different command/program, any code after that becomes invalid. Be careful when using them in your scripts.
Example 3: Logging
The Bash shell offers 3 unique file descriptors to any running program/script:
- STDOUT (1): standard output, stores normal output
- STDERR (2): standard error, stores error messages
- STDIN (0): standard input
Using exec, we can redirect these file descriptors to a different location, for example: log files. It can help with debugging and logging in general.
Generally, if you want to redirect STDOUT and STDERR to a log file, you use the redirect operator:
$ monke 2>&1 | tee test.log
This method requires redirection at every point that you want to log. To solve this issue, we can use the exec command to create a permanent redirect for the shell session. Check out the following example:
> test.log
exec 1>>test.log
exec 2>&1
echo "hello world"
wrong_command
Here, the first line creates an empty log file. The first exec command establishes a permanent redirect of STDOUT to the log file. The second exec command redirects STDERR to STDOUT.
With this setup, all the outputs and error messages are dumped into the log file:
$ cat test.log
What if the script generates continuous log entries?
> test.log
exec 1>>test.log
exec 2>&1
while true
do
echo $RANDOM
sleep 5
done
Here, in the first portion, we create a permanent redirect of STDOUT and STDERR to our log file. The infinite while loop runs the echo command until we close it forcibly using “Ctrl + C”. The $RANDOM variable is a special variable that returns a random string every time it’s accessed.
To check the updating log entry, use the following tail command:
Note that this redirection only lasts for the shell session.
Example 4: Input from File
Similar to how we created a permanent STDOUT and STDERR redirect, we can also create one for STDIN. However, since STDIN is used for input, the implementation is a bit different.
In the following script, we take STDIN from a file:
echo "echo "hello world"" > input
exec < input
read line_1
eval $line_1
Here, in the first line, we use echo to generate the content of input_string file using redirection. The exec command redirects the content of input_string to STDIN of the current shell session. After reading the string, we use eval to treat the content of $line_1 as a shell code.
Run the following script:
Conclusion
We discussed about the exec command in Bash. We also showcased the various ways of using it in scripts. We demonstrated using exec to work with multiple shells, create memory efficient scripts, and redirect the file descriptors.
This is just a small portion of what can be achieved using the Bash scripting. Learn more about Bash scripting from the Bash programming sub-category.
Happy computing!