Как изменить рабочий каталог в Python?


698

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

Ответы:


767

Вы можете изменить рабочий каталог с помощью:

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

311

Вот пример контекстного менеджера для изменения рабочего каталога. Это проще, чем версия 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().

Больше здесь


118

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
Brilliance! Если бы вводный комментарий из принятого ответа был введен в этот ответ, это было бы неизмеримо идеальным. Тем не менее, этот ответ лаконичен, Python-безопасная реализация гарантирует все положительные отзывы, которые я должен дать.
Сесил Карри

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
Зависимости плохие. Встроенный в Python contextlib.contextmanagerдекоратор хорош. Смотрите ответ 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
Безумный, хрупкий взлом получает обязательные откаты. Никто никогда не должен делать это, особенно с этим "и если пользователь печатает, пока он работает ...", будьте осторожны. Тем не менее, это щекочет повстанческой neckbeard во мне , чтобы увидеть , что изменение родительского УХА является родом-но на самом деле не представляется возможным. Upvotes! 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. Конечно, необходим импорт os (например, введите его в качестве первой строки вашего кода)


0

Если вы используете spyder и love GUI, вы можете просто нажать на кнопку папки в верхнем правом углу экрана и перемещаться по папкам / каталогам, которые вы хотите использовать в качестве текущего каталога. После этого вы можете перейти на вкладку «Обозреватель файлов» окна в IDE spyder и увидеть все имеющиеся там файлы / папки. чтобы проверить текущий рабочий каталог, перейдите в консоль IDE spyder и просто наберите

pwd

он напечатает тот же путь, который вы выбрали ранее.


-1

Изменение текущей директории процесса скрипта тривиально. Я думаю, что на самом деле вопрос заключается в том, как изменить текущий каталог командного окна, из которого вызывается скрипт Python, что очень сложно. Сценарий Bat в Windows или сценарий Bash в оболочке Bash могут сделать это с помощью обычной команды cd, поскольку сама оболочка является интерпретатором. И в Windows, и в 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)

Хотя это хороший ответ, OP выбрал ответ, который говорит, что речь не идет об изменении CWD родительского процесса. Это проясняет любую возможную путаницу в том, что означает вопрос.
Жестянщик

To Tin Man-- этот ответ был выбран до того, как я опубликовал свое предложение. Я думаю, что ответы на самые разные вопросы могли сбить с толку. cd в данном процессе (например, скрипт на python) настолько прост, что я не знаю, почему кто-то спросил бы его.
Дэвид МакКракен,

1
На самом деле этот ответ был выбран несколько лет назад. Если бы это не было уместно, с тех пор его вызывали бы много раз.
Жестянщик

Я думаю, что путаница остается. Совсем недавно вопрос «имитация команды Linux« cd »в python и сохранение изменения каталога после выхода из программы [duplicate]» был отклонен как ответ здесь, но на самом деле этот вопрос не решен в выбранном ответе. Мое предложение для Windows, но проблемы те же в Linux.
Дэвид МакКракен
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.