845

cd is the shell command to change the working directory.

How do I change the current working directory in Python?

martineau
  • 119,623
  • 25
  • 170
  • 301
too much php
  • 88,666
  • 34
  • 128
  • 138
  • 2
    So in the interpreter `os.chdir(os.path.join(os.path.abspath(os.path.curdir),u'subfolder'))` - or ? – Mr_and_Mrs_D Dec 26 '14 at 15:31
  • 2
    Interesting in this context: [Find current directory and file's directory](https://stackoverflow.com/q/5137497/562769): `os.getcwd()` – Martin Thoma Dec 20 '17 at 10:34

15 Answers15

930

You can change the working directory with:

import os

os.chdir(path)

There are two best practices to follow when using this method:

  1. Catch the exception (WindowsError, OSError) on invalid path. If the exception is thrown, do not perform any recursive operations, especially destructive ones. They will operate on the old path and not the new one.
  2. Return to your old directory when you're done. This can be done in an exception-safe manner by wrapping your chdir call in a context manager, like Brian M. Hunt did in his answer.

Changing the current working directory in a subprocess does not change the current working directory in the parent process. This is true of the Python interpreter as well. You cannot use os.chdir() to change the CWD of the calling process.

Alan W. Smith
  • 24,647
  • 4
  • 70
  • 96
Michael Labbé
  • 12,017
  • 4
  • 27
  • 36
  • 5
    [cdunn2001](https://stackoverflow.com/users/263998/cdunn2001)'s lightweight [decorator-based answer](https://stackoverflow.com/a/24176022/2809027) is the ideal approach for modern Python. The above answer demonstrates why. **Never call `os.chdir()` outside of a context manager,** unless you think you know what you're doing. (_You probably don't._) – Cecil Curry May 22 '16 at 05:19
  • 7
    This is the easiest way in an interactive shell, I think. Note that in Windows, you have to use forward slashes, like `os.chdir("C:/path/to/location")` – Josiah Jul 25 '16 at 11:45
  • 1
    The one thing to be aware of is that if you make your python program an executable and run it in cron it will start up in your home directory. So it is best to use a fully-qualified path. This definitely works, yet I still use fully-qualified paths in any script I might invoke from Python because there is no guarantee that this will apply outside of the Python program itself. – SDsolar Aug 02 '17 at 03:43
  • 2
    In Windows, it is easier to use a raw string if you copied the path with backslashes. `r'C:\path\to\location'`. – Burak Sep 20 '21 at 19:31
  • While os.chdir won't change the parent directory, you should note that in some contexts, you can make an alias that uses cd to simulate a script that changes the directory it was called from (not in Python, per se, but you can do multiple instructions with one command, including running a Python script). E.g. `alias myAlias="cd ..;./myPythonScript.py;cd ~"`. Since the alias isn't technically a script, it works. – Brōtsyorfuzthrāx Oct 25 '22 at 23:30
362

Here's an example of a context manager to change the working directory. It is simpler than an ActiveState version referred to elsewhere, but this gets the job done.

Context Manager: cd

import os

class cd:
    """Context manager for changing the current working directory"""
    def __init__(self, newPath):
        self.newPath = os.path.expanduser(newPath)

    def __enter__(self):
        self.savedPath = os.getcwd()
        os.chdir(self.newPath)

    def __exit__(self, etype, value, traceback):
        os.chdir(self.savedPath)

Or try the more concise equivalent(below), using ContextManager.

Example

import subprocess # just to call an arbitrary command e.g. 'ls'

# enter the directory like this:
with cd("~/Library"):
   # we are in ~/Library
   subprocess.call("ls")

# outside the context manager we are back wherever we started.
Community
  • 1
  • 1
Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343
  • 4
    If you ever need to know what directory you changed FROM, you can just add `return self` at the end of `__enter__`. That way you can do `with cd('foo') as cm:` and access the previous dir as `cm.savedPath` – Sam F Mar 27 '18 at 07:35
  • Note that there are cases, where returning to the old directory (the one stored in "savedPath") is not possible. For example, if a more priviledged process runs a less priviledged process, the second process inherits the first processes working directory, even in those cases, where the second process cannot enter that working directory with its own capabilities. – Kai Petzke Feb 11 '19 at 09:43
  • I am getting following warning message: `Attribute 'savedPath' defined outside __init__ [attribute-defined-outside-init]` – alper Feb 01 '21 at 16:37
  • If I `return` inside the `with cd("~/Library"):` block would it still work? – alper Dec 17 '21 at 22:38
  • I'd guess that is the whole point of a `with` block: Ensuring that `__exit__` is called in any case, even if you use `return`. – Iziminza Mar 13 '23 at 18:08
  • @alper: If that annoys you, define the attribute inside `__init__`, like `self.savedPath = None`. – Iziminza Mar 13 '23 at 18:14
164

cd() is easy to write using a generator and a decorator.

from contextlib import contextmanager
import os

@contextmanager
def cd(newdir):
    prevdir = os.getcwd()
    os.chdir(os.path.expanduser(newdir))
    try:
        yield
    finally:
        os.chdir(prevdir)

Then, the directory is reverted even after an exception is thrown:

os.chdir('/home')

with cd('/tmp'):
    # ...
    raise Exception("There's no place like /home.")
# Directory is now back to '/home'.
maxymoo
  • 35,286
  • 11
  • 92
  • 119
cdunn2001
  • 17,657
  • 8
  • 55
  • 45
  • 3
    Also, note [this potential blunder](http://stackoverflow.com/a/15427892) (to forget the `try/finally`). – cdunn2001 Dec 26 '14 at 21:59
  • 8
    **Brilliance!** If the introductory commentary from the [accepted answer](https://stackoverflow.com/a/431715/2809027) were injected into _this_ answer, this would be immeasurably ideal. Still, this answer's concise, Pythonically safe implementation warrants all the upvotes I have to give. – Cecil Curry May 22 '16 at 05:13
  • 3
    Why `yield` and not `return`? Is this supposed to be a generator? – EKons Aug 05 '16 at 09:19
  • Please comment on the relevance of yield vs return! – NicoBerrogorry Jul 18 '17 at 05:27
  • 2
    @NicoBerrogorry, it's a generator. See docs on [contextlib.contextmanager](https://docs.python.org/2/library/contextlib.html). This is a very useful pattern in Python, worth learning. – cdunn2001 Sep 11 '17 at 18:52
  • I would like to ask about the use of finally. We know that finally always happen after try, exception or not. While it is fine when there is an exception, it also implies that in any case, you will be back to previous directory, which is not what is intended. Shouldn't os.chdir(prevdir) be under an else statement instead? Also, I wonder why the python subprocess.call("cd /path") won't actually work... – Ando Jurai Nov 10 '17 at 10:14
  • Why there is `raise Exception` at the end of the `with` block? – alper Jul 08 '20 at 20:18
  • 1
    @AndoJurai I actually think, that always going back to the previous directory is intended. This way, your code structure matches the directory structure and you don't have to put `cd('../')`, which is easy to forget. – Libavius Jan 14 '21 at 14:52
  • @alper, the 2nd code block is just an example of how the 1st might be used. If your code happens to throw an exception, then you would still return to the original directory, as AndoJurai says. The try/finally does that for you; not essential, but helpful. – cdunn2001 Jul 10 '21 at 17:28
  • 1
    Shouldn't you `chdir` in the `try` block? – Rainbow Fossil Apr 12 '22 at 22:31
  • @RainbowFossil, If `chdir` fails, then the directory was not changed, so we do not need to chdir back. – cdunn2001 Aug 22 '22 at 22:42
153

I would use os.chdir like this:

os.chdir("/path/to/change/to")

By the way, if you need to figure out your current path, use os.getcwd().

More here

parik
  • 2,313
  • 12
  • 39
  • 67
Evan Fosmark
  • 98,895
  • 36
  • 105
  • 117
27

If you're using a relatively new version of Python, you can also use a context manager, such as this one:

from __future__ import with_statement
from grizzled.os import working_directory

with working_directory(path_to_directory):
    # code in here occurs within the directory

# code here is in the original directory

UPDATE

If you prefer to roll your own:

import os
from contextlib import contextmanager

@contextmanager
def working_directory(directory):
    owd = os.getcwd()
    try:
        os.chdir(directory)
        yield directory
    finally:
        os.chdir(owd)
Brian Clapper
  • 25,705
  • 7
  • 65
  • 65
  • 1
    Good general idea. Here an [Activestate recipe](http://code.activestate.com/recipes/576620-changedirectory-context-manager) without other dependencies. – cfi Sep 26 '12 at 13:43
  • 4
    **Dependencies are bad.** Python's built-in `contextlib.contextmanager` decorator is good. See [cdunn2001](https://stackoverflow.com/users/263998/cdunn2001)'s [decorator-based answer](https://stackoverflow.com/a/24176022/2809027), which would ideally be the accepted answer now. – Cecil Curry May 22 '16 at 05:10
16

As already pointed out by others, all the solutions above only change the working directory of the current process. This is lost when you exit back to the Unix shell. If desperate you can change the parent shell directory on Unix with this horrible hack:

def quote_against_shell_expansion(s):
    import pipes
    return pipes.quote(s)

def put_text_back_into_terminal_input_buffer(text):
    # use of this means that it only works in an interactive session
    # (and if the user types while it runs they could insert characters between the characters in 'text'!)
    import fcntl, termios
    for c in text:
        fcntl.ioctl(1, termios.TIOCSTI, c)

def change_parent_process_directory(dest):
    # the horror
    put_text_back_into_terminal_input_buffer("cd "+quote_against_shell_expansion(dest)+"\n")
mrdiskodave
  • 353
  • 2
  • 4
  • 7
    **Insane, fragile hack gets mandatory upvotes.** No one should ever do this, particularly with that "and if the user types while it runs..." caveat. Still, it titillates the rebel neckbeard in me to see that changing the parent CWD _is_ sort-of but not really feasible. Upvotes! Upvotes for all! – Cecil Curry May 22 '16 at 05:06
  • This was the solution I was looking for, thanks for the hack. I'm not sure if I will stick with it, or use a shell function as advised in [Change working directory in shell with a python script](https://stackoverflow.com/a/3786928/11386095). I also mentioned your solution with some modifications for Python 3 over there, see [this answer](https://stackoverflow.com/a/75351711/11386095). – David Heresy Feb 05 '23 at 10:59
12

os.chdir() is the Pythonic version of cd.

Elliot A.
  • 1,511
  • 2
  • 14
  • 19
PEZ
  • 16,821
  • 7
  • 45
  • 66
12

os.chdir() is the right way.

Federico A. Ramponi
  • 46,145
  • 29
  • 109
  • 133
11
import os

abs_path = 'C://a/b/c'
rel_path = './folder'

os.chdir(abs_path)
os.chdir(rel_path)

You can use both with os.chdir(abs_path) or os.chdir(rel_path), there's no need to call os.getcwd() to use a relative path.

Riccardo D
  • 591
  • 1
  • 5
  • 12
  • 1
    Works well. One can use os.getcwd() to verify the current directory both before and after changing the directory.. – vinsinraw Jan 03 '19 at 21:38
6

Further into direction pointed out by Brian and based on sh (1.0.8+)

from sh import cd, ls

cd('/tmp')
print ls()
Yauhen Yakimovich
  • 13,635
  • 8
  • 60
  • 67
4

If You would like to perform something like "cd.." option, just type:

os.chdir("..")

it is the same as in Windows cmd: cd.. Of course import os is neccessary (e.g type it as 1st line of your code)

D.K
  • 57
  • 2
2

The Path objects in path (a third-party package available on PyPI, different from pathlib) offer both a context manager and a chdir method for this purpose:

from path import Path  # pip install path

with Path("somewhere"):
    ...

Path("somewhere").chdir()
AXO
  • 8,198
  • 6
  • 62
  • 63
  • 1
    Seems interesting. However, it should be noted that it's not the standard library's Path – Zvika Jan 10 '23 at 11:16
0

If you use spyder and love GUI, you can simply click on the folder button on the upper right corner of your screen and navigate through folders/directories you want as current directory. After doing so you can go to the file explorer tab of the window in spyder IDE and you can see all the files/folders present there. to check your current working directory go to the console of spyder IDE and simply type

pwd

it will print the same path as you have selected before.

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
majid bhatti
  • 83
  • 12
0

Python 3.11 now offers contextlib.chdir:

import contextlib

with contextlib.chdir(path):
    ...
stefs
  • 43
  • 6
-1

Changing the current directory of the script process is trivial. I think the question is actually how to change the current directory of the command window from which a python script is invoked, which is very difficult. A Bat script in Windows or a Bash script in a Bash shell can do this with an ordinary cd command because the shell itself is the interpreter. In both Windows and Linux Python is a program and no program can directly change its parent's environment. However the combination of a simple shell script with a Python script doing most of the hard stuff can achieve the desired result. For example, to make an extended cd command with traversal history for backward/forward/select revisit, I wrote a relatively complex Python script invoked by a simple bat script. The traversal list is stored in a file, with the target directory on the first line. When the python script returns, the bat script reads the first line of the file and makes it the argument to cd. The complete bat script (minus comments for brevity) is:

if _%1 == _. goto cdDone
if _%1 == _? goto help
if /i _%1 NEQ _-H goto doCd
:help
echo d.bat and dSup.py 2016.03.05. Extended chdir.
echo -C = clear traversal list.
echo -B or nothing = backward (to previous dir).
echo -F or - = forward (to next dir).
echo -R = remove current from list and return to previous.
echo -S = select from list.
echo -H, -h, ? = help.
echo . = make window title current directory.
echo Anything else = target directory.
goto done

:doCd
%~dp0dSup.py %1
for /F %%d in ( %~dp0dSupList ) do (
    cd %%d
    if errorlevel 1 ( %~dp0dSup.py -R )
    goto cdDone
)
:cdDone
title %CD%
:done

The python script, dSup.py is:

import sys, os, msvcrt

def indexNoCase ( slist, s ) :
    for idx in range( len( slist )) :
        if slist[idx].upper() == s.upper() :
            return idx
    raise ValueError

# .........main process ...................
if len( sys.argv ) < 2 :
    cmd = 1 # No argument defaults to -B, the most common operation
elif sys.argv[1][0] == '-':
    if len(sys.argv[1]) == 1 :
        cmd = 2 # '-' alone defaults to -F, second most common operation.
    else :
        cmd = 'CBFRS'.find( sys.argv[1][1:2].upper())
else :
    cmd = -1
    dir = os.path.abspath( sys.argv[1] ) + '\n'

# cmd is -1 = path, 0 = C, 1 = B, 2 = F, 3 = R, 4 = S

fo = open( os.path.dirname( sys.argv[0] ) + '\\dSupList', mode = 'a+t' )
fo.seek( 0 )
dlist = fo.readlines( -1 )
if len( dlist ) == 0 :
    dlist.append( os.getcwd() + '\n' ) # Prime new directory list with current.

if cmd == 1 : # B: move backward, i.e. to previous
    target = dlist.pop(0)
    dlist.append( target )
elif cmd == 2 : # F: move forward, i.e. to next
    target = dlist.pop( len( dlist ) - 1 )
    dlist.insert( 0, target )
elif cmd == 3 : # R: remove current from list. This forces cd to previous, a
                # desireable side-effect
    dlist.pop( 0 )
elif cmd == 4 : # S: select from list
# The current directory (dlist[0]) is included essentially as ESC.
    for idx in range( len( dlist )) :
        print( '(' + str( idx ) + ')', dlist[ idx ][:-1])
    while True :
        inp = msvcrt.getche()
        if inp.isdigit() :
            inp = int( inp )
            if inp < len( dlist ) :
                print( '' ) # Print the newline we didn't get from getche.
                break
        print( ' is out of range' )
# Select 0 means the current directory and the list is not changed. Otherwise
# the selected directory is moved to the top of the list. This can be done by
# either rotating the whole list until the selection is at the head or pop it
# and insert it to 0. It isn't obvious which would be better for the user but
# since pop-insert is simpler, it is used.
    if inp > 0 :
        dlist.insert( 0, dlist.pop( inp ))

elif cmd == -1 : # -1: dir is the requested new directory.
# If it is already in the list then remove it before inserting it at the head.
# This takes care of both the common case of it having been recently visited
# and the less common case of user mistakenly requesting current, in which
# case it is already at the head. Deleting and putting it back is a trivial
# inefficiency.
    try:
        dlist.pop( indexNoCase( dlist, dir ))
    except ValueError :
        pass
    dlist = dlist[:9] # Control list length by removing older dirs (should be
                      # no more than one).
    dlist.insert( 0, dir ) 

fo.truncate( 0 )
if cmd != 0 : # C: clear the list
    fo.writelines( dlist )

fo.close()
exit(0)
  • While it's a nice answer, the OP selected an answer that says it's not about changing the CWD of the parent process. That clears up any possible confusion about what the question means. – the Tin Man Apr 11 '16 at 18:54
  • To Tin Man-- that answer was selected before I posted my suggestion. I think that the wide ranging answers may have been confusing. cd within a given process (i.e. a python script) is so simple that I don't know why anyone would ask it. – David McCracken Apr 13 '16 at 04:11
  • 1
    Actually that answer was selected *years* ago. If it wasn't appropriate it would have been called out many times since then. – the Tin Man Apr 13 '16 at 16:13
  • I think that confusion remains. More recently, the question "simulating linux's “cd” command in python , and persist the directory change after the program exits [duplicate]" was dismissed as having been answered here but, in fact, this question is not addressed by the selected answer. My suggestion is for Windows but the issues are the same in Linux. – David McCracken Apr 15 '16 at 19:46