ฉันจะเปลี่ยนไดเรกทอรีการทำงานใน Python ได้อย่างไร


697

cd เป็นคำสั่งเชลล์เพื่อเปลี่ยนไดเรกทอรีทำงาน

ฉันจะเปลี่ยนไดเรกทอรีการทำงานปัจจุบันใน Python ได้อย่างไร


2
ดังนั้นในล่ามos.chdir(os.path.join(os.path.abspath(os.path.curdir),u'subfolder'))- หรือ
Mr_and_Mrs_D

2
สิ่งที่น่าสนใจในบริบทนี้: ค้นหาไดเรกทอรีปัจจุบันและไดเรกทอรีของไฟล์ :os.getcwd()
Martin Thoma

คำตอบ:


766

คุณสามารถเปลี่ยนไดเรกทอรีการทำงานด้วย:

import os

os.chdir(path)

มีสองแนวทางปฏิบัติที่ดีที่สุดที่ควรปฏิบัติเมื่อใช้วิธีนี้:

  1. ตรวจสอบข้อยกเว้น (WindowsError, OSError) บนเส้นทางที่ไม่ถูกต้อง หากมีการโยนข้อยกเว้นอย่าดำเนินการซ้ำซ้ำโดยเฉพาะอย่างยิ่งการทำลาย พวกเขาจะทำงานบนเส้นทางเดิมไม่ใช่เส้นทางใหม่
  2. กลับไปยังไดเรกทอรีเก่าของคุณเมื่อคุณทำเสร็จแล้ว ซึ่งสามารถทำได้ในลักษณะที่มีข้อยกเว้นความปลอดภัยโดยการตัดโทร chdir ของคุณในการจัดการบริบทเช่นไบรอันเมตรล่าได้ในคำตอบของเขา

การเปลี่ยนไดเร็กทอรีการทำงานปัจจุบันในกระบวนการย่อยไม่เปลี่ยนไดเร็กทอรีการทำงานปัจจุบันในกระบวนการพาเรนต์ นี่เป็นความจริงของ Python interpreter เช่นกัน คุณไม่สามารถใช้os.chdir()เพื่อเปลี่ยน CWD ของกระบวนการโทร


3
คำตอบจากมัณฑนากรที่มีน้ำหนักเบาของcdunn2001เป็นวิธีที่เหมาะสำหรับ Python สมัยใหม่ คำตอบข้างต้นแสดงให้เห็นว่าทำไม อย่าโทรos.chdir()ไปภายนอกผู้จัดการบริบทเว้นแต่คุณคิดว่าคุณรู้ว่ากำลังทำอะไรอยู่ ( คุณอาจจะทำไม่ได้ )
เซซิลแกงกะหรี่

6
ฉันคิดว่านี่เป็นวิธีที่ง่ายที่สุดในเชลล์แบบโต้ตอบ โปรดทราบว่าใน Windows คุณต้องใช้ฟอร์เวิร์ดสแลชเช่นos.chdir("C:/path/to/location")
Josiah

สิ่งหนึ่งที่ต้องระวังคือถ้าคุณทำให้โปรแกรมไพ ธ อนทำงานได้และทำงานใน cron มันจะเริ่มทำงานในโฮมไดเร็กตอรี่ของคุณ ดังนั้นจึงเป็นการดีที่สุดที่จะใช้เส้นทางที่ผ่านการรับรองโดยสมบูรณ์ วิธีนี้ใช้ได้ผลแน่นอน แต่ฉันยังคงใช้เส้นทางที่มีคุณสมบัติครบถ้วนในสคริปต์ใด ๆ ที่ฉันอาจเรียกใช้จาก Python เนื่องจากไม่มีการรับประกันว่าจะใช้นอกโปรแกรม Python เอง
SDsolar

310

นี่คือตัวอย่างของตัวจัดการบริบทเพื่อเปลี่ยนไดเรกทอรีทำงาน มันง่ายกว่าเวอร์ชัน ActiveState ที่อ้างถึงที่อื่น แต่สิ่งนี้จะทำให้งานสำเร็จ

ผู้จัดการบริบท: 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)

หรือลองเทียบเท่ารัดกุมมากขึ้น (ด้านล่าง)โดยใช้ContextManager

ตัวอย่าง

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.

หากคุณต้องการที่จะรู้ว่าสิ่งที่ไดเรกทอรีที่คุณเปลี่ยนจากคุณก็สามารถเพิ่มในตอนท้ายของreturn self __enter__ด้วยวิธีนี้คุณสามารถทำได้with cd('foo') as cm:และเข้าถึง dir ก่อนหน้านี้ในฐานะcm.savedPath
Sam F

