Bash Exec Command with Examples

Written by: Madhur Batra   |   Last updated: July 12, 2023

1. Introduction

Let's unlock the power of bash scripting with this guide on exec command. Deepen your understanding of process management, command execution, and input/output redirection from the command line.

2. Understanding the exec Command

Generally, when we execute a command in the Bash shell, it spawns a new child process and runs the command in the context of this process. But, when running a command using exec, it replaces the current shell process with a new process. The control never return to the shell script.

So you may think what fork does? both fork and exec are used to create processes. But both work differently. Basically, when a process calls fork it duplicates itself to create a child process whereas exec doesn't create a new process but simply replaces the current one. It's very common that many applications use it together.

Let's look into the basic syntax of exec:

exec [command [arguments]]

Where

[command [arguments]] : The command exec to execute. If any argument command has it can be passed. If the command is not used exec can be used to redirect the shell's input or output.

Overview of exec with redirection

CommandDescription
Redirect standard output to a fileexec > file.txt
Redirecting standard error to a fileexec 2> errorfile.txt
Redirecting both standard output and standard error to a fileexec >outanderrorfile.txt 2>&1
Open a file for readingexec 3< datefile.txt

3. Use Cases of exec command

Let's look deep dive into some use cases of the bash exec command with examples.

3.1 Using exec to Execute Commands

When a bash command is executed, generally it invokes the combination of fork() and exec(). Internally the parent will first fork to create a child process and then the child process will use exec to replace itself with the new program.

Let’s try to understand this with an example:

Executing ps command to get the list of currently running processes.

$ ps
PID TTY          TIME CMD
1002 pts/2    00:00:00 bash
1101 pts/2    00:00:00 ps

The process id of the current shell is 1002.

Let’s execute a sleep command in this shell and observe the process tree.

$ sleep 10
$ ps -ef --forest
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 13:44 ?        00:00:01 /sbin/init
root           2       1  0 13:44 ?        00:00:00 /init
root         368     367  0 13:44 ?        00:00:00  |   \_ /init
linuxopsys    1002     888  0 15:01 pts/2    00:00:00  |     \_ -bash
linuxopsys    1094    1002  0 15:05 pts/2    00:00:00  |    	\_ sleep 10
linuxopsys    1036     888  0 15:03 pts/3    00:00:00  |      \_ -bash
linuxopsys    1095    1036  0 15:05 pts/3    00:00:00  |     	 \_ ps -ef --forest

Observe the process id of the sleep command. A new child process that has PID = 1094 is spawned which executes the command.

But, when we execute commands using exec, it simply invokes the exec() loading the passed command in the currently running process.

Let’s run the sleep command in exec mode this time:

$ exec sleep 10

Observing the process tree once again:

$ ps -ef --forest
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 13:44 ?        00:00:01 /sbin/init
root           2       1  0 13:44 ?        00:00:00 /init
root         368     367  0 13:44 ?        00:00:00  |   \_ /init
linuxopsys    1002     888  0 15:01 pts/2    00:00:00  |     \_ sleep 10
linuxopsys    1036     888  0 15:03 pts/3    00:00:00  |     \_ -bash
linuxopsys    1108    1036  0 15:08 pts/3    00:00:00  |   	 \_ ps -ef --forest

This time we see that the sleep command replaces the bash command itself in the process 1002.

From this, we can understand the parent process can continue with other tasks while the child executes new programs.

3.2. Using exec command to replace the login shell

Suppose our default login shell is bash and we want to replace it with sh, this can be done by using the exec command.

Example:

$ cat ~/.bashrc
# .bashrc

## Switch to sh
exec sh

Here in the example the exec sh command in a ~/.bashrc file replaces the current Bash shell with a Bourne shell (sh) every time a new non-login interactive Bash shell is started.

Sourcing .bashrc to reflect the changes in this session.

$ source ~/.bashrc

3.4. Invoking another program from a shell script

Let’s see how we can use the exec command to call other programs from a shell script and let it execute in the same session.

#!/bin/bash

function invokeScriptAndReturn() {
        ./listFilesInDirectory.sh
        echo "Executing in function invokeScriptAndReturn"
}

function invokeScriptAndTerminate() {
        exec ./listFilesInDirectory.sh
        echo "Executing in function invokeScriptAndTerminate"
}

invokeScriptAndReturn
invokeScriptAndTerminate
script output

