протолкнуть через os.system - PullRequest
17 голосов
/ 01 июня 2011

Я использую crontab для запуска сценария обслуживания для моего сервера minecraft. В большинстве случаев он работает нормально, если только crontab не пытается использовать скрипт перезапуска. Если я запускаю скрипт перезапуска вручную, никаких проблем не возникает. Поскольку я полагаю, что это связано с путевыми именами, я пытаюсь убедиться, что он всегда выполняет любую команду minecraft ИЗ директории minecraft. Поэтому я инкапсулирую команду в pushd / popd:

os.system("pushd /directory/path/here")
os.system("command to sent to minecraft")
os.system("popd")

Ниже приведен интерактивный сеанс, который выводит Minecraft из уравнения. Простой тест "ls". Как видите, он вообще не запускает команду os.system из каталога pushd, а вместо этого из / etc /, которая является каталогом, в котором я запускал python, чтобы проиллюстрировать свою точку зрения. Очевидно, что pushd не работает через python Вот и мне интересно, как еще я могу этого добиться. Спасибо!

>>> def test():
...     import os
...     os.system("pushd /home/[path_goes_here]/minecraft")
...     os.system("ls")
...     os.system("popd")
... 
>>> test()
~/minecraft /etc
DIR_COLORS    cron.weekly  gcrypt         inputrc    localtime   mime.types         ntp       ppp         rc3.d       sasldb2         smrsh      vsftpd.ftpusers
DIR_COLORS.xterm  crontab      gpm-root.conf      iproute2   login.defs  mke2fs.conf            ntp.conf      printcap        rc4.d       screenrc        snmp       vsftpd.tpsave
X11       csh.cshrc    group          issue      logrotate.conf  modprobe.d         odbc.ini      profile         rc5.d       scsi_id.config  squirrelmail   vz
adjtime       csh.login    group-         issue.net  logrotate.d     motd               odbcinst.ini  profile.d       rc6.d       securetty       ssh        warnquota.conf
aliases       cyrus.conf   host.conf      java       lvm         mtab               openldap      protocols       redhat-release  security        stunnel        webalizer.conf
alsa          dbus-1       hosts          jvm        lynx-site.cfg   multipath.conf         opt       quotagrpadmins  resolv.conf     selinux         sudoers        wgetrc
alternatives      default      hosts.allow    jvm-commmon    lynx.cfg    my.cnf             pam.d         quotatab        rndc.key        sensors.conf    sysconfig      xinetd.conf
bashrc        depmod.d     hosts.deny     jwhois.conf    mail        named.caching-nameserver.conf  passwd        rc          rpc         services        sysctl.conf    xinetd.d
blkid         dev.d        httpd          krb5.conf  mail.rc     named.conf         passwd-       rc.d        rpm         sestatus.conf   termcap        yum
cron.d        environment  imapd.conf     ld.so.cache    mailcap     named.rfc1912.zones        pear.conf     rc.local        rsyslog.conf    setuptool.d     udev       yum.conf
cron.daily    exports      imapd.conf.tpsave  ld.so.conf     mailman     netplug            php.d         rc.sysinit      rwtab       shadow          updatedb.conf  yum.repos.d
cron.deny     filesystems  init.d         ld.so.conf.d   makedev.d   netplug.d          php.ini       rc0.d       rwtab.d         shadow-         vimrc
cron.hourly   fonts        initlog.conf   libaudit.conf  man.config  nscd.conf          pki       rc1.d       samba       shells          virc
cron.monthly      fstab        inittab        libuser.conf   maven       nsswitch.conf          postfix       rc2.d       sasl2       skel        vsftpd
sh: line 0: popd: directory stack empty

=== (Сервер CentOS с python 2.4)

Ответы [ 6 ]

72 голосов
/ 13 декабря 2012

В Python 2.5 и более поздних версиях я думаю, что лучшим способом будет использование диспетчера контекста, например:

import contextlib
import os


