通过os.system使用pushd

26 投票
7 回答
33307 浏览
提问于 2025-04-16 18:43

我在用定时任务(crontab)来运行一个维护脚本,专门为我的Minecraft服务器服务。大部分时候这个脚本运行得很好,但如果定时任务尝试使用重启脚本,就会出问题。如果我手动运行重启脚本,就没有任何问题。我觉得这可能和路径有关,所以我想确保每次执行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)

7 个回答

8

pushdpopd 有一些额外的功能:它们可以把之前的工作目录存储在一个堆栈里。简单来说,你可以用 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())
101

在Python 2.5及以后的版本中,我觉得有一种更好的方法,就是使用上下文管理器,像这样:

import contextlib
import os


@contextlib.contextmanager
def pushd(new_dir):
    previous_dir = os.getcwd()
    os.chdir(new_dir)
    try:
        yield
    finally:
        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"
17

每个命令在一个独立的进程中运行。它会启动一个新的命令行窗口,执行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")

撰写回答