Come cambio la directory di lavoro in Python?


Risposte:


766

Puoi cambiare la directory di lavoro con:

import os

os.chdir(path)

Esistono due best practice da seguire quando si utilizza questo metodo:

  1. Cattura l'eccezione (WindowsError, OSError) su un percorso non valido. Se viene generata l'eccezione, non eseguire alcuna operazione ricorsiva, specialmente distruttiva. Opereranno sul vecchio sentiero e non su quello nuovo.
  2. Ritorna alla tua vecchia directory quando hai finito. Questo può essere fatto in maniera eccezionalmente sicura avvolgendo la tua chiamata chdir in un gestore di contesto, come ha fatto Brian M. Hunt nella sua risposta .

La modifica della directory di lavoro corrente in un sottoprocesso non modifica la directory di lavoro corrente nel processo padre. Questo vale anche per l'interprete Python. Non è possibile utilizzare os.chdir()per modificare il CWD del processo di chiamata.


3
La risposta basata su decoratore leggero di cdunn2001 è l'approccio ideale per il moderno Python. La risposta sopra dimostra il perché. Non chiamare mai os.chdir()al di fuori di un gestore di contesto, a meno che non pensi di sapere cosa stai facendo. ( Probabilmente no. )
Cecil Curry,

6
Questo è il modo più semplice in una shell interattiva, credo. Si noti che in Windows, è necessario utilizzare le barre, come ad esempioos.chdir("C:/path/to/location")
Josiah, il

L'unica cosa da tenere presente è che se rendi il tuo programma Python un eseguibile e lo esegui in cron, questo si avvierà nella tua home directory. Quindi è meglio usare un percorso completo. Funziona sicuramente, eppure uso ancora percorsi completamente qualificati in qualsiasi script che potrei invocare da Python perché non vi è alcuna garanzia che questo si applichi al di fuori del programma Python stesso.
SDsolar,

310

Ecco un esempio di un gestore di contesto per modificare la directory di lavoro. È più semplice di una versione di ActiveState citata altrove, ma questo porta a termine il lavoro.

Gestore del contesto: 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)

Oppure prova l' equivalente più conciso (sotto) , usando ContextManager .

Esempio

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.

Se hai mai bisogno di sapere da quale directory hai cambiato, puoi semplicemente aggiungere return selfalla fine di __enter__. In questo modo puoi fare with cd('foo') as cm:e accedere alla directory precedente comecm.savedPath
Sam F

Si noti che ci sono casi in cui non è possibile tornare alla vecchia directory (quella memorizzata in "savedPath"). Ad esempio, se un processo più privilegiato esegue un processo meno privilegiato, il secondo processo eredita la prima directory di lavoro dei processi, anche in quei casi in cui il secondo processo non può accedere a quella directory di lavoro con le proprie capacità.
Kai Petzke,

140

Vorrei usare os.chdircosì:

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

A proposito, se hai bisogno di capire il tuo percorso attuale, usa os.getcwd().

Più qui


117

cd() è facile da scrivere usando un generatore e un decoratore.

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)

Quindi, la directory viene ripristinata anche dopo aver generato un'eccezione:

os.chdir('/home')

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

3
Inoltre, nota questo potenziale errore (per dimenticare try/finally).
cdunn2001,

5
Brillantezza! Se il commento introduttivo della risposta accettata fosse inserito in questa risposta, questo sarebbe incommensurabilmente ideale. Tuttavia, l'implementazione concisa e sicura di questa risposta garantisce tutti i voti che devo dare.
Cecil Curry,

3
Perché yielde no return? Questo dovrebbe essere un generatore?
EKons,

Si prega di commentare la rilevanza del rendimento rispetto al rendimento!
NicoBerrogorry,

1
@NicoBerrogorry, è un generatore. Consulta i documenti su contextlib.contextmanager . Questo è un modello molto utile in Python, che vale la pena imparare.
cdunn2001,

25

Se stai utilizzando una versione relativamente nuova di Python, puoi anche utilizzare un gestore di contesto, ad esempio questo :

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

AGGIORNARE

Se preferisci lanciare il tuo:

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
Buona idea generale Ecco una ricetta Activestate senza altre dipendenze.
cfi,