โปรดทราบว่ามีหลายกรณีที่ไม่สามารถกลับไปที่ไดเรกทอรีเก่า (ที่เก็บไว้ใน "savePath") เป็นไปไม่ได้ ตัวอย่างเช่นหากกระบวนการที่มีการสงวนมากขึ้นเรียกใช้กระบวนการที่มีสิทธิ์น้อยกว่ากระบวนการที่สองสืบทอดไดเรกทอรีการทำงานของกระบวนการแรกแม้ในกรณีเหล่านั้นโดยที่กระบวนการที่สองไม่สามารถเข้าสู่ไดเรกทอรีการทำงานนั้นด้วยความสามารถของตัวเอง
Kai Petzke

141

ฉันจะใช้os.chdirสิ่งนี้:

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

โดยวิธีการถ้าคุณต้องการที่จะคิดออกเส้นทางปัจจุบันของคุณใช้ os.getcwd()โดยวิธีการที่ถ้าคุณต้องการที่จะคิดออกเส้นทางปัจจุบันของคุณใช้

เพิ่มเติมที่นี่


117

cd() ง่ายต่อการเขียนโดยใช้เครื่องกำเนิดไฟฟ้าและมัณฑนากร

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)

จากนั้นไดเรกทอรีจะถูกเปลี่ยนกลับแม้จะเกิดข้อยกเว้นขึ้น:

os.chdir('/home')

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

3
นอกจากนี้ให้สังเกตความผิดพลาดที่อาจเกิดขึ้นนี้ (เพื่อลืมtry/finally)
cdunn2001

5
ความสดใส! หากความเห็นเบื้องต้นจากคำตอบที่ยอมรับนั้นถูกใส่เข้าไปในคำตอบนี้มันจะเหมาะอย่างยิ่งล้นพ้น กระนั้นก็ตามคำตอบที่รัดกุมและปลอดภัยการใช้ Pythonically รับประกัน upvotes ทั้งหมดที่ฉันต้องให้
เซซิลแกงกะหรี่

3
ทำไมyieldไม่return? นี่ควรจะเป็นเครื่องกำเนิดไฟฟ้าหรือไม่?
EKons

กรุณาแสดงความคิดเห็นเกี่ยวกับความเกี่ยวข้องของผลตอบแทนเทียบกับผลตอบแทน!
NicoBerrogorry

1
@NicoBerrogorry มันเป็นเครื่องกำเนิดไฟฟ้า ดูเอกสารในcontextlib.contextmanager นี่เป็นรูปแบบที่มีประโยชน์มากใน Python คุ้มค่ากับการเรียนรู้
cdunn2001

25

หากคุณใช้ Python เวอร์ชันใหม่คุณสามารถใช้ตัวจัดการบริบทเช่นอันนี้ :

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

หากคุณต้องการที่จะม้วนของคุณเอง:

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
ความคิดทั่วไปที่ดี ที่นี่สูตรเปิดใช้งานโดยไม่มีการอ้างอิงอื่น ๆ
cfi

4
การพึ่งพานั้นไม่ดี contextlib.contextmanagerตกแต่งภายในของ Python เป็นสิ่งที่ดี ดูคำตอบจากมัณฑนากรของcdunn2001ซึ่งน่าจะเป็นคำตอบที่ได้รับการยอมรับในขณะนี้
เซซิลแกงกะหรี่

14

ดังที่ผู้อื่นชี้ให้เห็นแล้ววิธีแก้ปัญหาทั้งหมดข้างต้นเปลี่ยนไดเรกทอรีการทำงานของกระบวนการปัจจุบันเท่านั้น สิ่งนี้จะหายไปเมื่อคุณออกจากระบบกลับไปยัง Unix shell ถ้าหมดหวังคุณสามารถเปลี่ยนพาเรนต์เชลล์หลักใน Unix ด้วยแฮ็คที่น่ากลัวนี้:

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
การแฮ็กที่เปราะบางและบ้าๆ ไม่มีใครควรทำเช่นนี้โดยเฉพาะอย่างยิ่งกับ "และถ้าผู้ใช้พิมพ์ในขณะที่มันทำงาน ... " ข้อแม้ ถึงกระนั้นมันก็ยังทำให้คอของกบฏที่อยู่ในตัวฉันสั่นคลอนเพื่อที่จะเห็นว่าการเปลี่ยน CWD ของผู้ปกครองนั้นเป็นเรื่องง่าย แต่ไม่สามารถทำได้ upvotes! โหวตขึ้นสำหรับทุกคน!
เซซิลแกงกะหรี่



8
import os

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

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

คุณสามารถใช้ทั้งกับ os.chdir (abs_path) หรือ os.chdir (rel_path) ไม่จำเป็นต้องเรียก os.getcwd () เพื่อใช้เส้นทางสัมพัทธ์


ทำได้ดี. หนึ่งสามารถใช้ os.getcwd () เพื่อตรวจสอบไดเรกทอรีปัจจุบันทั้งก่อนและหลังการเปลี่ยนไดเรกทอรี ..
vinsinraw


3

