6

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.

1
  • 2
    Please note that in your second example with source, the file is executed as a bash script, not in zsh (the #! is ignored). Jun 19 at 19:13

2 Answers 2

5

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 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%.}
4
  • Many thanks for this answer. that shebang line is ignored, since the file isn't being executed: only its contents are that helped me a lot to understand what was going on during source. I thought that shebang is also read while sourcing.
    – t7e
    Jun 19 at 8:21
  • @t7e, ...what could the shell do with the shebang even if it read it? The whole idea of source is that you're running content inside your existing shell interpreter, so if the shebang says this is really Python source or Perl source or bash source, or otherwise not zsh source, what could the interpreter do about the problem? Jun 19 at 17:06
  • I thought it would read shebang during sourcing, interpret the code in a different shell but somehow set the variables in the current shell. It seems that I was wrong.
    – t7e
    Jun 19 at 17:21
  • 2
    @t7e, well, it can't work that way. Shell variables only exist in the memory of the shell process (same as variables in a C program only exist in the memory of the process running that program), and processes can't modify the memory of other processes. (Now, exported environment variables get passed from the shell to any commands it runs, but those, too, only get passed downward, there's no "global environment" that would be instantly available to all processes.)
    – ilkkachu
    Jun 19 at 20:16
3

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
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.