Reputation: 2025
I'm currently trying to write some component tests for my team using python and I ran into a test procedure that tells the tester to source a csh file. This file has a bunch of setenv commands and aliases. All of these environment variables are needed by the next executable in the chain. I wanted to devise some way to extract all of the env vars and pass them to the next process in the chain.
I looked at the following question, which is is almost what I need: Emulating Bash 'source' in Python
It pushes all exported bash environment variables into a dictionary, which I can then pass to the next process. The problem is this seems to only work for export
and not the csh setenv
command.
If this isn't possible, is there a way to run the .csh file with a subprocess command such as /bin/sh -c script.csh
and then run the process that needs those environment variables as a subprocess to that process (so that it could inherit it's environment variables?)
Essentially, I have a process and script that has a bunch of setenv variables and that process needs to run in an environment that contains all of those environment variables.
Thanks for any help,
Upvotes: 2
Views: 10737
Reputation: 1
This works for me to get the environment from a shell script (that calls other shell scripts). You could do something like this to read the env into a dictionary and then use os.environ to change the env.
import subprocess SAPENVCMD = "/bin/csh -c \'source ~/.cshrc && env\'" envdict = {} def runcommand(command): ps = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) out,err = ps.communicate() outlist = out.split('\n') if ps.returncode == 0: return outlist else: return None def elist2dict(dict,list): for entry in list: if entry is not None: pos = entry.find('=') key = entry[:pos] pos += 1 value = entry[pos:] dict.setdefault(key,value) def env2dict(dict,ENVCMD): envlist = runcommand(ENVCMD) elist2dict(dict,envlist) env2dict(envdict,SAPENVCMD) v = envdict["SAPSYSTEMNAME"] print v
Upvotes: 0
Reputation: 880627
Have you considered John Kugelman's solution? That would be the simplest way.
However, if for some reason you need or want Python to be an intermediary between these scripts, then you could use the following to source the script and retrieve the environment variables. (Edit: thanks to abarnert and Keith Thompson for the correct csh
command.)
If script.csh
contains
setenv foo bar
then
import subprocess
import os
import json
PIPE = subprocess.PIPE
output = subprocess.check_output(
'''source script.csh; python -c "import os, json; print(json.dumps(dict(os.environ)))"''',
shell=True, executable='/bin/csh')
env = json.loads(output)
print(env['foo'])
yields
bar
Upvotes: 1
Reputation: 263577
So script.csh
contains setenv
commands that set certain environment variables.
/bin/sh -c script.csh
will just execute the script; the environment variables will be lost as soon as the script finishes. Even that assumes that script.csh
is executable; if it's meant to be sourced, it needn't be, and probably shouldn't be.
For the environment variable settings to be visible, script.csh
has to be sourced by a csh process.
Here's a rough outline:
/bin/csh -c 'source script.csh ; python -c ...'
The python -c ...
process will be able to see the environment variables. The process that executes the above command will not.
A more complex solution would be something like this:
printenv > before.txt
/bin/csh -c 'source script.csh ; printenv' > after.txt
And then compare before.txt
and after.txt
to see what changed in the environment. (Or you can capture the output in some other way.) That has the advantage of letting the calling process see the modified environment, or at least obtain information about it.
Note that in some unusual cases, you may not be able to determine the environment from the output of printenv
. For example, if the value of $FOO
is "10\nBAR=20"
, that will appear in the output of printenv
as:
FOO=10
BAR=20
which looks like $FOO
is 10
and $BAR
is 20
. (It's actually not uncommon to have newlines in environment variable values; my $TERMCAP
is currently set to a 21-line chunk of text.
You could avoid that by writing your own printenv
replacement that prints an unambiguous representation of the environment.
Upvotes: 0
Reputation: 366003
The simplest way to do this is to let csh
source the setup script and then run the other script. For example:
setup.csh:
setenv foo bar
script.csh:
echo $foo
runner.py:
import subprocess
csh_command = 'source ./setup.csh && ./script.csh'
subprocess.check_call(['/bin/csh', '-c', csh_command])
If you just want to use the code from the linked question, all you have to do is change the regex. Just replace export
with setenv
, and the =
with \s+
.
Upvotes: 0