4
Le dipendenze sono cattive. Il contextlib.contextmanagerdecoratore incorporato di Python è buono. Vedere cdunn2001 's risposta decoratore-based , che sarebbe idealmente essere la risposta accettata ora.
Cecil Curry,

14

Come già sottolineato da altri, tutte le soluzioni sopra cambiano solo la directory di lavoro del processo corrente. Questo si perde quando si ritorna alla shell Unix. Se sei disperato puoi cambiare la directory della shell genitore su Unix con questo orribile 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")

4
L'hacking folle e fragile ottiene voti obbligatori. Nessuno dovrebbe mai farlo, in particolare con questo "e se l'utente digita mentre esegue ..." avvertenza. Tuttavia, mi titola la barba ribelle ribelle in me per vedere che cambiare il CWD genitore è un po 'ma non è realmente fattibile. Upvotes! Voti positivi per tutti!
Cecil Curry,


11

os.chdir()è la versione Pythonic di cd.


8
import os

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

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

Puoi usare entrambi con os.chdir (abs_path) o os.chdir (rel_path), non è necessario chiamare os.getcwd () per usare un percorso relativo.


Funziona bene. Si può usare os.getcwd () per verificare la directory corrente sia prima che dopo aver cambiato la directory ..
vinsinraw

6

Più avanti nella direzione indicata da Brian e basata su sh (1.0.8+)

from sh import cd, ls

cd('/tmp')
print ls()

3

Se desideri eseguire qualcosa di simile all'opzione "cd ..", digita:

os.chdir ( "..")

è lo stesso di Windows cmd: cd .. Ovviamente import os è necessario (es. digitalo come prima riga del tuo codice)


0

Se usi spyder e ami la GUI, puoi semplicemente fare clic sul pulsante della cartella nell'angolo in alto a destra dello schermo e navigare tra le cartelle / directory che desideri come directory corrente. Dopo averlo fatto, puoi andare alla scheda Esplora file della finestra in IDE spyder e puoi vedere tutti i file / cartelle presenti lì. per controllare la tua directory di lavoro corrente vai alla console di IDE spyder e digita semplicemente

pwd

stamperà lo stesso percorso selezionato in precedenza.


-1

La modifica della directory corrente del processo di script è banale. Penso che la domanda sia in realtà come cambiare la directory corrente della finestra di comando da cui viene invocato uno script Python, il che è molto difficile. Uno script Bat in Windows o uno script Bash in una shell Bash può farlo con un normale comando cd perché la shell stessa è l'interprete. In Windows e Linux Python è un programma e nessun programma può cambiare direttamente l'ambiente del suo genitore. Tuttavia, la combinazione di un semplice script shell con uno script Python che esegue la maggior parte delle cose difficili può ottenere il risultato desiderato. Ad esempio, per eseguire un comando cd esteso con cronologia di attraversamento per la visualizzazione indietro / avanti / seleziona, ho scritto uno script Python relativamente complesso invocato da un semplice script bat. L'elenco di attraversamento è memorizzato in un file, con la directory di destinazione sulla prima riga. Quando lo script python ritorna, lo script bat legge la prima riga del file e lo rende l'argomento su cd. Lo script bat completo (meno commenti per brevità) è:

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

Lo script 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)

Sebbene sia una buona risposta, l'OP ha selezionato una risposta che dice che non si tratta di cambiare il CWD del processo genitore. Ciò chiarisce ogni possibile confusione sul significato della domanda.
Tin Man,

A Tin Man-- quella risposta è stata selezionata prima di pubblicare il mio suggerimento. Penso che le risposte ad ampio raggio possano essere state fonte di confusione. il cd all'interno di un determinato processo (cioè uno script Python) è così semplice che non so perché qualcuno lo chieda.
David McCracken,

1
In realtà quella risposta è stata selezionata anni fa. Se non fosse appropriato, sarebbe stato chiamato più volte da allora.
Tin Man,

Penso che rimanga confusione. Più di recente, la domanda "simulando il comando" cd "di Linux in Python e persistendo il cambio di directory dopo che il programma è uscito [duplicato]" è stata respinta come risposta qui ma, in realtà, questa domanda non viene risolta dalla risposta selezionata. Il mio suggerimento è per Windows ma i problemi sono gli stessi in Linux.
David McCracken,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.