2011-05-31 103 views
13

我正在使用crontab为我的Minecraft服务器运行维护脚本。大多数时候它工作正常,除非crontab尝试使用重新启动脚本。如果我手动运行重新启动脚本,则没有任何问题。因为我认为这与路径名有关,所以我试图确保它始终在minecraft目录中执行任何minecraft命令。所以我在pushd/popd中加入命令:通过os.system推送

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

下面是一个互动式会话,将Minecraft排除在等式之外。一个简单的“ls”测试。正如你所看到的,它根本不会从pushd目录运行os.system命令,而是从/ 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)在一个单独过程

+0

我有点糊涂了由去行对我来说,这看起来像是一个简单的例子:'os.system'生成一个子shell ...做'bash -c'pushd directory'','bash -c“popd”'会给你相同的结果.. 。为什么不使用'os.chdir'? – photoionized 2011-05-31 23:02:56

+1

nvm关于混淆线,它是你正在执行的'pushd'的输出,但分析仍然存在,你的命令不起作用,因为'os.system'产生了一个子shell。 – photoionized 2011-05-31 23:04:44

+0

...一旦子外壳完成,pushd/popd上下文变得毫无意义。 – macetw 2016-11-07 13:53:49

回答

9

每个外壳命令运行。它产生一个shell,执行pushd命令,然后shell退出。

只写在同一个shell脚本的命令:

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

一个更好的(也许)的方法是使用subprocess模块:

from subprocess import Popen 
Popen("run the commands", shell=True, cwd="/directory/path/here") 
+0

我刚试过第二种方法;我最近才开始使用子进程(这里的新程序员很清楚),我想更好地学习。它的工作原理,但我必须CTRL + C来获取python提示符。奇。 – 2011-05-31 23:06:43

+0

communicate()调用Popen后 – kocodude 2014-01-26 13:54:26

4

我不认为你可以调用pushdos.system()内电话:

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

M aybe只是也许你的系统实际上提供了一个pushd二进制触发壳内部函数( 我想我 FreeBSD has some tricks like this, but not for pushd以前见过这在FreeBSD),而是一个过程的当前工作目录不能被其他进程的影响 - 所以你的第一个system()启动一个shell,运行一个假想的pushd,启动一个shell,运行ls,启动一个shell,运行一个假想的popd ......没有一个互相影响。

可以使用os.chdir("/home/path/")改变路径:http://docs.python.org/library/os.html#os-file-dir

+0

chdir确实有效。谢谢! – 2011-05-31 23:08:22

4

无需使用pushd - 只需使用os.chdir

>>> import os 
>>> os.getcwd() 
'/Users/me' 
>>> os.chdir('..') 
>>> os.getcwd() 
'/Users' 
>>> os.chdir('me') 
>>> os.getcwd() 
'/Users/me' 
+0

是的。这正是我想要的。谢谢! – 2011-05-31 23:10:17

+0

这只适用于以前的目录是其他目录的直接父目录。 – 2018-03-05 23:46:38

+0

@DaveKennedy,真的。我想你也可以说接受的答案只适用于目录为“/ directory/path/here'',不是吗? – senderle 2018-03-06 21:26:51

3

pushdpopd有一些附加的功能:他们以前存储的工作目录在一个堆栈 - 换句话说,你可以五次,做一些东西,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()) 
55

在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在嵌套块,而不必依靠全局目录栈:“〜/的Minecraft的/ etc”

with pushd('somewhere'): 
    # do something 
    with pushd('another/place'): 
     # do something else 
    # do something back in "somewhere" 
+2

我喜欢这个想法,这是更优雅和pythonic :) – 2013-05-18 03:36:42

+4

那是我一直在寻找。但是,如果在with语句中引发异常,则此代码不会弹出,因为@contextmanager不处理异常https://docs.python.org/2/library/contextlib.html您需要用yield尝试...终于 – 2015-11-02 03:45:47

+1

@MaximeViargues还有contextlib'closing'方法,它会照顾你的最后需求... https://docs.python.org/2/library/contextlib.html#contextlib.closing – mogga 2016-01-11 18:29:12