@contextlib.contextmanager
def pushd(new_dir):
    previous_dir = os.getcwd()
    os.chdir(new_dir)
    yield
    os.chdir(previous_dir)

Затем вы можете использовать его следующим образом:

with pushd('somewhere'):
    print os.getcwd() # "somewhere"

print os.getcwd() # "wherever you started"

Используя менеджер контекста, вы будете в безопасности исключений и возвращаемых значений: ваш код всегда вернется к исходному месту, даже если вы выбросите исключение или вернетесь из блока контекста.

Вы также можете вкладывать вызовы pushd во вложенные блоки, не полагаясь на стек общих каталогов:

with pushd('somewhere'):
    # do something
    with pushd('another/place'):
        # do something else
    # do something back in "somewhere"
12 голосов
/ 01 июня 2011

Каждая команда оболочки выполняется в отдельном процессе.Он порождает оболочку, выполняет команду pushd, а затем оболочка закрывается.

Просто напишите команды в том же сценарии оболочки:

os.system("cd /directory/path/here; run the commands")

Более хороший (возможно) способ заключается вsubprocess модуль:

from subprocess import Popen
Popen("run the commands", shell=True, cwd="/directory/path/here")
4 голосов
/ 10 мая 2012

pushd и popd имеют некоторые дополнительные функциональные возможности: они хранят предыдущие рабочие каталоги в стеке - другими словами, вы можете pushd пять раз, делать что-то и popd пять раз, чтобы оказаться там, гдеты начал.Вы не используете это здесь, но это может быть полезно для других, ищущих подобные вопросы.Вот как вы можете эмулировать это:

# initialise a directory stack
pushstack = list()

def pushdir(dirname):
  global pushstack
  pushstack.append(os.getcwd())
  os.chdir(dirname)

def popdir():
  global pushstack
  os.chdir(pushstack.pop())
4 голосов
/ 01 июня 2011

Я не думаю, что вы можете позвонить pushd из os.system() звонка:

>>> import os
>>> ret = os.system("pushd /tmp")
sh: pushd: not found

Может быть, просто ваша система на самом деле предоставляет двоичный файл pushd, который запускает внутреннюю функцию оболочки ( Я думаю, что я видел это во FreeBSD раньше Во FreeBSD есть некоторые приемы, подобные этому, но не для pushd), но текущий рабочий каталог процесса процесса не может зависеть от других процессов - поэтому ваш первый system() запускает оболочку, запускает гипотетический pushd, запускает shell, запускает ls, запускает shell, запускает гипотетический popd ... ни один из которых не влияет друг на друга.

Вы можете использовать os.chdir("/home/path/") для изменения пути: http://docs.python.org/library/os.html#os-file-dir

3 голосов
/ 01 июня 2011

Не нужно использовать pushd - просто используйте os.chdir:

>>> import os
>>> os.getcwd()
'/Users/me'
>>> os.chdir('..')
>>> os.getcwd()
'/Users'
>>> os.chdir('me')
>>> os.getcwd()
'/Users/me'
0 голосов
/ 21 декабря 2018

Или создайте класс для использования с 'with'

import os

class pushd: # pylint: disable=invalid-name
    __slots__ = ('_pushstack',)

    def __init__(self, dirname):
        self._pushstack = list()
        self.pushd(dirname)

    def __enter__(self):
        return self

    def __exit__(self, exec_type, exec_val, exc_tb) -> bool:
        # skip all the intermediate directories, just go back to the original one.
        if self._pushstack:
            os.chdir(self._pushstack.pop(0)))
        if exec_type:
            return False
        return True

    def popd(self) -> None:
        if len(self._pushstack):
            os.chdir(self._pushstack.pop())

    def pushd(self, dirname) -> None:
        self._pushstack.append(os.getcwd())
        os.chdir(dirname)


    with pushd(dirname) as d:
        ... do stuff in that dirname
        d.pushd("../..")
        d.popd()
...