444

I want to run a Python script from another Python script. I want to pass variables like I would using the command line.

For example, I would run my first script that would iterate through a list of values (0,1,2,3) and pass those to the 2nd script script2.py 0 then script2.py 1, etc.

I found Stack Overflow question 1186789 which is a similar question, but ars's answer calls a function, where as I want to run the whole script, not just a function, and balpha's answer calls the script but with no arguments. I changed this to something like the below as a test:

execfile("script2.py 1")

But it is not accepting variables properly. When I print out the sys.argv in script2.py it is the original command call to first script "['C:\script1.py'].

I don't really want to change the original script (i.e. script2.py in my example) since I don't own it.

I figure there must be a way to do this; I am just confused how you do it.

8
  • 3
    If os.system isn't powerful enough for you, there's the subprocess module. Commented Sep 23, 2010 at 19:37
  • 1
    The question is if you know the name of the script (then import it) or if you do not know the name of the script at programming time (then use subprocess.call). In the second case this question also would not be a duplicate. Because the question doesn't make it clear, it's also not really a good one. Commented Sep 3, 2015 at 11:37
  • @Trilarion: wrong. You can import a python module even if its name is generated at runtime. Commented Nov 29, 2015 at 8:33
  • 1
    @Trilarion: why should it be covered at all? The names are fixed in both questions. Anyway, the statement "if you do not know the name of the script at programming time (then use subprocess.call)." is wrong regardless. If you have a new question; ask. Commented Nov 29, 2015 at 10:23
  • 1
    @Oli4 "definitely" is a strong word. Care to elaborate? I see subprocess.call() solution that accepts passing multiple command line arguments. I see import being mentioned for cases where main() function is defined (it won't help OP but it is the right way for many other people with a similar problem). I see execfile() for Python 2 that uses whatever you put into sys.argv (admittedly that last bit is not mentioned explicitly) — this option should be ignored by beginners. There is even an explicit os.system() answer with multiple arguments (the answer that is accepted here). Commented Aug 10, 2017 at 16:57

6 Answers 6

431

Try using os.system:

os.system("script2.py 1")

execfile is different because it is designed to run a sequence of Python statements in the current execution context. That's why sys.argv didn't change for you.

Sign up to request clarification or add additional context in comments.

16 Comments

I believe it's generally preferable to use subprocess.Popen over os.system: docs.python.org/library/subprocess.html#replacing-os-system.
Yes, that's what the help for os.system says. However, for simple uses os.system is the simplest way to get the job done. It depends on what your needs are, of course.
You have to add python to run the script: os.system("python script2.py 1")
@macdonjo: No, the os.system() call waits until the thing you called finishes before continuing. You could use subprocess.Popen() and manage the new processes yourself, or use the multiprocessing module, or various other solutions.
I encounter the issue sh: 1: *.py: not found. Add python and then it works, i.e., os.system("python script2.py 1").
|
145

Ideally, the Python script you want to run will be set up with code like this near the end:

def main(arg1, arg2, etc):
    # do whatever the script does


if __name__ == "__main__":
    main(sys.argv[1], sys.argv[2], sys.argv[3])

In other words, if the module is called from the command line, it parses the command line options and then calls another function, main(), to do the actual work. (The actual arguments will vary, and the parsing may be more involved.)

If you want to call such a script from another Python script, however, you can simply import it and call modulename.main() directly, rather than going through the operating system.

os.system will work, but it is the roundabout (read "slow") way to do it, as you are starting a whole new Python interpreter process each time for no raisin.

9 Comments

Re: "no raisin." This is not an error. However, it was interesting to see how long it would take for someone unfamiliar with Futurama to "correct" it in a random Stack Overflow question: two years and three months. :-)
I laughed at "no raisin" simply because it was a ridiculous typo, then saw your comment and found a clip on YouTube. Even funnier.
Here we are again nearly 5 years later, and I was delighted to find your quote from that Fry-befuddled brain from Futurama.
whoa, 2 years...an unraisinable length of time!
I just saw this in 2023 and it is still riddled with plot holes and spelling errors.
|
136

This is inherently the wrong thing to do. If you are running a Python script from another Python script, you should communicate through Python instead of through the OS:

import script1

In an ideal world, you will be able to call a function inside script1 directly:

for i in range(whatever):
    script1.some_function(i)

If necessary, you can hack sys.argv. There's a neat way of doing this using a context manager to ensure that you don't make any permanent changes.

import contextlib
@contextlib.contextmanager
def redirect_argv(num):
    sys._argv = sys.argv[:]
    sys.argv=[str(num)]
    yield
    sys.argv = sys._argv

with redirect_argv(1):
    print(sys.argv)

I think this is preferable to passing all your data to the OS and back; that's just silly.

11 Comments

It might be the "wrong" thing to do but what if the script you need to reference has no main or functions.... an import would execute the script at the time of import, which probably isn't what you want (and you don't want to refactor it because people are also using that script as is). os/subprocess could deal with such a case
Won't that fail for multithreaded scripts?
@MarioVilas probably, yes.
From a higher-level perspective, it's "wrong" to call this strategy "wrong". When a system scales up, having detailed knowledge of the inner workings of components becomes a larger and larger problem -- a program written in Python might get redesigned in another language, and using the generic process communication facilities of the operating system has much to offer as a strategy.
... and what about dependencies? If each script has its own dependencies why the first script should to know all the sencond's script dependencies? From the point of view of architecture, what is wrong is to call directly importing in python (IMHO)
|
62

I think the good practice may be something like this;

import subprocess
cmd = 'python script.py'

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
out, err = p.communicate() 
result = out.split('\n')
for lin in result:
    if not lin.startswith('#'):
        print(lin)

according to documentation The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace several older modules and functions:

os.system
os.spawn*
os.popen*
popen2.*
commands.*

Use communicate() rather than .stdin.write, .stdout.read or .stderr.read to avoid deadlocks due to any of the other OS pipe buffers filling up and blocking the child process. Read Here

Comments

54

SubProcess module:
http://docs.python.org/dev/library/subprocess.html#using-the-subprocess-module

import subprocess
subprocess.Popen("script2.py 1", shell=True)

With this, you can also redirect stdin, stdout, and stderr.

2 Comments

Do not use shell=True unless necessary.
@PiotrDobrogost could you elaborate on that? Edit: For anyone curious, here's an explanation on why: stackoverflow.com/questions/3172470/…
41
import subprocess
subprocess.call(" python script2.py 1", shell=True)

3 Comments

You might wish to expand on your answer to explain why this is a better option than some of the other answers presented here.
How to pass argument but not as string, for example list? Is there other option than pass every element separately?
Do not use shell=True unless necessary.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.