Quotes in shell scripts do not behave differently from quotes in shell commands.
With the $(gpg -d filename.gpg)
syntax, you are not executing a shell script, but a regular single command.
What your command does
- It executes
gpg -d filename.gpg
- From the result, it takes the first (IFS-separated) word as the command to execute
- It takes every other (IFS-separated) words, including words from additional lines, as its parameters
- It executs the command
From the following practical examples, you can see how it differs from executing a shell script:
- Remove the word export from filename.gpg: the command is then
SOME_ENV_VAR='123'
which is not understood as a variable assignment (you will get SOME_ENV_VAR='123': command not found
).
- If you add several lines, they won't be understood as separated command lines, but as parameters to the very first command (
export
).
- If you change
export SOME_ENV_VAR='123'
to export SOME_ENV_VAR=$PWD
, SOME_ENV_VAR will not contain the content of variable PWD, but the string $var
Why is it so?
See how bash performs expansion when analyzing a command.
There are many steps. $(...)
is called "command substitution" and is the fourth step. When it is done, none of the previous steps will be performed again. This explains why your command does not work when you remove the export
word, and why variables are not substituted in the result.
Moreover "quote Removal" is the last step and the manual reads:
all unquoted occurrences of the characters ‘\’, ‘'’, and ‘"’ that did
not result from one of the above expansions are removed
Since the single quotes resulted from the "command substitution" expansion, they were not removed. That's why the content of SOME_ENV_VAR is '123'
and not 123
.
Why does eval
work?
Because eval triggers another complete parsing of its parameters. The whole set of expansions is run again.
From the manual:
The arguments are concatenated together into a single command, which is then read and executed
Note that this means that you are still running one single command, and not a shell script. If your filename.gpg
script has several lines, subsequent lines will be added to the argument list of the first (and only) command.
What should I do then?
Just use source
along with process substitution.
source <(gpg -d filename.gpg)
Contrary to eval
, source
is used to execute a shell script in the current context. Process substitution provides a pseudo-filename that contains the result of the substitution (i.e. the output of gpg
).