199 votes

python : Changer le répertoire de travail scripts en répertoire propre au scripts.

Je lance un shell python depuis la crontab toutes les minutes :

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

/home/udi/foo a quelques sous-répertoires nécessaires, comme /home/udi/foo/log y /home/udi/foo/config qui /home/udi/foo/bar.py se réfère à.

Le problème est que crontab exécute le script à partir d'un répertoire de travail différent, donc essayer d'ouvrir ./log/bar.log échoue.

Existe-t-il un moyen agréable d'indiquer au script de changer le répertoire de travail pour le propre répertoire du script ? J'aurais envie d'une solution qui fonctionnerait pour n'importe quel emplacement du script, plutôt que de dire explicitement au script où il se trouve.

EDIT :

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

C'était la solution la plus compacte et la plus élégante. Merci pour vos réponses et explications !

0 votes

Sans rapport avec crontab cas d'utilisation : les deux sys.argv[0] y __file__ échoue si script est exécuté en utilisant execfile() ; inspect -solution basée sur la technologie pourrait être utilisé à la place.

240voto

Eli Courtwright Points 53071

Cela changera votre répertoire de travail actuel de sorte que l'ouverture des chemins relatifs fonctionnera :

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

Cependant, vous avez demandé comment passer dans le répertoire où se trouve votre script Python, même si vous ne savez pas quel répertoire ce sera lorsque vous écrirez votre script. Pour ce faire, vous pouvez utiliser la fonction os.path fonctions :

import os

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

Ceci prend le nom de fichier de votre script, le convertit en un chemin absolu, puis extrait le répertoire de ce chemin, puis change dans ce répertoire.

3 votes

Équivaut à coder en dur le répertoire.

0 votes

J'ai ajouté des informations sur la façon d'entrer dans le répertoire du script ; j'ai posté une réponse partielle pendant que je cherchais les fonctions os.path, dont je ne me souvenais plus.

2 votes

Si vous l'exécutez à partir d'un lien symbolique, cela ne fonctionnera pas. Utilisez __file__ au lieu de sys.argv[0] .

51voto

Xv. Points 879

Vous pouvez obtenir une version plus courte en utilisant sys.path[0] .

os.chdir(sys.path[0])

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

Tel qu'il est initialisé au démarrage du programme, le premier élément de cette liste, path[0] est le répertoire contenant le script utilisé pour invoquer l'interprète Python. invoquer l'interpréteur Python

0 votes

Sachez que cela ne fonctionne pas si vous import git . Le premier chemin pointera vers <pythondir>/lib/site-packages/git/ext/gitdb. En tout cas, cela m'arrive

0 votes

Merci, @marco. Je viens de créer un PR pour cela github.com/gitpython-developers/GitPython/pull/1068

26voto

S.Lott Points 207588

Ne faites pas ça.

Vos scripts et vos données ne doivent pas être mélangés dans un seul grand répertoire. Mettez votre code dans un endroit connu ( site-packages o /var/opt/udi ou autre) séparée de vos données. Utilisez un bon contrôle des versions de votre code pour vous assurer que les versions actuelles et précédentes sont séparées les unes des autres, afin de pouvoir revenir aux versions précédentes et tester les versions futures.

La ligne du bas : Ne mélangez pas le code et les données.

Les données sont précieuses. Le code va et vient.

Fournir le répertoire de travail comme valeur d'argument de ligne de commande. Vous pouvez fournir une valeur par défaut en tant que variable d'environnement. Ne le déduisez pas (ou ne le devinez pas).

Faites-en une valeur d'argument obligatoire et faites ceci.

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

Ne "supposez" pas un répertoire en fonction de l'emplacement de votre logiciel. Cela ne fonctionnera pas bien à long terme.

12 votes

Je pense que vous avez raison de séparer le code et les données pour les grands logiciels, mais cela semble assez farfelu pour un petit script de maintenance. Je suis tout à fait d'accord sur le contrôle de version.

3 votes

S. Lott a raison. Il faut toujours séparer les données et le code, sauf si les données ne sont pas éphémères. Par exemple, si vous avez des icônes, ce sont des données, mais elles ne sont pas transitoires, et il est logique de les considérer par rapport à l'ensemble du logiciel (quoi que cela signifie).

6 votes

Udi Pasmon : Pas du tout tiré par les cheveux. Ce sont les "petites maintenances scripts" qui mettent les organisations dans de gros problèmes. Dans quelques années, cette "petite maintenance scripts" et ses enfants, ses dérivations et ses fichiers de données seront un cauchemar à démêler et à réimplémenter. Gardez les données aussi loin que possible du code -- passez des paramètres pour tout -- ne supposez rien.

20voto

Ruud Althuizen Points 81

Changez votre commande crontab en

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

Le site (...) démarre un sous-shell que votre crond exécute comme une seule commande. Le site || exit 1 fait échouer votre cronjob dans le cas où le répertoire n'est pas disponible.

Bien que les autres solutions puissent être plus élégantes à long terme pour vos scripts spécifiques, mon exemple pourrait toujours être utile dans les cas où vous ne pouvez pas modifier le programme ou la commande que vous voulez exécuter.

2 votes

Il s'agit d'une solution extrêmement saine. J'ai l'habitude de modifier les réponses des autres pour y ajouter des éléments comme l'élément || exit 1 . C'est rafraîchissant de voir ça. Bien que je doive me demander pourquoi vous ne feriez pas simplement cd /home/udi/foo/ && ./bar.py

3 votes

@BrunoBronosky Avec l'explicite exit 1 votre crond sera informé d'une erreur et, dans la plupart des cas, enverra une notification par courriel de l'échec.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X