Darek
Darek

Reputation: 2921

Change working directory in shell with a python script

I want to implement a userland command that will take one of its arguments (path) and change the directory to that dir. After the program completion I would like the shell to be in that directory. So I want to implement cd command, but with external program.

Can it be done in a python script or I have to write bash wrapper?

Example:

tdi@bayes:/home/$>python cd.py tdi
tdi@bayes:/home/tdi$>

Upvotes: 18

Views: 9712

Answers (7)

JohnA
JohnA

Reputation: 849

Tried a bunch of the answers, none worked in Ubuntu 22.04
The termios.TIOCSTI does not work anymore or it may require sudo.

Eventually just opened another terminal in the new directory all invoked from my python script:

next_proj = '/home/myuserid/full/path/to/dir'
print(f'directory: {next_proj}')
os.system(f'gnome-terminal --tab '
          f'-- bash -c \'cd {next_proj}; exec $SHELL\'')

Not exactly what the question is looking for but may help find a better answer. All that is needed is a way to close the previous terminal (the one the script is running in.)

Upvotes: 0

David Härer
David Härer

Reputation: 181

As explained by mrdiskodave in Equivalent of shell 'cd' command to change the working directory? there is a hack to achieve the desired behavior in pure Python. I made some modifications to the answer from mrdiskodave to make it work in Python 3:

  • The pipes.quote() function has moved to shlex.quote().
  • To mitigate the issue of user input during execution, you can delete any previous user input with the backspace character "\x08".

So my adaption looks like the following:

import fcntl
import shlex
import termios
from pathlib import Path

def change_directory(path: Path):
    quoted_path = shlex.quote(str(path))

    # Remove up to 32 characters entered by the user.
    backspace = "\x08" * 32

    cmd = f"{backspace}cd {quoted_path}\n"
    for c in cmd:
        fcntl.ioctl(1, termios.TIOCSTI, c)

Upvotes: 0

gspr
gspr

Reputation: 11227

As codaddict writes, what happens in your sub-shell does not affect the parent shell. However, if your goal is to present the user with a shell in a different directory, you could always have Python use os.chdir to change the sub-shell's working directory and then launch a new shell from Python. This will not change the working directory of the original shell, but will leave the user with one in a different directory.

Upvotes: 2

bstpierre
bstpierre

Reputation: 31256

Others have pointed out that you can't change the working directory of a parent from a child.

But there is a way you can achieve your goal -- if you cd from a shell function, it can change the working dir. Add this to your ~/.bashrc:

go() {
    cd "$(python /path/to/cd.py "$1")"
}

Your script should print the path to the directory that you want to change to. For example, this could be your cd.py:

#!/usr/bin/python
import sys, os.path
if sys.argv[1] == 'tdi': print(os.path.expanduser('~/long/tedious/path/to/tdi'))
elif sys.argv[1] == 'xyz':  print(os.path.expanduser('~/long/tedious/path/to/xyz'))

Then you can do:

tdi@bayes:/home/$> go tdi
tdi@bayes:/home/tdi$> go tdi

Upvotes: 23

Robert Armstrong
Robert Armstrong

Reputation: 99

I shall try to show how to set a Bash terminal's working directory to whatever path a Python program wants in a fairly easy way.

Only Bash can set its working directory, so routines are needed for Python and Bash. The Python program has a routine defined as:

fob=open(somefile,"w")
fob.write(dd)
fob.close()

"Somefile" could for convenience be a RAM disk file. Bash "mount" would show tmpfs mounted somewhere like "/run/user/1000", so somefile might be "/run/user/1000/pythonwkdir". "dd" is the full directory path name desired.

The Bash file would look like:

#!/bin/bash
#pysync ---Command ". pysync" will set bash dir to what Python recorded
cd `cat /run/user/1000/pythonwkdr`

Upvotes: -5

anilmwr
anilmwr

Reputation: 477

cd is exclusively(?) implemented as a shell internal command, because any external program cannot change parent shell's CWD.

Upvotes: 1

codaddict
codaddict

Reputation: 455410

That is not going to be possible.

Your script runs in a sub-shell spawned by the parent shell where the command was issued.

Any cding done in the sub-shell does not affect the parent shell.

Upvotes: 5

Related Questions