1. Overview
In Linux, we can execute shell commands by passing them as arguments to either eval or bash -c. However, there are key differences between eval and bash -c, and these differences may define how we use either command.
In this tutorial, we go over eval and bash -c and highlight their primary differences.
2. eval
eval is a built-in shell command that combines its arguments into a single string before evaluating said string as shell commands and executing them accordingly. In other words, when we pass arguments to eval, it takes the arguments as one string. Then, it parses the string as shell input before executing the commands formed from the parsed strings.
The eval command has a straightforward syntax:
$ eval [arg ...]
We’ll illustrate how eval works using the apt update command.
First, let’s run the command without eval:
$ sudo apt update
Hit:1 http://deb.debian.org/debian bullseye InRelease
...truncated...
166 packages can be upgraded. Run 'apt list --upgradable' to see them.
Next, we’ll test it out with eval:
$ eval sudo apt update
Hit:1 http://deb.debian.org/debian bullseye InRelease
...truncated...
166 packages can be upgraded. Run 'apt list --upgradable' to see them.
As we can see, we get the same output whether we run apt update with or without eval. This is because eval could parse the apt update string into known commands. Now, let’s see what happens when we pass non-command arguments to eval:
$ eval linux on baeldung
-bash: linux: command not found
Of course, when we execute eval with invalid arguments/commands, we get an error message telling us “command not found”. Since eval evaluates its arguments and executes them as commands, it will try executing linux on baeldung as a command. But, since the first argument in the string is not even a command, it will throw an error when trying to execute that first argument.
Say the first argument was a valid command but the second wasn’t, the error message might be different:
$ eval cp on baeldung
cp: cannot stat 'on': No such file or directory
We had a different result since we used cp as the first argument in the eval string. The eval builtin command could execute cp accordingly. So, the new error message is the same as we’d get when trying to cp a non-existent file or directory.
Using eval comes with potential security risks. For one, i****f we use it on a string whose safety is unverified, we might just be executing malware. Unfortunately, if that happens, our system will take a hit.
3. bash -c
bash -c is simply the bash command with the -c option. Like eval, bash -c executes command strings passed to it. It’s also a potential security risk, albeit relatively less risky than eval.
When used correctly and given the same commands, eval and bash -c might return the same output, depending on the command. But under the hood, they do not work the same. Let’s look at two key areas in which these are different: execution environment and syntax.
3.1. Execution Environment
The primary difference between eval and bash -c is their execution environment. While bash -c executes its command strings in a different shell, eval executes commands in the current shell.
Since bash -c executes commands in a different shell, changes made by the commands may not change anything in the current shell. But with eval, the commands affect the current shell.
3.2. Syntax
A major syntactic difference between eval and bash -c is the use of quotation marks. When using eval, we may or may not wrap the command strings in quotation marks. eval will still combine them into a string and parse them accordingly.
On the other hand, when using bash -c, quotation marks are essential, especially when trying to execute commands that have arguments or options.
Without quotation marks, bash -c will only execute the first argument passed to it. The second argument will be assigned to $0, and then every other argument after will be treated as a bash positional argument.
Let’s run apt update using bash -c with quotation marks:
$ bash -c "sudo apt update"
Hit:1 http://security.debian.org/debian-security bullseye-security InRelease
...truncated...
Reading state information... Done
166 packages can be upgraded. Run 'apt list --upgradable' to see them.
Now, let’s try the same command without quotation marks:
$ bash -c sudo apt update
usage: sudo -h | -K | -k | -V
...truncated...
usage: sudo -e [-AknS] [-r role] [-t type] [-C num] [-D directory] [-g group] [-h host] [-p prompt] [-R
directory] [-T timeout] [-u user] file ...
As we see above, when we executed the command with quotation marks, it went fine. But when we did the same thing without quotation marks, the command wasn’t successful. We got the same output we’d get if we ran sudo alone. In other words, bash -c executed only the first argument – sudo – because we didn’t wrap the other arguments together with the first in quotes.
4. Additional Differences Between eval and bash -c
Besides the execution environment and syntax, there are two other key differences between the two approaches: execution speed and security risk. Let’s take a brief look at each of these differences.
4.1. Execution Speed
Compared to eval, bash -c might have a slower execution speed in some cases. This is because bash -c commands are executed in subshells, which are generally slow.
Subshells are slow for many reasons, most of which are a result of the resources and time consumed when creating them. With eval, a new shell is not created, so execution goes on without any significant extra overhead.
4.2. Security Risk
Both eval and bash -c can be security risks if not used correctly. However, of the two, eval might just be riskier. One major reason eval carries a potentially higher risk is that it executes commands in the current shell.
Contrarily, bash -c runs commands in subshells. In other words, eval‘s scope of execution is not as limited as bash -c‘s.
As such, if both were used on a command that compromises security, the effects of running it in eval would be more widespread than in bash -c.
5. Conclusion
In this article, we talked about eval and bash -c. We went over their similarities before highlighting their differences.
While eval generally executes commands faster than bash -c, it might just be a bigger security risk than bash -c. But then, its syntax is relatively less stringent than bash -c.