python: змініть робочий каталог скриптів у власний каталог сценарію


171

Я щохвилини запускаю оболонку пітона з crontab:

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

/home/udi/fooмає деякі необхідні підкаталоги, як-от /home/udi/foo/logі /home/udi/foo/config, на які /home/udi/foo/bar.pyпосилається.

Проблема полягає в тому, crontabщо сценарій запускається з іншого робочого каталогу, тому намагання відкрити ./log/bar.logне вдається.

Чи є приємний спосіб сказати сценарію, щоб змінити робочий каталог у власний каталог сценарію? Я б хотів, щоб рішення, яке працювало б для будь-якого місця скрипту, замість того, щоб явно сказати сценарію, де він є.

Редагувати:

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

Було найбільш компактним елегантним рішенням. Дякуємо за відповіді та пояснення!


не пов’язаний із crontabвипадком використання: і те, sys.argv[0]і __file__помилка, якщо сценарій запускається з використанням execfile(); Натомість може бути використаний inspectрозчин на основі .
jfs

Відповіді:


206

Це змінить ваш поточний робочий каталог так, щоб відкрити відносні шляхи:

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

Однак ви запитали, як змінити в будь-який каталог, де знаходиться ваш скрипт Python, навіть якщо ви не знаєте, який саме буде каталог під час написання сценарію. Для цього можна скористатися os.pathфункціями:

import os

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

Це приймає ім'я вашого сценарію, перетворює його в абсолютний шлях, потім витягує каталог цього шляху, а потім змінює в цей каталог.


3
Дорівнює жорсткому коду каталогу.
Іке

2
Якщо ви запускаєте його із симпосилання, це не працюватиме. Використовуйте __file__замість sys.argv[0].
Кріс Даун

1
Чому безтурботний крок? Чому б не просто os.chdir(os.path.dirname(__file__))?
Полковник Паніка

8
__file__виходить з ладу в «заморожених» програмах (створених за допомогою py2exe, PyInstaller, cx_Freeze). sys.argv[0]працює. @ChrisDown: якщо ви хочете переходити до посилань; os.path.realpath()може бути використаний.
jfs

3
@EliCourtwright Якщо __file__це вже не абсолютний шлях, а користувач змінив робочий каталог, то os.path.abspathвсе одно вийде з ладу.
Артур Такка

45

Ви можете отримати більш коротку версію за допомогою sys.path[0].

os.chdir(sys.path[0])

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

Як ініціалізовано при запуску програми, перший пункт цього списку path[0]- це каталог, що містить сценарій, який використовувався для виклику інтерпретатора Python


23

Не робіть цього.

Ваші сценарії та ваші дані не повинні переміщуватися в один великий каталог. Помістіть свій код в деякому відомому місці ( site-packagesабо /var/opt/udiчи що - то) окремо від ваших даних. Використовуйте хороший контроль версій свого коду, щоб переконатися, що у вас поточна та попередня версії відокремлені одна від одної, щоб ви могли перейти до попередніх версій та протестувати майбутні версії.

Підсумок: Не змішуйте код та дані.

Дані дорогоцінні. Код приходить і йде.

Надайте робочий каталог як значення аргументу командного рядка. Ви можете вказати за замовчуванням як змінну середовища. Не виводити його (чи здогадуватися)

Зробіть це необхідним значенням аргументу і зробіть це.

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

Не «припускайте» каталог, заснований на розташуванні вашого програмного забезпечення. У довгостроковій перспективі це не вийде.


9
Я думаю, ви маєте рацію щодо розділення коду та даних для великих програмних пакетів, але для невеликого сценарію технічного обслуговування це здається досить надуманим. Я повністю згоден щодо контролю версій.
Адам Матан

3
С. Лотт має рацію. Завжди тримайте дані та код відокремленими, якщо дані не є тимчасовими. Наприклад, якщо у вас є піктограми, це дані, але вони не є тимчасовими, і є сенс розглянути це стосовно пакета програмного забезпечення (все, що це означає)
Стефано Борині

5
@Udi Pasmon: Зовсім не надумано. Саме «невеликі сценарії обслуговування» вникають у організації глибоких проблем. Через роки цей "маленький сценарій технічного обслуговування", і це діти та файли виводу та даних, будуть кошмаром для роз'єднання та повторного втілення. Тримайте дані якомога далі від коду - передайте параметри для всього - не припускайте нічого.
S.Lott

1
+1 Я думав, що хочу зробити як ОП, але, прочитавши вашу пораду, замість цього я змінив свій сценарій. Тепер йому потрібен параметр, щоб вказати розташування файлу журналу.
Ієн Самуель Маклін Старший

+1. Простіше створити пакет (rpm) для сценарію python, якщо каталоги даних можна легко налаштувати.
jfs

18

Змініть команду crontab на

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

(...)Починає подоболочкі , що ваш crond виконується в якості однієї команди. || exit 1Викликає ваш cronjob до збою в разі, якщо каталог недоступний.

Хоча інші рішення в майбутньому можуть бути більш витонченими для ваших конкретних сценаріїв, мій приклад все-таки може бути корисним у випадках, коли ви не можете змінити програму чи команду, яку ви хочете виконати.


1
Це надзвичайно здорове рішення. Зазвичай я можу редагувати відповіді інших людей, щоб додати такі речі, як || exit 1. Це бадьоро бачить. Хоча мені доводиться дивуватися, чому ви не просто так зробитеcd /home/udi/foo/ && ./bar.py
Бруно Броноскі,

2
@BrunoBronosky З явним exit 1вашим кінцем буде повідомлено про помилку, і в більшості випадків надішле повідомлення електронною поштою про помилку.
Рууд Альтуайзен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.