18

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$>
Darek
  • 2,861
  • 4
  • 29
  • 52

6 Answers6

23

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
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
bstpierre
  • 30,042
  • 15
  • 70
  • 103
  • 1
    I wrote a script that prints "cd {directory}", which I invoke with `\`./change_dir\`` (ie, run the script but surround it with backticks) That makes the interpreter execute whatever the program prints to stdout. – Shadow Nov 23 '15 at 23:58
  • @Shadow, that practice is buggy -- it has all the caveats discussed in [BashFAQ #50: I'm trying to put a command in a variable, but complex cases always fail!](http://mywiki.wooledge.org/BashFAQ/050). In particular, the result of a command substitution skips all the parsing steps other than string-splitting and glob expansion -- quotes aren't honored so there's no way to `cd` to a directory name with spaces, f/e. – Charles Duffy Oct 09 '20 at 16:15
  • 1
    BTW, the original code here didn't work, because tilde expansion doesn't happen to command substitution results. You can't have your Python code write a literal `~` in its output -- it'll get passed to `cd` without being replaced with an actual directory name; hence, the edit to use `os.path.expanduser`. – Charles Duffy Oct 09 '20 at 16:18
5

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.

codaddict
  • 445,704
  • 82
  • 492
  • 529
2

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.

gspr
  • 11,144
  • 3
  • 41
  • 74
  • 2
    This answer definitely deserves more attention. – Brōtsyorfuzthrāx Oct 02 '21 at 05:39
  • @gspr Can you expand on this approach with code? Is it possible "exec bash" the shell which called the python script? – user2514157 Aug 10 '22 at 15:31
  • @user2514157: I don't understand the last sentence. Could you rephrase what you're trying to do? – gspr Aug 11 '22 at 07:46
  • I want to run a python script from my bash shell which changes the current working directory of the shell (e.g., changes the terminal prompt to the location of a file specified in the script). It is not possible to change the parent shell from a child, however, running ```exec bash``` in the terminal replaces the existing parent shell with a new shell that has the desired settings. I thought you were implying a way to accomplish this from a python script. In other words, can a python script change the settings of the shell that called the script? – user2514157 Aug 12 '22 at 17:16
1

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

anilmwr
  • 477
  • 9
  • 16
  • Some Unix varieties have supported an external `cd` command - it is fairly useless for exactly this reason, but some edge-case uses are discussed at https://unix.stackexchange.com/questions/50058/what-is-the-point-of-the-cd-external-command – alani Oct 09 '20 at 16:22
0

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)

David Heresy
  • 181
  • 1
  • 4
-5

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`
Ajean
  • 5,528
  • 14
  • 46
  • 69
  • I have fixed your formatting, but seriously, you really must not have tried very hard, it was a completely unintelligible mess. – Ajean Jun 16 '17 at 20:45
  • Ajean, Thank you. You are a good sport. I hope the post will be of some help to a few. But no, i did not try very hard. I dislike editors that know what you want better than you do. I wrote a reasonably well-indented post, but the editor kept mangling it because I was not hitting some key or other. – Robert Armstrong Jun 18 '17 at 00:02