ChatGPT解决这个技术问题 Extra ChatGPT

Equivalent of shell 'cd' command to change the working directory?

cd is the shell command to change the working directory.

How do I change the current working directory in Python?

So in the interpreter os.chdir(os.path.join(os.path.abspath(os.path.curdir),u'subfolder')) - or ?
Interesting in this context: Find current directory and file's directory: os.getcwd()

A
Alan W. Smith

You can change the working directory with:

import os

os.chdir(path)

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

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. 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.


cdunn2001's lightweight decorator-based answer 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.)
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")
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.
In Windows, it is easier to use a raw string if you copied the path with backslashes. r'C:\path\to\location'.
C
Community

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.

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
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.
I am getting following warning message: Attribute 'savedPath' defined outside __init__ [attribute-defined-outside-init]
If I return inside the with cd("~/Library"): block would it still work?
p
parik

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


m
maxymoo

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'.

Also, note this potential blunder (to forget the try/finally).
Brilliance! If the introductory commentary from the accepted answer 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.
Why yield and not return? Is this supposed to be a generator?
@NicoBerrogorry, it's a generator. See docs on contextlib.contextmanager. This is a very useful pattern in Python, worth learning.
@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.
B
Brian Clapper

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)

Good general idea. Here an Activestate recipe without other dependencies.
Dependencies are bad. Python's built-in contextlib.contextmanager decorator is good. See cdunn2001's decorator-based answer, which would ideally be the accepted answer now.
m
mrdiskodave

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")

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!
F
Federico A. Ramponi

os.chdir() is the right way.


E
Elliot A.

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


R
Riccardo D
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.


Works well. One can use os.getcwd() to verify the current directory both before and after changing the directory..
Y
Yauhen Yakimovich

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

from sh import cd, ls

cd('/tmp')
print ls()

D
D.K

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)


A
AXO

The Path objects in path library offer both a context manager and a chdir method for this purpose:

from path import Path

with Path("somewhere"):
    ...

Path("somewhere").chdir()

H
HoldOffHunger

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.


D
David McCracken

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.
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.
Actually that answer was selected years ago. If it wasn't appropriate it would have been called out many times since then.
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.