243

Is there a way to specify the running directory of command in Python's subprocess.Popen()?

For example:

Popen('c:\mytool\tool.exe', workingdir='d:\test\local')

My Python script is located in C:\programs\python

Is is possible to run C:\mytool\tool.exe in the directory D:\test\local?

How do I set the working directory for a sub-process?

tripleee
  • 175,061
  • 34
  • 275
  • 318
icn
  • 17,126
  • 39
  • 105
  • 141
  • 3
    keep in mind that subprocess.call is just a thin wrapper over subprocess.Popen, and this wrapper deals with all arguments of Popen as well, at least as far as I remember :) In simple cases, better stick to subprocess.call – shabunc Oct 31 '13 at 13:49
  • You should now probably prefer `subprocess.run`, though `call` and the slightly newer legacy wrappers `check_call` and `check_output` are still available. – tripleee Dec 27 '22 at 17:57

2 Answers2

356

subprocess.Popen takes a cwd argument to set the Current Working Directory; you'll also want to escape your backslashes ('d:\\test\\local'), or use r'd:\test\local' so that the backslashes aren't interpreted as escape sequences by Python. The way you have it written, the \t part will be translated to a tab.

So, your new line should look like:

subprocess.Popen(r'c:\mytool\tool.exe', cwd=r'd:\test\local')

To use your Python script path as cwd, import os and define cwd using this:

os.path.dirname(os.path.realpath(__file__)) 
Enrico
  • 766
  • 8
  • 19
Mark Rushakoff
  • 249,864
  • 45
  • 407
  • 398
  • 2
    What effect, if any, would adding Shell=True to the arguments have on also setting the cwd? – T. Stone Nov 06 '09 at 03:17
  • 3
    @T. Stone: For a standalone executable, it shouldn't change anything, unless the exe depends on some environment variables in the shell, maybe. But, with `shell=False`, you can't use a shell builtin such as `cd`: i.e., try this on Linux with shell both ways: `subprocess.Popen("cd /tmp; pwd")` – Mark Rushakoff Nov 06 '09 at 03:22
  • I have to run a program the same way but with a path associated with it. I tried to run it like: (r'c:\mytool\tool.exe -fPath\to\file.txt', cwd=r'd:\test\local') but it did not work. Any suggestions? – Drewdin Jul 20 '13 at 20:52
  • 17
    In python 3 at least, you do not have to use backslashes even when on a windows machine, i just did `subprocess.call(["C:/Users/Bob/Some.exe"], cwd="C:/Users/Jane/")` and it works fine – mgrandi Aug 16 '13 at 21:05
  • 9
    Does the working directory have to be an absolute path? – DXsmiley May 29 '15 at 22:12
  • 12
    It works also for subprocess.check_output(). Thanks ! – Samuel Dauzon Sep 04 '15 at 07:15
  • 1
    @Zim: `__file__` may fail. [Use `get_script_dir()` instead](http://stackoverflow.com/a/22881871/4279) – jfs May 11 '16 at 21:23
  • 1
    Just to add, we should not mention the command with it's full path when using cwd=location. This is because, the way it works is...It switches to the location specified and searches for the executable file mentioned. Quoting from python.org, "If cwd is not None, the child’s current directory will be changed to cwd before it is executed. Note that this directory is not considered when searching the executable, so you can’t specify the program’s path relative to cwd." Refer this article: thebongtraveller.blogspot.com/2016/09/python-running-executables-using.html – Arindam Roychowdhury Sep 15 '16 at 08:33
  • 2
    @ArindamRoychowdhury the docs are saying the exact contrary. "Note that this directory *is not* considered when searching the executable.". So always use full path or path relative to your current python module. – konoufo May 15 '18 at 02:32
11

Other way is to simply do this

cwd = os.getcwd()
os.chdir('c:\some\directory')
subprocess.Popen('tool.exe')
os.chdir(cwd)

This solution works if you want to rely on relative paths, for example, if your tool's location is c:\some\directory\tool.exe. cwd keyword argument for Popen will not let you do this. Some scripts/tools may rely on you being in the given directory while invoking them. To make this code less noisy, aka detach the logic related to changing directories from the "business logic", you can use a decorator.

def invoke_at(path: str):
    def parameterized(func):
        def wrapper(*args, **kwargs):
            cwd = os.getcwd()
            os.chdir(path)

            try:
                ret = func(*args, **kwargs)
            finally:
                os.chdir(cwd)

            return ret

        return wrapper

    return parameterized            

Such decorator can be then used in a way:

@invoke_at(r'c:\some\directory')
def start_the_tool():
    subprocess.Popen('tool.exe')
ashrasmun
  • 490
  • 6
  • 11
  • Note that you can get around the relative path problem by resolving first, e.g. with `Path("../../something").resolve()`. That said I routinely use `chdir` when writing quick scripts I might otherwise write in bash. – 2e0byo Jan 12 '22 at 13:20
  • @2e0byo I must admit, that as a mainly Windows developer, I prefer python over batch :) Bash is not an option for me. – ashrasmun Jan 12 '22 at 16:26
  • 1
    Actually that is the only way (or better: workaround) i can find to "set" the working-directory wenn using Popen. thank you. – anion Aug 06 '22 at 09:45