通过os.system使用pushd
我在用定时任务(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 个回答
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())
在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"
每个命令在一个独立的进程中运行。它会启动一个新的命令行窗口,执行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")