jluzwick
jluzwick

Reputation: 2025

Python Sourcing a CSH and passing setenv to a new subprocess

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

Answers (4)

Lulz
Lulz

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

unutbu
unutbu

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

Keith Thompson
Keith Thompson

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

abarnert
abarnert

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

Related Questions