cd as in a shell command to change the working directory.
How do I change the current working directory in Python?
#1 building
This is an example of a context manager changing a working directory. It's more than anything else ActiveState version It's simple, but it can do the job.
Context manager: 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)
Or use ContextManager attempt More concise equivalent method (as follows) .
example
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.
#2 building
As others have pointed out, all of the above solutions only change the working directory of the current process. This will be lost when you return to the Unix shell. If you are desperate, you can change the parent shell directory on Unix in the following terrible ways:
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")
#3 building
Further entry directions are indicated by Brian and based on SH (1.0.8+)
from sh import cd, ls cd('/tmp') print ls()
#4 building
cd() is easy to write with generators and decorators.
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)
Then, even after an exception is thrown, the directory is restored:
os.chdir('/home') with cd('/tmp'): # ... raise Exception("There's no place like home.") # Directory is now back to '/home'.
#5 building
Changing the current directory of the script process is simple. I think the problem is actually how to change the current directory of the command window from which the python script is invoked, which is very difficult. A bat script in Windows or a Bash script in a Bash shell can do this using normal cd commands, because the shell itself is the interpreter. In Windows and Linux, Python is a program. No program can directly change its parent environment. However, using simple shell scripts in combination with Python scripts can accomplish most of the hard work and achieve the desired results. For example, in order to make an extended cd command with traversal history for backward / forward / selective re access, I wrote a relatively complex Python script called by a simple bat script. The traversal list is stored in the file with the destination directory on the first line. When the python script returns, the bat script reads the first line of the file and takes it as a parameter to cd. The complete bat script (minus comments for brevity) is:
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
The python script dSup.py is:
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)