Як я можу змінити робочий каталог в 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. Ви не можете використовувати os.chdir()для зміни CWD процесу виклику.


3
Легка відповідь декоратора cdunn2001 - ідеальний підхід для сучасного Python. Наведена відповідь демонструє, чому. Ніколи не дзвоніть os.chdir()за межі менеджера контексту, якщо ви не думаєте, що знаєте, що робите. ( Напевно, ні. )
Сесіль Керрі

6
Я думаю, що це найпростіший спосіб в інтерактивній оболонці. Зауважте, що в Windows ви повинні використовувати косої риски, наприкладos.chdir("C:/path/to/location")
Josiah

Варто пам’ятати, що якщо ви зробите свою програму python виконуваною і запустите її в 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:та отримати доступ до попереднього режисера якcm.savedPath
Сем Ф

Зауважте, що є випадки, коли повернення до старої каталоги (тієї, що зберігається у "збереженому шляху") неможливе. Наприклад, якщо більш привілейований процес запускає менш привілейований процес, другий процес успадковує перший робочий каталог процесів, навіть у тих випадках, коли другий процес не може увійти в цей робочий каталог своїми можливостями.
Кай Петцке

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
Блискучість! Якби вступний коментар із прийнятої відповіді був введений у цю відповідь, це було б незмірно ідеальним. І все ж, лаконічна відповідь цієї відповіді, безпечна реалізація, є гарантією всіх результатів, які я маю дати.
Сесіль Карі

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

ОНОВЛЕННЯ

Якщо ви віддаєте перевагу власноруч:

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
Гарна загальна ідея. Ось рецепт Activestate без інших залежностей.
cfi

4
Залежності погані. Вбудований contextlib.contextmanagerдекоратор Python - це добре. Див cdunn2001 «и декоратора на основі відповіді , який би ідеально бути прийнято відповідати прямо зараз.
Сесіль Карі

14

Як уже вказували інші, всі рішення, наведені вище, лише змінюють робочий каталог поточного процесу. Це втрачається при виході назад до оболонки Unix. Якщо у відчаї ви можете змінити батьківський каталог оболонки в 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 є своєрідною, але не реальною. Оновлення! Оновлення для всіх!
Сесіль Карі



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

Якщо ви користуєтесь шпідером та любите GUI, ви можете просто натиснути кнопку папки у верхньому правому куті екрану та перейти через папки / каталоги, які ви хочете як поточний каталог. Після цього ви можете перейти на вкладку «Провідник файлів» вікна у програмі IDE шпигуна, і ви зможете побачити всі файли / папки, які там присутні. Щоб перевірити поточний робочий каталог, перейдіть до консолі IDE шпигуна та просто наберіть

pwd

він буде друкувати той самий шлях, який ви вибрали раніше.


-1

Зміна поточного каталогу сценарію процес тривіальний. Я думаю, що насправді питання полягає в тому, як змінити поточний каталог вікна команд, з якого викликається скрипт python, що дуже важко. Сценарій Bat в Windows або сценарій Bash в оболонці Bash може це зробити за допомогою звичайної команди CD, оскільки сама оболонка є інтерпретатором. І в Windows, і в Linux Linux Python - це програма, і жодна програма не може безпосередньо змінити середовище батьків. Однак комбінація простого скрипта оболонки з Python-скриптом, що робить більшість важких матеріалів, може досягти бажаного результату. Наприклад, щоб скласти розширену команду cd з історією обходу для перегляду назад / вперед / вибору, я написав відносно складний скрипт Python, на який посилався простий сценарій bat. Список обходу зберігається у файлі, з цільовим каталогом у першому рядку. Коли сценарій python повертається, сценарій bat читає перший рядок файлу і робить його аргументом 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)

Хоча це приємна відповідь, ОП обрав відповідь, яка говорить, що мова не йде про зміну CWD батьківського процесу. Це усуває будь-яку можливу плутанину щодо того, що означає питання.
Олов'яна людина

Тин Ман - ця відповідь була обрана ще до того, як я опублікував свою пропозицію. Я думаю, що широкі відповіді, можливо, були бентежні. CD в ​​даному процесі (тобто сценарій python) настільки простий, що я не знаю, чому хтось би це запитував.
Девід Маккракен

1
Насправді цю відповідь було обрано років тому. Якби це не було доречно, відтоді його називали б багато разів.
Олов'яний чоловік

Я думаю, що плутанина залишається. Зовсім недавно на питання "моделювання команди" cd "linux в python та збереження зміни каталогів після виходу програми [дублікат]" було відхилено як відповідь тут, але насправді на це питання не вирішено обраною відповіддю. Моя пропозиція для Windows, але проблеми однакові в Linux.
Девід Маккракен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.