หากคุณต้องการดำเนินการบางอย่างเช่นตัวเลือก "cd .. " ให้พิมพ์:

os.chdir ( "..")

มันเป็นเช่นเดียวกับใน Windows cmd: cd .. แน่นอนว่าการนำเข้าระบบปฏิบัติการเป็นสิ่งจำเป็น (เช่นพิมพ์เป็นบรรทัดที่ 1 ของรหัสของคุณ)


0

หากคุณใช้ Spyder และ GUI รักคุณสามารถคลิกที่ปุ่มโฟลเดอร์ที่มุมขวาบนของหน้าจอและนำทางผ่านโฟลเดอร์ / ไดเรกทอรีที่คุณต้องการเป็นไดเรกทอรีปัจจุบัน หลังจากทำเช่นนั้นคุณสามารถไปที่แท็บ File explorer ของหน้าต่างใน spyder IDE และคุณสามารถดูไฟล์ / โฟลเดอร์ทั้งหมดที่อยู่ในนั้น เพื่อตรวจสอบไดเรกทอรีการทำงานปัจจุบันของคุณไปที่คอนโซลของ spyder IDE และเพียงพิมพ์

pwd

มันจะพิมพ์เส้นทางเดียวกับที่คุณเลือกมาก่อน


-1

การเปลี่ยนไดเรกทอรีปัจจุบันของกระบวนการสคริปต์เป็นเรื่องเล็กน้อย ฉันคิดว่าคำถามคือวิธีการเปลี่ยนไดเรกทอรีปัจจุบันของหน้าต่างคำสั่งที่เรียกใช้สคริปต์ python ซึ่งยากมาก สคริปต์ Bat ใน Windows หรือสคริปต์ Bash ใน Bash shell สามารถทำได้ด้วยคำสั่ง cd ปกติเนื่องจากเชลล์เป็นตัวแปลภาษา ทั้งใน Windows และ Linux Python เป็นโปรแกรมและไม่มีโปรแกรมใดสามารถเปลี่ยนสภาพแวดล้อมของแม่ได้โดยตรง อย่างไรก็ตามการรวมกันของสคริปต์เชลล์แบบเรียบง่ายกับสคริปต์ Python ที่ทำสิ่งที่ยากที่สุดสามารถบรรลุผลลัพธ์ที่ต้องการ ตัวอย่างเช่นในการสร้างคำสั่ง cd แบบขยายพร้อมประวัติย้อนกลับสำหรับการย้อนกลับ / ไปข้างหน้า / เลือกฉันจะเขียนสคริปต์ Python ที่ค่อนข้างซับซ้อนซึ่งเรียกใช้โดยสคริปต์ค้างคาวแบบง่าย รายการเส้นทางผ่านจะถูกเก็บไว้ในไฟล์ ด้วยไดเรกทอรีเป้าหมายในบรรทัดแรก เมื่อสคริปต์ python ส่งคืนสคริปต์ค้างคาวจะอ่านบรรทัดแรกของไฟล์และทำให้อาร์กิวเมนต์เป็น cd สคริปต์ค้างคาวที่สมบูรณ์ (ลบความคิดเห็นเพื่อความกระชับ) คือ:

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

สคริปต์ python dSup.py คือ:

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)

ในขณะที่มันเป็นคำตอบที่ดี OP เลือกคำตอบที่บอกว่ามันไม่เกี่ยวกับการเปลี่ยน CWD ของกระบวนการหลัก นั่นเป็นการขจัดความสับสนที่เป็นไปได้เกี่ยวกับความหมายของคำถาม
คนดีบุก

สำหรับ Tin Man - คำตอบนั้นได้รับเลือกก่อนที่ฉันจะโพสต์คำแนะนำของฉัน ฉันคิดว่าคำตอบที่หลากหลายอาจทำให้สับสน cd ในกระบวนการที่กำหนด (เช่นสคริปต์ python) นั้นง่ายมากจนฉันไม่รู้ว่าทำไมใครถึงถาม
David McCracken

1
จริงๆแล้วคำตอบนั้นถูกเลือกเมื่อหลายปีก่อน หากไม่เหมาะสมก็จะถูกเรียกออกมาหลายครั้งตั้งแต่นั้นมา
Tin Man

ฉันคิดว่าความสับสนนั้นยังคงอยู่ เมื่อเร็ว ๆ นี้คำถาม "จำลองคำสั่ง" cd "ของ linux ใน python และยืนยันการเปลี่ยนแปลงไดเรกทอรีหลังจากโปรแกรมออก [ซ้ำ]" ถูกไล่ออกเนื่องจากได้รับคำตอบที่นี่ แต่ที่จริงแล้วคำถามนี้ไม่ได้ตอบโดยคำตอบที่เลือก คำแนะนำของฉันสำหรับ Windows แต่ปัญหาเหมือนกันใน Linux
David McCracken
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.