python:スクリプトの作業ディレクトリをスクリプト自身のディレクトリに変更します


171

私は毎分crontabからPythonシェルを実行しています:

* * * * * /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 2014

回答:


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

2
シンボリックリンクから実行している場合、これは機能しません。の__file__代わりに使用しますsys.argv[0]
クリスダウン

1
なぜabspathステップ?なぜ単純ではないのですos.chdir(os.path.dirname(__file__))か?
大佐パニック

8
__file__「冷凍」プログラム(py2exe、PyInstaller、cx_Freezeを使用して作成)で失敗します。sys.argv[0]動作します。@ChrisDown:シンボリックリンクを追跡したい場合。os.path.realpath()使用することができます。
jfs 2014

3
@EliCourtwright __file__がまだ絶対パスでなく、ユーザーが作業ディレクトリを変更したos.path.abspath場合、とにかく失敗します。
Arthur Tacca 2017年

45

を使用すると、より短いバージョンを取得できますsys.path[0]

os.chdir(sys.path[0])

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

プログラムの起動時に初期化されると、このリストの最初の項目は path[0]、Pythonインタープリターを呼び出すために使用されたスクリプトを含むディレクトリです


23

これを行わないでください。

スクリプトとデータを1つの大きなディレクトリにまとめないでください。あなたのコードは、いくつかの既知の場所(に置く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
S.ロットは正しいです。データが一時的でない限り、常にデータとコードを分離してください。たとえば、アイコンがある場合、それはデータですが、一時的なものではなく、ソフトウェアバンドル(それが何であれ意味する)に関連してそれを考慮することは理にかなっています
Stefano Borini

5
@Udi Pasmon:全然遠くない。これは、組織を深刻な問題に陥らせる「小さなメンテナンススクリプト」です。今から数年後、この「小さなメンテナンススクリプト」とその子、派生物、データファイルは、もつれを解いて再実装するのに悪夢になります。データをコードからできるだけ離します-すべてのパラメーターを渡します-何も仮定しません。
S.Lott、

1
+1 OPとしてやりたいと思ったが、アドバイスを読んだ後、代わりにスクリプトを変更した。現在は、ログファイルの場所を指定するパラメーターが必要です。
Iain Samuel McLean Elder

+1。データディレクトリを簡単にカスタマイズできる場合は、Pythonスクリプトのパッケージ(rpm)を作成する方が簡単です。
jfs 2014

18

crontabコマンドを次のように変更します

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

(...)単一のコマンドとしてごcrond実行するサブシェルを起動します。これ|| exit 1により、ディレクトリが利用できない場合にcronjobが失敗します。

他のソリューションは、特定のスクリプトについては長期的に見ればより洗練されているかもしれませんが、実行したいプログラムまたはコマンドを変更できない場合でも、私の例は役立つ可能性があります。


1
これは非常に健全なソリューションです。私は通常、のようなものを追加するために他の人の回答を編集してい|| exit 1ます。これを見ると爽やかです。私はなぜあなたがそうしないのか疑問に思う必要がありますcd /home/udi/foo/ && ./bar.py
Bruno Bronosky

2
@BrunoBronosky明示的に指定exit 1すると、crondにエラーが通知され、ほとんどの場合、失敗のメール通知が送信されます。
Ruud Althuizen 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.