2011-05-31 17 views
13

Estoy usando un crontab para ejecutar un script de mantenimiento para mi servidor de Minecraft. La mayoría de las veces funciona bien, a menos que crontab intente utilizar el script de reinicio. Si ejecuto el script de reinicio manualmente, no hay ningún problema. Como creo que tiene que ver con los nombres de las rutas, estoy intentando asegurarme de que siempre está haciendo un comando de Minecraft DESDE el directorio de Minecraft. Así que estoy encierra el mando en pushd/popd:pushd through os.system

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

A continuación se muestra una sesión interactiva teniendo Minecraft fuera de la ecuación. Una simple prueba "ls". Como puede ver, no ejecuta en absoluto el comando os.system desde el directorio pushd, sino desde/etc/que es el directorio en el que estaba ejecutando python para ilustrar mi punto. Claramente pushd no funciona a través de python , entonces me pregunto cómo más puedo lograr esto. ¡Gracias!

>>> 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 

=== (servidor CentOS con el pitón 2.4)

+0

estoy un poco confundido por la línea que va Para mí, parece un simple caso del hecho de que 'os.system' genera una subshell ... haciendo' bash -c "pushd directory" ',' bash -c "popd" 'le dará el mismo resultado. ¿Por qué no usar 'os.chdir'? – photoionized

+1

nvm acerca de la línea de confusión, es la salida de su 'pushd' que se está ejecutando, pero el análisis sigue en pie, sus comandos no funcionan porque' os.system' genera una subshell. – photoionized

+0

... y una vez que se completa la subshell, el contexto pushd/popd se vuelve sin sentido. – macetw

Respuesta

9

cada cáscara comando se ejecuta en un proceso independiente. Genera un shell, ejecuta el comando pushd y luego sale el shell.

Sólo tiene que escribir los comandos en la misma secuencia de comandos shell:

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

manera más elegante (quizás) es con el módulo de subprocess:

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

Acabo de probar el segundo método; Recientemente comencé a usar el subproceso (nuevo programador aquí, claramente) y me gustaría aprenderlo mejor. Funciona, pero luego tengo que presionar CTRL + C para recuperar el prompt de Python. Impar. –

+0

.communicate() después de llamar a Popen – kocodude

4

No creo que se puede llamar desde pushd dentro de una llamada os.system():

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

M al vez sólo tal vez su sistema realmente proporciona un binario pushd que desencadena una función interna de la cáscara ( Creo que he visto esto antes en FreeBSD FreeBSD has some tricks like this, but not for pushd), pero el directorio de trabajo actual de un proceso no puede ser influenciado por otros procesos - entonces su primer system() inicia un shell, ejecuta un hipotético pushd, inicia un shell, ejecuta ls, inicia un shell, ejecuta un hipotético popd ... ninguno de los cuales se influencian entre sí.

Usted puede uso os.chdir("/home/path/") para cambiar de ruta: http://docs.python.org/library/os.html#os-file-dir

+0

chdir funciona. ¡Gracias! –

4

No hay necesidad de utilizar pushd - sólo tiene que utilizar os.chdir:

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

Sí. Esto hace exactamente lo que quiero. ¡Gracias! –

+0

Esto solo funciona si el directorio anterior es el primario directo del otro. –

+0

@DaveKennedy, cierto. Supongo que también podría decir que la respuesta aceptada solo funciona si el directorio es ''/ directory/path/here'', ¿o no? – senderle

3

pushd y popd tienen algunas funciones adicionales: almacenan los directorios de trabajo anteriores en una pila; en otras palabras, puede pushd cinco veces, hacer algunas cosas, y popd cinco veces para terminar donde comenzó. No está usando eso aquí, pero podría ser útil para otros que buscan las preguntas como esta. Así es como puedes emularlo:

# 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

En Python 2.5 y después, creo un método mejor sería utilizar un gestor de contexto, así:

import contextlib 
import os 


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

entonces Se puede utilizar como la siguiente:

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

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

Mediante el uso de un gestor de contexto que será excepción y valor de retorno seguro: su código siempre volverá al lugar desde donde comenzó, incluso si lanza una excepción o regresa desde el interior del bloque de contexto.

también puede desviar llamadas nido pushd en bloques anidados, sin tener que depender de una pila de directorio global: "~/Minecraft/etc"

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

Me gusta la idea, eso es mucho más elegante y pitónico :) –

+4

Eso era lo que estaba buscando. Sin embargo, este código no aparece si se genera una excepción en la sentencia with, porque @contextmanager no maneja las excepciones https://docs.python.org/2/library/contextlib.html. Debe rodear el rendimiento con un intente ... finalmente –

+1

@MaximeViargues también está el método contextlib 'closing' que se ocupa del intento ... por fin, el requisito para usted https://docs.python.org/2/library/contextlib.html#contextlib.closing – mogga