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
디렉토리를 하드 코딩합니다.
— Ikke September

2
심볼릭 링크에서 실행하면 작동하지 않습니다. __file__대신에 사용하십시오 sys.argv[0].
— Chris Down

1
왜 공감 단계? 왜 간단하지 os.chdir(os.path.dirname(__file__))않습니까?
— 대령 패닉

8
__file__"frozen"프로그램에서 실패합니다 (py2exe, PyInstaller, cx_Freeze를 사용하여 생성). sys.argv[0]공장. @ChrisDown : 심볼릭 링크를 따르고 싶다면; os.path.realpath()사용 될수있다.
— jfs

3
@EliCourtwright __file__아직 절대 경로가 아니고 사용자가 작업 디렉토리를 변경 한 os.path.abspath경우 어쨌든 실패합니다.
— Arthur Tacca

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
큰 소프트웨어 패키지의 코드와 데이터를 분리하는 것이 옳다고 생각하지만 작은 유지 보수 스크립트에서는 상당히 많이 사용됩니다. 버전 관리에 전적으로 동의합니다.
— Adam Matan

3
로트가 맞습니다. 데이터가 일시적인 것이 아닌 한 항상 데이터와 코드를 분리하여 보관하십시오. 예를 들어, 아이콘이있는 경우, 그 데이터이지만, 그것은 일시적인하지, 그리고이 소프트웨어 번들 (그것이 의미하는 것은 무엇이나)에이 상대적으로 고려하는 것이 합리적
— 스테파노 Borini

5
@Udi Pasmon : 전혀 가져 오지 않았습니다. 조직이 심각한 문제에 빠지게하는 "소규모 유지 관리 스크립트"입니다. 몇 년이 지난 지금이 ​​"작은 유지 보수 스크립트"와 그 데이터는 어린이와 파생물 및 데이터 파일이 풀리고 다시 구현하는 데 악몽이 될 것입니다. 데이터를 가능한 한 코드와 멀리 유지하고 모든 매개 변수를 전달하십시오.
— S.Lott

1
+1 OP로하고 싶다고 생각했지만 조언을 읽은 후 대신 스크립트를 수정했습니다. 이제 로그 파일의 위치를 ​​지정하는 매개 변수가 필요합니다.
— Iain Samuel McLean 장로

+1. 데이터 디렉토리를 쉽게 사용자 정의 할 수 있으면 Python 스크립트 용 패키지 (rpm)를 작성하는 것이 더 쉽습니다.
— jfs

18

crontab 명령을 다음으로 변경하십시오.

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

는 (...)크론이 단일 명령으로 실행하는 하위 쉘을 시작합니다. 는 || exit 1디렉토리를 사용할 수없는 경우에 실패하여 cronjob에 발생합니다.

다른 스크립트는 특정 스크립트에 대해 장기적으로 더 우아 할 수 있지만 실행하려는 프로그램이나 명령을 수정할 수없는 경우에도 여전히 예제가 유용 할 수 있습니다.


1
이것은 매우 건전한 솔루션입니다. 나는 보통 다른 사람들의 답변을 편집하여 || exit 1. 이것을보고 상쾌합니다. 난 왜 당신이하지 않을 궁금 궁금해cd /home/udi/foo/ && ./bar.py
— 브루노 Bronosky

2
@BrunoBronosky 명시 적으로 exit 1오류에 대한 알림 메시지가 표시되며 대부분의 경우 실패에 대한 전자 메일 알림을 보냅니다.
— Ruud Althuizen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.