Làm cách nào để thay đổi thư mục làm việc trong Python?


Câu trả lời:


765

Bạn có thể thay đổi thư mục làm việc với:

import os

os.chdir(path)

Có hai cách tốt nhất để làm theo khi sử dụng phương pháp này:

  1. Bắt ngoại lệ (WindowsError, OSError) trên đường dẫn không hợp lệ. Nếu ngoại lệ được ném, không thực hiện bất kỳ thao tác đệ quy nào, đặc biệt là các thao tác phá hoại. Họ sẽ hoạt động trên con đường cũ chứ không phải con đường mới.
  2. Quay trở lại thư mục cũ của bạn khi bạn hoàn thành. Điều này có thể được thực hiện theo cách an toàn ngoại lệ bằng cách gói cuộc gọi chdir của bạn vào một trình quản lý bối cảnh, giống như Brian M. Hunt đã làm trong câu trả lời của mình .

Thay đổi thư mục làm việc hiện tại trong một quy trình con không thay đổi thư mục làm việc hiện tại trong quy trình cha. Điều này cũng đúng với trình thông dịch Python. Bạn không thể sử dụng os.chdir()để thay đổi CWD của quá trình gọi.


3
Câu trả lời dựa trên trang trí nhẹ của cdunn2001 là cách tiếp cận lý tưởng cho Python hiện đại. Câu trả lời trên chứng minh tại sao. Không bao giờ gọi os.chdir()bên ngoài một người quản lý bối cảnh, trừ khi bạn nghĩ rằng bạn biết những gì bạn đang làm. ( Bạn có thể không. )
Cecil Curry

6
Đây là cách dễ nhất trong một vỏ tương tác, tôi nghĩ vậy. Lưu ý rằng trong Windows, bạn phải sử dụng dấu gạch chéo về phía trước, nhưos.chdir("C:/path/to/location")
Josiah

Một điều cần lưu ý là nếu bạn làm cho chương trình python của bạn có thể thực thi được và chạy nó trong cron, nó sẽ khởi động trong thư mục chính của bạn. Vì vậy, tốt nhất là sử dụng một con đường đủ điều kiện. Điều này chắc chắn hoạt động, nhưng tôi vẫn sử dụng các đường dẫn đủ điều kiện trong bất kỳ tập lệnh nào tôi có thể gọi từ Python vì không có gì đảm bảo rằng điều này sẽ áp dụng bên ngoài chương trình Python.
SDsolar

310

Đây là một ví dụ về trình quản lý bối cảnh để thay đổi thư mục làm việc. Nó đơn giản hơn phiên bản ActiveState được đề cập ở nơi khác, nhưng điều này hoàn thành công việc.

Quản lý bối cảnh: 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)

Hoặc thử tương đương ngắn gọn hơn (bên dưới) , sử dụng ContextManager .

Thí dụ

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.

Nếu bạn cần biết thư mục nào bạn đã thay đổi TỪ, bạn chỉ có thể thêm return selfvào cuối __enter__. Bằng cách đó bạn có thể làm with cd('foo') as cm:và truy cập thư mục trước nhưcm.savedPath
Sam F

Lưu ý rằng có những trường hợp, trong đó việc quay lại thư mục cũ (thư mục được lưu trong "yetPath") là không thể. Ví dụ, nếu một quy trình được bảo mật nhiều hơn chạy một quy trình ít được bảo mật hơn, quy trình thứ hai sẽ kế thừa thư mục làm việc của quy trình đầu tiên, ngay cả trong những trường hợp đó, trong đó quy trình thứ hai không thể vào thư mục làm việc đó với khả năng của chính nó.
Kai Petzke

140

Tôi sẽ sử dụng os.chdirnhư thế này:

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

Nhân tiện, nếu bạn cần tìm ra con đường hiện tại của mình, hãy sử dụng os.getcwd().

Thêm ở đây


117

cd() là dễ dàng để viết bằng cách sử dụng một máy phát điện và trang trí.

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)

Sau đó, thư mục được hoàn nguyên ngay cả sau khi ném ngoại lệ:

os.chdir('/home')

with cd('/tmp'):
    # ...
    raise Exception("There's no place like home.")
# Directory is now back to '/home'.

3
Ngoài ra, lưu ý sai lầm tiềm năng này (để quên try/finally).
cdunn2001

5
Sáng chói! Nếu bình luận giới thiệu từ các câu trả lời chấp nhận được tiêm vào này câu trả lời, đây sẽ là vô cùng lý tưởng. Tuy nhiên, câu trả lời ngắn gọn, an toàn này của Python đảm bảo tất cả các ưu đãi tôi phải cung cấp.
Cecil Curry

3
Tại sao yieldvà không return? Đây có phải là một máy phát điện?
EKons

Hãy bình luận về sự liên quan của năng suất so với lợi nhuận!
NicoBerrog lỗi

1
@NicoBerrog lỗi, đó là một máy phát điện. Xem tài liệu trên contextlib.contextmanager . Đây là một mẫu rất hữu ích trong Python, đáng để học hỏi.
cdunn2001

25

Nếu bạn đang sử dụng phiên bản Python tương đối mới, bạn cũng có thể sử dụng trình quản lý bối cảnh, chẳng hạn như phiên bản này :

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

CẬP NHẬT

Nếu bạn thích tự cuộn:

import os
from contextlib import contextmanager

@contextmanager
def working_directory(directory):
    owd = os.getcwd()
    try:
        os.chdir(directory)
        yield directory
    finally:
        os.chdir(owd)

1
Ý kiến ​​chung tốt. Đây là một công thức Activestate mà không cần phụ thuộc khác.
cfi

