1

I am struggling to execute a shell script from a Python program. The actual issue is the script is a load profile script and runs manually as :

. /path/to/file

The program can't be run as sh script as the calling programs are loading some configuration file and so must need to be run as . /path/to/file

Please do guide how can I integrate the same in my Python script? I am using subprocess.Popen command to run the script and as said the only way it works is to run as . /path/to/file and so not giving the right result.

accdias
  • 5,160
  • 3
  • 19
  • 31
Bicky
  • 63
  • 5
  • If the requirement is to run as a shell script, there really isn't a sane way to run the script directly from Python. The requirement to source a script means that it wants to interact with your shell. (You can nominally run a shell and source the script from there, but the effects will be gone as soon as the shell exits, so you can factor out Python entirely then.) – tripleee Dec 31 '19 at 08:19
  • Related: https://stackoverflow.com/questions/12060863/python-subprocess-call-a-bash-alias/49209211#49209211 – tripleee Dec 31 '19 at 08:20
  • You need to understand what `. /path/to/file` means. Read `help source` first. – accdias Dec 31 '19 at 08:51
  • Tangentially, you want to avoid `Popen` if you can. See e.g. https://stackoverflow.com/questions/4256107/running-bash-commands-in-python/51950538#51950538 – tripleee Dec 31 '19 at 08:58

1 Answers1

0

Without knowledge of the precise reason the script needs to be sourced, this is slightly speculative.

The fundamental problem is this: How do I get a source command to take effect outside the shell script?

Let's say your sourced file does something like

export fnord="value"

This cannot (usefully) be run in a subshell (as a normally executed script would) because the environment variable and its value will be lost when the script terminates. The solution is to source (aka .) this snippet from an already running shell; then the value stays in that shell's environment until that shell terminates.

But Python is not a shell, and there is no general way for Python to execute arbitrary shell script code, short of reimplementing the shell in Python. You can reimplement a small subset of the shell's functionality with something like

with open('/path/to/file') as shell_source:
     lines = shell_source.readlines()
for line in lines:
    if line.strip().startswith('export '):
        var, value = line[7:].strip().split('=', 1)
        if value.startswith('"'):
            value = value.strip('"')
        elif value.startswith("'"):
            value = value.strip("'")
     os.environ[var] = value

with some very strict restrictions (let's not say naïve assumptions) on the allowable shell script syntax in the file. But what if the file contained something else than a series of variable assignments, or the assignment used something other than trivial quoted strings in the values? (Even the export might or might not be there. Its significance is to make the variable visible to subprocesses of the current shell; maybe that is not wanted or required? Also export variable=value is not portable; proper Bourne shell script syntax would use variable=value; export variable or one of the many variations.)

If you know what exactly your Python script needs from the shell script, maybe do something like

r = subprocess.run('. /path/to/file; printf "%s\n" "$somevariable"',
    shell=True, capture_output=True, text=True)
os.environ['somevariable'] = r.stdout.split('\n')[-2]

to source the entire script in a subshell, then print to standard output the part you actually need, and capture that from your Python script (and assign it to an environment variable if that's what you eventually need to accomplish).

tripleee
  • 175,061
  • 34
  • 275
  • 318