The main difference between the two functions is that invokeScriptAndReturn will continue execution of the script after listFilesInDirectory.sh completes, whereas invokeScriptAndTerminate will replace the current process with listFilesInDirectory.sh, and the rest of the script will not be executed.

3.5. Using exec with find command

The exec command can be used in conjunction with find to execute other operations (mv, cat, cp, rm, grep, wc, sed, etc) on each file that matches the search criteria.

Example exec with find:

Let's assume we have the following files in the current directory.

$ ls
invokeProgramUsingExec.sh  listFilesInDirectory.sh  txtFile1.txt  txtFile2.txt
$ cat txtFile1.txt
Bag of words:
Slow, Fast, Quick, Rapid, Brisk, Speedy, Abrupt, Sharp, Instant, Relaxed, Easy, Unhurried
$ cat txtFile2.txt
Bag of words:
Sushi, Rice, Wedges, Noodles, Pizza, Burger, Pasta, Chicken, Egg

The following command search through files in the current directory and its subdirectories and specifically looks for files whose names start with "txtFiletxtFile", and within those files, it searches for words that start with 's' (case insensitive).

$ find . -name "txtFiletxtFile*" -exec grep -iwoe "s[a-zA-Z]*" {} \;
Slow
Speedy
Sharp
Sushi

3.6. Using exec to redirect stdin, stdout, and stderr

With the exec command, we can also modify the file descriptors and redirect logs to the text files instead of stdout. This comes in very handy during debugging especially when dealing with large scripts. 

Read from a file by changing stdin

Let’s take an example to understand how we can manipulate stdin to reference and take inputs from a file instead of the terminal.

Reusing the text file from the previous example.

#!/bin/bash

exec < txtFile1.txt
i=0

while read line;
do
        echo "Line $i: $line"
        i=$((i+1))
done
Read from a file by changing stdin

exec < txtFile1.txt tells the Linux to take inputs from the text file instead of the terminal. Within the loop, we iterate over the file and print it to stdout.

Write to a file by redirecting stdout and stderr

Similar to stdin, stdout, and stderr can also be manipulated to redirect the output to a file instead of the terminal.

See the following example:

#!/bin/bash
outFile="outTxtFile.txt"
exec 1>$outFile

echo "Text from txtFile1.txt:-"
while read -r line;
do
        echo "Line $i: $line"
done < txtFile1.txt

echo "Text from txtFile2.txt:-"
while read -r line;
do
        echo "Line $i: $line"
done < txtFile2.txt

exec 2>&1
echo "Following line will throw an error that gets added to the output file"
dsfd
run the bash script and output of outTxtFile.txt file
  • We create an output file and use exec 1> command to redirect the stdout to this file.
  • Next, we iterate over the text files and echo the output to stdout.
  • We also redirect stderr to stdout by using the command exec 2>&1.
  • We run an invalid command dsfd. Bash recognizes this and outputs the error to the stdout.

3.7. Restoring the file descriptors

Modifying the file descriptors is a useful tool but we should always take care to revert it back to the original state once done.

Let’s see how we can do this:

#!/bin/bash

exec 11<"txtFile1.txt"
exec 12>&1
exec > "outTxtFile2.txt"
echo "This output goes in outTxtFile2.txt"

while read -u 11 line
do
        echo $line
done

exec 11<&-
exec 12>&-
output of bash script
  • exec 11<"txtfile1.txt"  opens a file descriptor 11 and references a text file.
  • exec 12>&1 opens a file descriptor 12 and references the stdout.
  • exec > "outTxtFile2.txt" finally tells the shell to redirect stdout to the output file.
  • We iterate over the file descriptor 11, read lines from the input file, and output it to the output file 
  • instead of stdout.
  • Finally, we close the input and the output file descriptors 11, and 12 using the command exec 
  • 11<&- and exec 12>&- respectively.

About The Author

Madhur Batra

Madhur Batra

Madhur Batra is a highly skilled software engineer with 8 years of personal programming experience and 4 years of professional industry experience. He holds a B.E. degree in Information Technology from NSUT, Delhi. Madhur’s expertise lies in C/C++, Python, Golang, Shell scripting, Azure, systems programming, and computer networking. With a strong background in these areas, he is well-equipped to tackle complex software development projects and contribute effectively to any team.

SHARE

Comments

Please add comments below to provide the author your ideas, appreciation and feedback.

Leave a Reply

Leave a Comment