4
Phụ thuộc là xấu. Trang trí tích hợp của Python contextlib.contextmanagerlà tốt. Xem câu trả lời dựa trên trang trí của cdunn2001 , lý tưởng nhất sẽ là câu trả lời được chấp nhận ngay bây giờ.
Cecil Curry

14

Như đã được chỉ ra bởi những người khác, tất cả các giải pháp ở trên chỉ thay đổi thư mục làm việc của quy trình hiện tại. Điều này bị mất khi bạn thoát trở lại shell Unix. Nếu tuyệt vọng, bạn có thể thay đổi thư mục shell cha trên Unix với bản hack khủng khiếp này:

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

4
Hack điên, dễ vỡ được upvote bắt buộc. Không ai nên làm điều này, đặc biệt với điều đó "và nếu người dùng gõ trong khi nó chạy ..." hãy cẩn thận. Tuy nhiên, điều đó làm tôi khó chịu với cái vòng đeo cổ của phiến quân trong tôi để thấy rằng việc thay đổi CWD gốc loại nhưng không thực sự khả thi. Upvote! Upvotes cho tất cả!
Cecil Curry


11

os.chdir()là phiên bản Pythonic của cd.


8
import os

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

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

Bạn có thể sử dụng cả hai với os.chdir (abs_path) hoặc os.chdir (rel_path), không cần phải gọi os.getcwd () để sử dụng đường dẫn tương đối.


Hoạt động tốt. Người ta có thể sử dụng os.getcwd () để xác minh thư mục hiện tại cả trước và sau khi thay đổi thư mục ..
vinsinraw

6

Tiếp tục đi vào hướng được chỉ ra bởi Brian và dựa trên sh (1.0.8+)

from sh import cd, ls

cd('/tmp')
print ls()

3

Nếu bạn muốn thực hiện một cái gì đó như tùy chọn "cd ..", chỉ cần gõ:

os.chdir ("..")

nó giống như trong Windows cmd: cd .. Tất nhiên nhập os là không cần thiết (ví dụ: nhập nó dưới dạng dòng thứ nhất của mã của bạn)


0

Nếu bạn sử dụng spyder và yêu GUI, bạn chỉ cần nhấp vào nút thư mục ở góc trên bên phải màn hình của bạn và điều hướng qua các thư mục / thư mục bạn muốn làm thư mục hiện tại. Sau khi làm như vậy, bạn có thể chuyển đến tab trình duyệt tệp của cửa sổ trong IDE gián điệp và bạn có thể thấy tất cả các tệp / thư mục có trong đó. để kiểm tra thư mục làm việc hiện tại của bạn, hãy vào bảng điều khiển của IDE gián điệp và chỉ cần gõ

pwd

nó sẽ in cùng một đường dẫn như bạn đã chọn trước đó.


-1

Thay đổi thư mục hiện tại của quá trình tập lệnh là chuyện nhỏ. Tôi nghĩ rằng câu hỏi thực sự là làm thế nào để thay đổi thư mục hiện tại của cửa sổ lệnh mà từ đó một kịch bản python được gọi, điều này rất khó. Tập lệnh Bat trong Windows hoặc tập lệnh Bash trong trình bao Bash có thể thực hiện việc này bằng lệnh cd thông thường vì chính trình bao là trình thông dịch. Trong cả Windows và Linux Python là một chương trình và không có chương trình nào có thể thay đổi trực tiếp môi trường của cha mẹ nó. Tuy nhiên, sự kết hợp của tập lệnh shell đơn giản với tập lệnh Python thực hiện hầu hết các nội dung cứng có thể đạt được kết quả mong muốn. Ví dụ, để tạo một lệnh cd mở rộng với lịch sử truyền tải cho truy cập ngược / tiến / chọn, tôi đã viết một tập lệnh Python tương đối phức tạp được gọi bởi một tập lệnh bat đơn giản. Danh sách truyền tải được lưu trữ trong một tệp, với thư mục đích trên dòng đầu tiên. Khi tập lệnh python trả về, tập lệnh bat đọc dòng đầu tiên của tập tin và biến nó thành đối số thành cd. Kịch bản dơi hoàn chỉnh (trừ bình luận cho ngắn gọn) là:

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

Kịch bản python, dSup.py là:

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)

Mặc dù đó là một câu trả lời hay, OP đã chọn một câu trả lời nói rằng đó không phải là về việc thay đổi CWD của quy trình mẹ. Điều đó làm sáng tỏ mọi sự nhầm lẫn có thể có về ý nghĩa của câu hỏi.
Tin Man

Gửi Tin Man-- câu trả lời đó đã được chọn trước khi tôi đăng đề nghị của mình. Tôi nghĩ rằng các câu trả lời khác nhau có thể đã gây nhầm lẫn. cd trong một quy trình nhất định (ví dụ: tập lệnh python) đơn giản đến mức tôi không biết tại sao mọi người lại hỏi nó.
David McCracken

1
Thật ra câu trả lời đã được chọn từ nhiều năm trước. Nếu nó không phù hợp, nó đã được gọi ra nhiều lần kể từ đó.
Tin Man

Tôi nghĩ rằng sự nhầm lẫn vẫn còn. Gần đây, câu hỏi "mô phỏng lệnh cd cd của linux trong python và tiếp tục thay đổi thư mục sau khi chương trình thoát [trùng lặp]" đã bị loại bỏ vì đã được trả lời ở đây, nhưng trên thực tế, câu hỏi này không được trả lời bởi câu trả lời được chọn. Đề nghị của tôi là dành cho Windows nhưng các vấn đề đều giống nhau trong Linux.
David McCracken
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.