2

I have a long bash script that at the end exports an environment variable, let's call it myscript.sh. I need to call this shell script from python code. As far as I know, the exported environment variable will be local, and won't be visible in python.

Is there a proper way to make it exported in the python environment as well?

texmelex
  • 57
  • 7
  • 2
    You could run the env command at the end of your shell script. I don't see a way to do it otherwise. Environment variables are passed to child processes, but your Python script is a parent process to your bash script, and environment variables don't flow from child to parent. – Nick ODell Feb 03 '23 at 17:31
  • 1
    So in other words, run your shell script, _then_ your Python script. Alternatively, reimplement all (or at least the main parts) of the shell in Python. Or if you can, change or wrap the shell script with something which turns its result into something you can `exec` or `ast.literal_eval` in Python. – tripleee Feb 03 '23 at 17:36
  • The desperate method would be `source ./myscript.sh && python ./otherscript.py` but I wouldn't do that personally unless I am putting that line in my .bashrc or .profile or whatnot. – Tyler Stoney Feb 03 '23 at 18:05
  • At the end of `myscript.sh`, write `myvar=some value` to a temp file and read that temp file in python. Next Python can set the variable. – Walter A Feb 03 '23 at 19:22
  • You'd probably want `set -a` before that `source ./myscript` to make sure all variables it defines are exported. – Charles Duffy Feb 03 '23 at 21:03

2 Answers2

1

You can use env -0 in your script to print all environment variables, separated with a null char as newline might be problematic if it's contained in some variable value. Then from python you can set the process environment using this

import os
import subprocess

for k,v in filter(lambda p: len(p)==2, map(lambda e:e.decode().split('='), subprocess.run('script', check=True, capture_output=True).stdout.split(b'\x00'))):
    os.environ[k]=v

Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
0

The general easiest way to get any information back from a child process is via the stdout pipe, which is very easily captured in the python exec method.

Get your script to print the info you want in a way that is easy to parse.

If you really want to see the env vars then you will need to print them out. If you don't want to modify the script you are trying to run then you could exec the script then dump the env, which is parseable.

. myscript && env

or if there is a particular env var you want:

. myscript && echo MYVAR="$myvar"

Note that this will be executed by python's default shell. If you want bash in particular, or you want the hashbang of your script to be honoured, that takes more effort. You could scrape the hashbang yourself, for example, but the env printing trick will only work trivially with shells - hashbangs may refer to other clevernesses such as sed/awk

To avoid nesting shell invocations, you could call subprocess.Popen with, eg ["/bin/bash", "-c", ". myscript && echo MYVAR=\"$myvar\""] and shell=False

I added quotes to cover some cases where the var contains wildcard characters. Depending on the content of the variables printed, this may be good enough. If you expect the var to contain multi lines or perhaps the string MYVAR= or feel that the value set by the script is unconstrained, then you might need to do something more complex to ensure the output is preserved, as described by CD. It will also be marginally more difficult to parse from python.

Gem Taylor
  • 5,381
  • 1
  • 9
  • 27
  • `echo MYVAR=$myvar` is buggy; basically all the problems described in [I just assigned a variable but `echo $variable` shows something else!](https://stackoverflow.com/questions/29378566/i-just-assigned-a-variable-but-echo-variable-shows-something-else). `printf '\0%s\0' "MYVAR=$myvar"` is much less ambiguous. (The first NUL is so there's another NUL separator between the script's other stdout and the variable dump; a NUL is the one thing that can't be _inside_ a shell variable, so the variable's contents can't spoof a fake separator like they could with a newline). – Charles Duffy Feb 03 '23 at 21:03
  • (For _other_ problems with echo, see the [unix.se] question [Why is printf better than echo?](https://unix.stackexchange.com/a/65819/3113) -- `echo` has some pretty serious portability problems baked into its specification that can easily result in output that is not byte-for-byte identical to the variable you're trying to dump). – Charles Duffy Feb 03 '23 at 21:07