python: cambia la directory di lavoro degli script nella propria directory dello script


171

Eseguo una shell Python da crontab ogni minuto:

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

/home/udi/fooha alcune sottodirectory necessarie, come /home/udi/foo/loge /home/udi/foo/config, a cui /home/udi/foo/bar.pyfa riferimento.

Il problema è che crontabesegue lo script da una directory di lavoro diversa, quindi il tentativo di apertura ha ./log/bar.logesito negativo.

C'è un bel modo per dire allo script di cambiare la directory di lavoro nella propria directory dello script? Vorrei una soluzione che funzionasse per qualsiasi posizione dello script, piuttosto che dire esplicitamente allo script dove si trova.

MODIFICARE:

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

Era la soluzione elegante più compatta. Grazie per le tue risposte e spiegazioni!


non correlato al crontabcaso d'uso: entrambi sys.argv[0]e __file__falliscono se lo script viene eseguito usando execfile(); inspectal suo posto potrebbe essere utilizzata una soluzione basata su .
jfs,

Risposte:


206

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

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

Tuttavia, hai chiesto come cambiare in qualunque directory si trova il tuo script Python, anche se non sai quale directory sarà quando stai scrivendo il tuo script. Per fare ciò, è possibile utilizzare le os.pathfunzioni:

import os

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

Questo prende il nome del tuo script, lo converte in un percorso assoluto, quindi estrae la directory di quel percorso, quindi cambia in quella directory.


3
È uguale all'hardcoding della directory.
Ikke,

2
Se lo stai eseguendo da un collegamento simbolico, questo non funzionerà. Usa __file__invece di sys.argv[0].
Chris Down,

1
Perché il passo abspath? Perché non semplicemente os.chdir(os.path.dirname(__file__))?
Colonnello Panic,

8
__file__fallisce nei programmi "congelati" (creati usando py2exe, PyInstaller, cx_Freeze). sys.argv[0]lavori. @ChrisDown: se vuoi seguire i symlink; os.path.realpath()potrebbe essere usato.
jfs,

3
@EliCourtwright Se __file__non è già un percorso assoluto e l'utente ha cambiato la directory di lavoro, allora os.path.abspathfallirà comunque.
Arthur Tacca,

45

Puoi ottenere una versione più breve usando 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 questo elenco path[0], è la directory contenente lo script che è stato utilizzato per richiamare l'interprete Python


23

Non farlo

Gli script e i dati non devono essere uniti in un'unica grande directory. Inserire il codice in qualche posizione nota ( site-packageso /var/opt/udio qualcosa) separare dai dati. Usa un buon controllo della versione sul tuo codice per assicurarti di avere le versioni attuali e precedenti separate l'una dall'altra in modo da poter ricorrere alle versioni precedenti e testare le versioni future.

Bottom line: non mescolare codice e dati.

I dati sono preziosi. Il codice va e viene.

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

Renderlo un valore argomento richiesto 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.


9
Penso che tu abbia ragione a 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.
Adam Matan,

3
S. Lott ha ragione. Mantieni sempre dati e codice separati, a meno che i dati non siano transitori. Ad esempio, se si dispone di icone, ovvero dati, ma non sono transitori, e ha senso considerarli relativi al pacchetto software (qualunque cosa significhi)
Stefano Borini,

5
@Udi Pasmon: non inverosimile. Sono i "piccoli script di manutenzione" che mettono in grave difficoltà le organizzazioni. A distanza di anni, questo "piccolo script di manutenzione" ed i suoi figli, derivazioni e file di dati saranno un incubo da districare e reimplementare. Mantenere i dati il ​​più lontano possibile dal codice - passare parametri per tutto - non assumere nulla.
S.Lott

1
+1 Pensavo di voler fare come OP, ma dopo aver letto i tuoi consigli ho invece modificato la mia sceneggiatura. Ora richiede un parametro per specificare il percorso di un file di registro.
Iain Samuel McLean Elder,

+1. È più semplice creare un pacchetto (rpm) per uno script Python se le directory dei dati possono essere personalizzate facilmente.
jfs,

18

Cambia il tuo comando crontab in

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

Il (...)inizia un sub-shell che esegue i vostri crond come un singolo comando. La || exit 1causa tua cronjob a fallire nel caso che la directory non è disponibile.

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


1
Questa è una soluzione estremamente valida. Di solito mi ritrovo a modificare le risposte di altre persone per aggiungere cose come || exit 1. È rinfrescante vederlo. Anche se devo chiedermi perché non dovresti farlocd /home/udi/foo/ && ./bar.py
Bruno Bronosky il

2
@BrunoBronosky Con l'esplicito, la exit 1tua crond riceverà una notifica di errore e nella maggior parte dei casi invierà un'email di notifica dell'errore.
Ruud Althuizen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.