Как мне изменить мой текущий каталог из скрипта Python? - PullRequest
3 голосов
/ 03 апреля 2010

Я пытаюсь реализовать свою собственную версию команды 'cd', которая предоставляет пользователю список жестко закодированных каталогов на выбор, и пользователь должен ввести число, соответствующее записи в списке. Программа, названная сейчас my_cd.py, должна затем эффективно «переместить» пользователя в выбранный каталог. Пример того, как это должно работать:

/some/directory
$ my_cd.py
1) ~
2) /bin/
3) /usr
Enter menu selection, or q to quit: 2

/bin
$

В настоящее время я пытаюсь "cd", используя os.chdir('dir'). Однако это не работает, вероятно, потому что my_cd.py запускается в своем собственном дочернем процессе. Я попытался обернуть вызов к my_cd.py в исходном скрипте bash с именем my_cd.sh:

#! /bin/bash
function my_cd() {
    /path/to/my_cd.py
}

/some/directory
$ . my_cd.sh
$ my_cd
... shows list of dirs, but doesn't 'cd' in the interactive shell

Любые идеи о том, как я могу заставить это работать? Можно ли изменить текущий каталог моей интерактивной оболочки из скрипта Python?

Ответы [ 5 ]

7 голосов
/ 03 апреля 2010

Измените ваш исходный код bash на:

#! /bin/bash
function my_cd() {
    cd `/path/to/my_cd.py`
}

и ваш код Python для вывода всех своих cosmetic (сообщений пользователям, меню и т. Д.) На sys.stderr, и, в конце, вместо os.chdir, просто print (на sys.stdout) путь, по которому следует изменить каталог.

3 голосов
/ 03 апреля 2010

my_cd.py:

#!/usr/bin/env python
import sys

dirs = ['/usr/bin', '/bin', '~']
for n, dir in enumerate(dirs):
    sys.stderr.write('%d) %s\n' % (n+1, dir))
sys.stderr.write('Choice: ')
n = int(raw_input())
print dirs[n-1]

Использование:

nosklo:/tmp$ alias mcd="cd \$(/path/to/my_cd.py)"
nosklo:/tmp$ mcd
1) /usr/bin
2) /bin
3) ~
Choice: 1
nosklo:/usr/bin$ 
2 голосов
/ 03 апреля 2010

Для чего оно стоит, поскольку этот вопрос также помечен как "bash", вот простое решение только для bash:

$ cat select_cd
#!/bin/bash

PS3="Number: "

dir_choices="/home/klittle /local_home/oracle"

select CHOICE in $dir_choices; do
   break
done

[[ "$CHOICE" != "" ]] &&  eval 'cd '$CHOICE

Теперь этот скрипт должен иметь исходный код, а не выполняться:

$ pwd
/home/klittle/bin
$ source select_cd
1) /home/klittle
2) /local_home/oracle
Number: 2
$ pwd
/local_home/oracle

Итак,

$ alias mycd='source /home/klittle/bin/select_cd'
$ mycd
1) /home/klittle
2) /local_home/oracle
Number:

Чтобы решить ваш случай, вы могли бы использовать команду, которую пользователь запускает, в качестве псевдонима, который создает сценарий bash, который сначала выбирает dir, а затем погружается в программу на python после того, как cd был выполнен.

2 голосов
/ 03 апреля 2010

Этого нельзя сделать. Изменения в рабочем каталоге не видны для родительских процессов. В лучшем случае вы можете сделать так, чтобы скрипт Python распечатывал каталог для изменения, а затем исходный скрипт фактически сменил этот каталог.

1 голос
/ 03 апреля 2010

Вопреки сказанному, вы можете сделать это, заменив образ процесса дважды.

В bash замените функцию my_cd на:

function my_cd() {
    exec /path/to/my_cd.py "$BASH" "$0"
}

Тогда ваш скрипт на python должен заканчиваться на:

os.execl(sys.argv[1], sys.argv[2])

Не забудьте import os, sys в начале сценария.

Но учтите, что это пограничный хак. Ваша оболочка умирает, заменяя себя скриптом python, и запускает в том же процессе . Скрипт python вносит изменения в среду и заменяет себя оболочкой, опять же, в том же процессе. Это означает, что если у вас есть другие локальные несохраненные и неэкспортированные данные или среда в предыдущем сеансе оболочки, они не сохранятся в новом сеансе. Это также означает, что сценарии rc и profile будут запущены снова (обычно это не проблема).

...