Is it possible to pass environment variables from child to parent in user space?

No, it's not possible.

Environment variables can only be passed from parent to child (as part of environment export/inheritance), not the other way around.


You can transmit variables values from a child to its parent process through a file or a named pipe.

Here is a theoretical simplest example:

child process:

echo ${variable} >/tmp/file

parent process:

read variable </tmp/file

This is very tricky if the parent process is not expecting and cooperating with it.  In that case, see change environment of a running process and Is there a way to change another process's environment variables?

If the parent process is expecting the value and cooperating with the transfer, the simple way is to use command substitution:

export VAR=$(cmd)

This assumes that the value of the variable is the only thing the program wants to write.  If the child process needs to be able to write to the screen (specifically, the parent’s stdout), we can do that by hiding the parent’s file descriptor 1 in another file descriptor:

exec 3>&1       # Copy our file descriptor 1 into file descriptor 3.
                # child_prog will be invoked with file descriptor 1 pointing to a pipe
                # for the command substitution, but all other file descriptors intact.
                # Specifically, fd3 will point to our stdout.
export var=$(child_prog)
exec 3>&-       # (Optionally) close fd3 as cleanup.

Now, if child_prog is short and simple, it may be easiest simply to write the value for the variable to file descriptor 1 and use file descriptor 3 (cmd >&3) as the standard output.  If it’s large and/or complex, you’ll want to do something like:

exec 5>&1       # Redirect fd1 (the command substitution pipe) to fd5.
exec 1>&3       # Set our fd1 (stdout) to our parent's stdout (which was passed in as fd3).
exec 3>&-       # Close fd3; it’s no longer needed.

and then use stdout normally, and use >&5 for writing the value.

So far I’ve been assuming that you want to pass only one value to one variable.  If you have multiple values, it’s a simple matter of delimiting them with a character (or string) that’s guaranteed not to appear in any of the values.  If we select @@, then the parent can say

exec 3>&1
temp=$(child_prog)
exec 3>&-
export var1="${temp%%@@*}"
rest="${temp#*@@}"
export var2="${rest%%@@*}"
export var3="${rest#*@@}"

and the child can say echo "value1@@value2@@value3" >&5.

If it’s hard to find a string of printing characters that’s guaranteed not to appear in any of the values, you can use newline.  Just change @@ to newline in the above commands:

Parent:

export var1="${temp%%
*}"
rest="${temp#*
}"
export var2="${rest%%
*}"
export var3="${rest#*
}"

Child:

printf "%s\n" "value1" "value2" "value3" >&5


Yet another variation is to have the child feed commands back to the parent, rather than values.  If the parent says . <(child_prog), it runs the child, captures the output, and executes it.  Then the child can do

printf "export var1='value1'\nexport var2='value2' var3=\"value3\"\n" >&5

(I tested this with a value3 that contained an apostrophe, so I had to quote it with \"…\", and I left it that way just to illustrate the alternative syntax.)

A feature of this technique is that you can add variable(s) to be exported without changing the code in the parent.

This approach requires that the parent process be running bash (or maybe one of the other advanced shells?), since POSIX doesn’t support <(cmd).