2011-05-31 88 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