How to source some shell code not a file?
There is a description of a source
command:
source
is a bash shell built-in command that executes the content of the file passed as argument, in the current shell. It has a synonym in .
(period).
For example, for the sake of an experiment, I want to export a variable from a different shell zsh
in my case (running the command in bash
):
$ zsh -c "export test=$(echo "hello world")"
$ echo $test
$
It does not work because the command runs in a zsh
subshell and is not executed directly in bash
.
If I create and source a script this way:
#!/home/linuxbrew/.linuxbrew/bin/zsh
export test=$(echo "hello world")
$ chmod 777 test.zsh
$ source test.zsh
$ echo $test
hello world
It works alright.
The question is how to source
a command without using a script since source
can be run with files only?
I want to achieve something like that:
source zsh -c "export test=$(echo "hello world")"
If it is not possible, please explain why.
The answer to the question you asked is: use eval
.
eval "export test=$(echo "hello world")"
Do take care that the argument to eval
is executed as a piece of shell code. It’s usually tricky to get right. For example, the code above sets test
to the 5-character string hello
, not to the 11-character string hello world
. This mimics your original example with zsh -c "export test=$(echo "hello world")"
, which also sets test
to hello
. The reason is that in both cases, the outer shell runs the code and determines that it’s calling a command with the argument export test=hello world
. In one case the command is the external program zsh
with a first argument -c
, and in the other case the command is the builtin eval
.
If you wanted to set test
to hello world
, you’d have to arrange for the inner shell evaluation to get an input like export test="hello world"
or export test='hello world'
or export test=hello world
. Note that there is no direct method that works for arbitrary characters in the value: surrounding with single quotes fails if the string contains single quotes; surrounding with double quotes fails if the string contains any of `"[
; adding backslashes requires adding them before each of bunch of characters, and doesn’t work for newlines. How to work around this difficulty depends on what you want to do. Many answers are to be found among quoting questions on this site.
However, this may not be what you want — it’s not clear what you actually want to do. Note that the code is executed by the same shell. That’s the whole point of source
or eval
: to execute code inside the same program. You’re showing an example with source
where you read a file that starts with a shebang, but that shebang line is ignored, since the file isn’t being executed: only its contents are.
If you want to construct the value of a variable using one program (e.g. zsh) while using another shell (e.g. bash), forget about all that and make the other program print the value. The simple way is:
test=$(zsh -c 'echo "hello world"')
Note that command substitution removes trailing newlines. So the value of test
will be the 11-character string hello world
, without a newline at the end. If you want to get the full output from echo
with the trailing newline, add another character (and optionally a newline) at the end, then strip it out.
test=$(zsh -c 'echo "hello world"'; echo .); test=${test%.}
It might be simpler to have your command write out the variable’s definition and then to source that using process substitution:
$ echo $test
$ . <(echo "test='hello world'")
$ echo "$test"
hello world