1

When I run an executable (like a.out) from a Bash shell, is that executable run in some kind of "sub" shell, i.e. different from the shell at which I'm typing?

I'll try illustrate my question with an example. The following program gets and prints the value of an environment variable, changes it, then re-gets and prints it again:

#include <iostream>
#include <string>
#include <cstdlib>
int main( int argc, char* argv[] )
{
  std::string str( getenv( "FOO" ) );
  std::cout << str << std::endl;
  setenv( "FOO", "bar", 1 );
  str = getenv( "FOO" );
  std::cout << str << std::endl;
  return 0;
}

Now, notice the output when I run the following at my Bash prompt:

>unset FOO && export FOO=foo && printf "$FOO\n" && ./a.out && printf "$FOO\n"
foo
foo
bar
foo
>
>unset FOO && export FOO=baz && printf "$FOO\n" && ./a.out && printf "$FOO\n"
baz
baz
bar
baz

So I'm exporting FOO so it's gettable from the executable - I understand that. And the executable's output shows the envvar being changed.

But the final printf "$FOO\n" prints the pre-executable value. Is this because the executable is run in a "different environment" than where I type commands?

  • Beware that "subshell" has a specific meaning. A subshell is created by a shell with a fork not followed by an exec. So it is still the shell binary running, not another program like your C++ example. – jlliagre Feb 24 '18 at 15:28
3

On Unix, each process has its own independent copy of the environment. A process gets its initial environment when its created (via fork()) by copying the parent process's environment.

So if you add a variable to the the shell's environment before calling a.out, a.out will see it (because a.out received a copy of the shell's environment, which contained that variable).

If a.out changes the environment, that changes a.out's environment — not the shell's. If a.out were to call another program (e.g., using system()), then that program would see the changed environment, because it'd get a copy of a.out's environment.

When a.out exits, its environment variables are destroyed; of course, if a child process were running, it'd still retain its copy (until it exits).

If you modify the environment in the shell, while a.out is still running (e.g., in the background: a.out &), then a.out will not see the changes: the environment is only copied at process creation.

[Note this is the typical way; the execve syscall allows you to execute a program with an environment you specify, instead of the one copied from the parent process.]

  • I guess in retrospect, I ought to have been able to surmise what you described: I'm familiar-ish with fork() and exec*(), and Bash is ultimately just another executable: so the way it "runs" processes when you type an executable name at the prompt must be some form of fork() and exec*(), and hence subject to the rules you described. Thanks again. – StoneThrow Feb 24 '18 at 1:33

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.