2009-09-16 14 views
104

ho eseguire una shell Python da crontab ogni minuto:pitone: Modificare gli script directory di lavoro alla propria directory dello script

* * * * * /home/udi/foo/bar.py 

/home/udi/foo ha alcuni sottodirectory necessarie, come /home/udi/foo/log e /home/udi/foo/config, che si riferisce a /home/udi/foo/bar.py.

Il problema è che crontab esegue lo script da una diversa directory di lavoro, pertanto non è possibile provare a aprire ./log/bar.log.

C'è un modo carino per dire allo script di cambiare la directory di lavoro nella directory dello script? Mi piacerebbe una soluzione che possa funzionare per qualsiasi posizione di script, piuttosto che dire esplicitamente allo script dove si trova.

EDIT:

os.chdir(os.path.dirname(sys.argv[0])) 

era la soluzione più compatta elegante. Grazie per le vostre risposte e spiegazioni!

+0

non correlato al caso d'uso 'crontab': entrambi' sys.argv [0] 'e' __file__' falliscono se lo script viene eseguito usando 'execfile()'; [Si potrebbe usare invece la soluzione '' inspect'-based] (http://stackoverflow.com/a/22881871/4279). – jfs

risposta

129

Questo cambierà la vostra directory di lavoro corrente in modo che l'apertura di percorsi relativi funzionerà:

import os 
os.chdir("/home/udi/foo") 

Tuttavia, lei ha chiesto come cambiare in qualunque directory lo script Python si trova, anche se non si conosce quale directory sarà quando stai scrivendo il tuo script. Per fare questo, è possibile utilizzare le funzioni os.path:

import os 

abspath = os.path.abspath(__file__) 
dname = os.path.dirname(abspath) 
os.chdir(dname) 

Questa prende il nome del file dello script, lo converte in un percorso assoluto, poi estrae la directory di quel percorso, poi si trasforma in quella directory.

+1

Uguale a hardcoding della directory. – Ikke

+0

Ho aggiunto informazioni su come cambiare nella directory dello script; Ho postato una risposta parziale mentre cercavo le funzioni os.path, che non riuscivo a ricordare in cima alla mia testa. –

+2

Se si sta eseguendo da un collegamento simbolico, questo non funzionerà. Usa '__file__' invece di' sys.argv [0] '. –

15

Non farlo.

Gli script e i dati non devono essere ridotti in un'unica directory. Inserisci il tuo codice in una posizione nota (site-packages o /var/opt/udi o qualcosa) separato dai tuoi dati. Usa un buon controllo della versione sul tuo codice per assicurarti di avere le versioni correnti e precedenti separate l'una dall'altra in modo da poter tornare alle versioni precedenti e testare le versioni future.

Bottom line: non collegare codice e dati.

I dati sono preziosi. Il codice va e viene.

Fornire la directory di lavoro come valore di argomento della riga di comando. È possibile fornire un valore predefinito come variabile di ambiente. Non dedurlo (o indovinarlo)

Farlo un valore di argomento obbligatorio e farlo.

import sys 
import os 
working= os.environ.get("WORKING_DIRECTORY","/some/default") 
if len(sys.argv) > 1: working = sys.argv[1] 
os.chdir(working) 

Non "assumere" una directory in base alla posizione del software. Non funzionerà bene a lungo termine.

+8

Penso che abbiate ragione nel separare codice e dati per pacchetti software di grandi dimensioni, ma sembra piuttosto inverosimile per un piccolo script di manutenzione . Sono totalmente d'accordo sul controllo della versione. –

+2

S. Lott ha ragione. Tenere sempre separati i dati e il codice, a meno che i dati non siano transitori. Ad esempio, se si dispone di icone, si tratta di dati, ma non è transitorio e ha senso considerarlo in relazione al pacchetto software (qualunque cosa ciò significhi) –

+4

@Udi Pasmon: Non molto inverosimile. Sono i "piccoli script di manutenzione" che mettono in difficoltà le organizzazioni. A partire da ora, questo "piccolo script di manutenzione" ed i suoi figli e le sue derivazioni e file di dati saranno un incubo da districare e reimplementare. Mantieni i dati il ​​più lontano possibile dal codice - passa i parametri per tutto - non assumere nulla. –

26

È possibile ottenere una versione più corta utilizzando sys.path[0].

os.chdir(sys.path[0]) 

Da http://docs.python.org/library/sys.html#sys.path

Come inizializzato all'avvio del programma, il primo elemento di questa lista, path[0], è la directory contenente lo script che è stato utilizzato per invocare l'interprete Python

11

Modifica il comando crontab in

* * * * * (cd /home/udi/foo/ || exit 1; ./bar.py) 

(...) avvia una sotto shell che viene eseguita come un singolo comando. Lo || exit 1 causa il fallimento del cronjob nel caso in cui la directory non sia disponibile.

Anche se le altre soluzioni potrebbero essere più eleganti a lungo termine per gli script specifici, il mio esempio potrebbe comunque essere utile nei casi in cui non è possibile modificare il programma o il comando che si desidera eseguire.

+0

Questa è una soluzione estremamente valida. Di solito mi trovo a modificare le risposte di altre persone per aggiungere cose come '|| uscita 1'. È bello vedere questo. Anche se devo chiedermi perché non dovresti semplicemente fare cd/home/udi/foo/&&./Bar.py' –

+2

@BrunoBronosky Con l'esplicito 'exit 1' il tuo crond verrà avvisato di un errore, e in la maggior parte dei casi invierà un'e-mail di notifica dell'errore. –

Problemi correlati