1. Overview
Dynamic execution of commands based on user input, configuration, and code flow can make our Bash scripts more powerful. We can achieve a lot using this approach, such as selecting commands for execution at runtime, providing customized application behavior based on environment variables, etc.
In this tutorial, we’ll learn multiple ways to run variable content as a command in Bash.
2. Understanding the Scenario
Let’s take a simple use case of showing the date and time based on user-provided input. For this purpose, we’ll write the show-datetime.sh Bash script as a template script for later reusability:
$ cat show-datetime.sh
#!/bin/bash
cmd="date"
if [[ -n "$1" ]]
then
cmd+=" --date $1"
fi
# run command stored in cmd variable
The idea is that, by default, we’ll show the current date and time. However, if the user provides a specific date as an argument to the script, we’ll use it to show the date. As a result, we generate the command dynamically in the cmd variable.
Eventually, we want to run the command stored in the cmd variable. So, let’s go ahead and learn different approaches to achieve this behavior.
3. Using echo and bash
Let’s start by looking at how we can execute the command stored in the cmd variable by using echo and pipe with the help of bash:
echo "$cmd" | bash
Now, let’s use the show-datetime.sh script as a template to write the show-datetime-echo.sh script that executes the command:
$ cat show-datetime-echo.sh
#!/bin/bash
cmd="date"
if [[ -n "$1" ]]
then
cmd+=" --date $1"
fi
echo "$cmd" | bash
Lastly, let’s run our script to see it in action:
$ ./show-datetime-echo.sh
Sun Sep 3 07:59:40 UTC 2023
$ ./show-datetime-echo.sh 2023-04-01
Sat Apr 1 00:00:00 UTC 2023
Great! It works as expected.
4. Using eval
The eval command is perfect for evaluating and executing a string as a command. While using it, we can provide the string as an argument, and eval executes it in the current shell environment.
So, let’s see how we can write the show-datetime-eval.sh script by extending the template script show-datetime.sh:
$ cat show-datetime-eval.sh
#!/bin/bash
cmd="date"
if [[ -n "$1" ]]
then
cmd+=" --date $1"
fi
eval "$cmd"
Further, let’s verify our script for the two scenarios:
$ ./show-datetime-eval.sh
Sun Sep 3 08:25:34 UTC 2023
$ ./show-datetime-eval.sh 2023-04-01
Sat Apr 1 00:00:00 UTC 2023
Fantastic! It looks like we nailed this one.
5. Using Command Substitution
One of the merits of using the command substitution approach is that we can store the output in a variable for later use. In this section, we’ll explore using command substitution to execute a variable command available as a string.
First, let’s take a look at the show-datetime-cmd-subs.sh script that uses the command substitution approach to solve our use case:
$ cat show-datetime-cmd-subs.sh
#!/bin/bash
cmd="date"
if [[ -n "$1" ]]
then
cmd+=" --date $1"
fi
output=$($cmd)
echo "$output"
Next, let’s focus on the last two lines of the script that execute the command stored in the cmd variable and then display it:
output=$($cmd)
echo "$output"
It’s worth noting that we enclose the command value within $() for execution. Further, the result of execution is stored in the output variable for later use in display.
Finally, let’s run the show-datetime-cmd-subs.sh script for two scenarios, namely, without and with custom date argument:
$ ./show-datetime-cmd-subs.sh
Sun Sep 3 10:23:55 UTC 2023
$ ./show-datetime-cmd-subs.sh 2023-04-01
Sat Apr 1 00:00:00 UTC 2023
Excellent! It works just fine.
6. Using Process Substitution
Process substitution in Bash provides a way to treat the output of a command as if it were a file. To understand it better, let’s take a look at its usage:
$ cmd1 <(cmd2)
Here, the result of the execution of the cmd2 command serves as a file and is fed to the cmd1 command as an input.
Now, let’s apply this concept to write the show-datetime-process-subs.sh script to solve our use case:
$ cat show-datetime-process-subs.sh
#!/bin/bash
cmd="date"
if [[ -n "$1" ]]
then
cmd+=" --date $1"
fi
bash <(echo "$cmd")
We must note that the last line of the script is responsible for executing the command available in the cmd variable. Further, we used echo and bash commands in the two processes.
Lastly, we must run this script and confirm that it’s working correctly:
$ ./show-datetime-process-subs.sh
Sun Sep 3 11:48:36 UTC 2023
$ ./show-datetime-process-subs.sh 2023-04-01
Sat Apr 1 00:00:00 UTC 2023
The output looks correct.
7. Using Here-String and Here-Doc
In this section, we’ll explore how to use here-string and here-doc to solve our use case of executing a variable content as a command in Bash.
7.1. With Here-String
First, let’s see how to write a simple statement that executes a command with input from a herestring:
$ command <<< "herestring"
Next, let’s write the show-datetime-herestring.sh script for showing the date based on user input:
$ cat show-datetime-herestring.sh
#!/bin/bash
cmd="date"
if [[ -n "$1" ]]
then
cmd+=" --date $1"
fi
bash <<< "$cmd"
We must focus on the last line of the script to see that the value stored in the cmd variable is served as a here-string. Further, we execute it with the help of bash.
Finally, let’s verify our script by running it for the two scenarios:
$ ./show-datetime-herestring.sh
Sun Sep 3 12:08:05 UTC 2023
$ ./show-datetime-herestring.sh 2023-04-01
Sat Apr 1 00:00:00 UTC 2023
It works fine.
7.2. With Here-Doc
Moving on, let’s see how to supply multiple lines of text as input to a command using here-doc:
$ command <<EOF
text
more text
EOF
Further, let’s write the show-datetime-heredoc.sh using here-document by using the show-datetime.sh template script:
$ cat show-datetime-heredoc.sh
#!/bin/bash
cmd="date"
if [[ -n "$1" ]]
then
cmd+=" --date $1"
fi
bash << EOF
$cmd
EOF
It’s important to note that we’ve used the value stored in the cmd variable as the first line of the here-doc. Further, the text from here-doc becomes the input for bash.
Like earlier, let’s conclude our approach with a set of verifications:
$ ./show-datetime-heredoc.sh
Sun Sep 3 12:37:14 UTC 2023
$ ./show-datetime-heredoc.sh 2023-04-01
Sat Apr 1 00:00:00 UTC 2023
Great! It looks like we’ve got this one right.
8. Using awk
In this section, we’ll use awk for executing the variable content as a command. For this purpose, we can use the system() function, as it allows us to run a shell command from within the awk script.
Let’s go ahead and take a look at the show-datetime-awk.sh script based on the show-datetime.sh template script:
$ cat show-datetime-awk.sh
#!/bin/bash
cmd="date"
if [[ -n "$1" ]]
then
cmd+=" --date $1"
fi
awk -v cmd="$cmd" 'BEGIN {system(cmd)}' /dev/null
Let’s focus on the last line of the script and understand the nitty gritty of the logic. Firstly, we use the -v option to pass the value of the cmd shell variable as a parameter to the awk script. Secondly, we call the system() function within the BEGIN block to execute it only once. Lastly, the logic doesn’t rely on any input, but it’s a mandatory argument, so we pass /dev/null as its value.
Like always, it’s good to verify that our code is working fine:
$ ./show-datetime-awk.sh
Sun Sep 3 11:21:15 UTC 2023
$ ./show-datetime-awk.sh 2023-04-01
Sat Apr 1 00:00:00 UTC 2023
9. Using sed
Alternatively, we can use a sed script to execute a variable content as a command. Further, we’ll need to use the substitution (s) command with the /e flag to achieve this goal.
First, we can start by looking at the show-datetime-sed.sh shell script in its entirety:
$ cat show-datetime-sed.sh
#!/bin/bash
cmd="date"
if [[ -n "$1" ]]
then
cmd+=" --date $1"
fi
echo "$cmd" | sed 's/.*/&/e'
Next, we should focus on the last line of the script that passes the value of the cmd variable as input to the sed script. Moreover, it does a no-op substitution and uses the /e flag for execution of the replacement text as a command.
Finally, let’s see our script in action and verify its output:
$ ./show-datetime-sed.sh
Sun Sep 3 11:25:58 UTC 2023
$ ./show-datetime-sed.sh 2023-04-01
Sat Apr 1 00:00:00 UTC 2023
Our script is working correctly.
10. Conclusion
In this article, we learned how to run variable content as a command in Bash. Furthermore, we explored multiple command-line utilities, such as eval, sed, awk, and so on, to solve the use case.