現在のスクリプトディレクトリを適切に判断するにはどうすればよいですか?


260

Pythonで現在のスクリプトディレクトリを特定するための最良の方法を教えてください。

私は、Pythonコードを呼び出す方法が多いため、適切な解決策を見つけるのが難しいことを発見しました。

ここにいくつかの問題があります:

  • __file__でスクリプトを実行した場合、は定義されませんexecexecfile
  • __module__ モジュールでのみ定義されています

ユースケース:

  • ./myfile.py
  • python myfile.py
  • ./somedir/myfile.py
  • python somedir/myfile.py
  • execfile('myfile.py') (別のスクリプトから、別のディレクトリに配置でき、別の現在のディレクトリを持つことができます。

私は完璧な解決策はないことを知っていますが、ほとんどの場合を解決する最善のアプローチを探しています。

最も使用されるアプローチはですos.path.dirname(os.path.abspath(__file__))が、で別のスクリプトからスクリプトを実行する場合、これは実際には機能しませんexec()

警告

現在のディレクトリを使用するソリューションはすべて失敗します。これは、スクリプトの呼び出し方法によって異なる場合や、実行中のスクリプト内で変更される場合があります。


1
ファイルがどこから来たのかを知る必要がある場所をより具体的にできますか?-ファイルをインポートするコード(インクルード対応ホスト)またはインポートされたファイル (自己認識スレーブ)
シンセサイザー

3
pathlibPython 3.4以降を使用している場合は、Ron Kalianのソリューションを参照してください。stackoverflow.com/ a / 48931294/1011724
Dan

したがって、解決策はコードで現在のディレクトリを使用するのではなく、いくつかの設定ファイルを使用することですか?
ZhaoGang 2018

興味深い発見は、私はちょうど作られた:やったときpython myfile.pyシェル、それは動作しますから、両方:!python %:!python myfile.py内からのvimで失敗指定されたパスが見つかりません。これは非常に迷惑です。これの背後にある理由と潜在的な回避策について誰かがコメントできますか?
inVader 2018年

回答:


231
os.path.dirname(os.path.abspath(__file__))

確かにあなたが手に入れる最高のものです。

exec/でスクリプトを実行するのは珍しいことexecfileです。通常、モジュールインフラストラクチャを使用してスクリプトをロードする必要があります。これらのメソッドを使用する必要がある場合は、スクリプトに渡してファイル名を読み取れるように設定__file__することをglobalsお勧めします。

実行されたコードでファイル名を取得する方法は他にありません。お気づきのように、CWDは完全に異なる場所にある可能性があります。


2
絶対とは絶対言うな?これによると: stackoverflow.com/a/18489147 クロスプラットフォームのソリューションはabspath(getsourcefile(lambda:0))ですか?それとも私が行方不明のものはありますか?
ジェフエレン

131

スクリプトが経由execfile(...)で呼び出されるケースを本当にカバーしたい場合は、inspectモジュールを使用してファイル名(パスを含む)を推測できます。私が知る限り、これはあなたがリストしたすべてのケースで機能します:

filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))

4
これは確かに最も堅牢な方法だと思いますが、OPがこれを必要としていることには疑問があります。開発者が実行中のモジュールに相対的な場所でデータファイルを使用しているときにこれを行うことがよくありますが、IMOデータファイルは既知の場所に配置する必要があります。
Ryan Ginstrom

14
@Ryan LOL、マルチプラットフォームであり、モジュールにも付属している「既知の場所」を定義できるとしたらすばらしいでしょう。安全な場所はスクリプトの場所だけであることに賭ける準備ができています。これは、スクリプトがこの場所に書き込む必要があるという意味ではありませんが、データを読み取る場合は安全です。
ソリン

1
それでも解決策は良くありませんchdir()。関数の前に呼び出してみてください。結果が変わってしまいます。また、Pythonスクリプトを別のディレクトリから呼び出すと結果が変わるため、適切な解決策ではありません。
ソリン

2
os.path.expanduser("~")ユーザーのディレクトリを取得するクロスプラットフォームの方法です。残念ながら、これはアプリケーションデータを貼り付ける場所に関するWindowsのベストプラクティスではありません。
Ryan Ginstrom

6
@sorin:chdir()スクリプトを実行する前に試しました。正しい結果が得られます。別のディレクトリからスクリプトを呼び出してみましたが、それも機能します。結果はinspect.getabsfile()ベースのソリューションと同じです。
jfs 2014

43
#!/usr/bin/env python
import inspect
import os
import sys

def get_script_dir(follow_symlinks=True):
    if getattr(sys, 'frozen', False): # py2exe, PyInstaller, cx_Freeze
        path = os.path.abspath(sys.executable)
    else:
        path = inspect.getabsfile(get_script_dir)
    if follow_symlinks:
        path = os.path.realpath(path)
    return os.path.dirname(path)

print(get_script_dir())

CPython、Jython、Pypyで動作します。スクリプトを使用して実行した場合に機能しますexecfile()sys.argv[0]および- __file__ベースのソリューションはここでは失敗します)。スクリプトが実行可能なzipファイル(/ ang)内にある場合に機能します。スクリプトがPYTHONPATH=/path/to/library.zip python -mscript_to_runzipファイルから"インポート"()された場合に機能します。この場合、アーカイブパスを返します。スクリプトがスタンドアロンの実行可能ファイル(sys.frozen)にコンパイルされている場合に機能します。これはシンボリックリンクに対して機能しrealpathます(シンボリックリンクを削除します)。インタラクティブなインタプリタで機能します。この場合、現在の作業ディレクトリを返します。


PyInstallerと完璧に連携します。
15

1
のドキュメントにgetabsfile(..)記載されinspectいない理由はありますか?そのページからリンクされているソースに表示されます。
Evgeni Sergeev 2015年

@EvgeniSergeevバグかもしれません。それは簡単なラッパでgetsourcefile()getfile()文書化されています。
jfs 2015年

24

Python 3.4以降では、より単純なpathlibモジュールを使用できます。

from inspect import currentframe, getframeinfo
from pathlib import Path

filename = getframeinfo(currentframe()).filename
parent = Path(filename).resolve().parent

2
シンプルさ抜群!
Cometsong 2018

3
おそらく使用できますPath(__file__)inspectモジュールは必要ありません)。
ペケ

@Pequeを実行すると、親ディレクトリではなく、現在のファイルの名前を含むパスが生成されます。私は、同じディレクトリ内のファイルにポイントするために、現在のスクリプトのディレクトリを取得しようとしている場合は、たとえば、スクリプトと同じディレクトリに設定ファイルをロードするために期待して、Path(__file__)与え/path/to/script/currentscript.pyOPを取得したいとき/path/to/script/
ダボス

8
ああ、私は誤解しました、inspectモジュールを避けて、parent = Path(__file__).resolve().parent それはもっといいものを使うことを意味します。
ダボス

3
@Dut A. ではなく.joinpath()(または/演算子)を使用する必要があります+
Eugene Yarmash

13

os.path...アプローチは、Python 2で「行われたこと」でした。

Python 3では、次のようにスクリプトのディレクトリを見つけることができます。

from pathlib import Path
cwd = Path(__file__).parents[0]

11
または単にPath(__file__).parent。しかしcwd、それは現在の作業ディレクトリではなく、ファイルのディレクトリです。それらは同じである可能性がありますが、通常はそうではありません。
ヌーノ・アンドレ

5

を使用os.path.dirname(os.path.abspath(__file__))する場合に本当に必要があるかどうかを使用して非常に慎重に調べてくださいexec。スクリプトをモジュールとして使用できない場合は、設計に問題があることを示している可能性があります。

Zen of Python#8に注意してください。Zenof Python#8が機能する必要があるユースケースについて適切な議論があると思われる場合execは、問題の背景に関する詳細をお知らせください。


2
exec()で実行しないと、デバッガーコンテキストが失われます。また、exec()は、新しいプロセスを開始するよりもかなり高速であると想定されています。
ソリン

@sorin execと新しいプロセスの開始の問題ではないので、それはわからない議論です。これは、execとインポートまたは関数呼び出しのどちらを使用するかという問題です。
WIM

4

だろう

import os
cwd = os.getcwd()

何をしたいですか?「現在のスクリプトディレクトリ」が正確に何を意味するのかわかりません。あなたが与えたユースケースに対して期待される出力は何でしょうか?


3
それは役に立ちません。@bogdanが呼び出しスタックの最上位にあるスクリプトのディレクトリを探していると思います。つまり、彼/彼女のすべてのケースで、「myfile.py」が置かれているディレクトリを出力する必要があります。しかし、あなたのメソッドはexec('myfile.py')、と同じように__file__、を呼び出すファイルのディレクトリのみを出力しますsys.argv[0]
Zhang18

ええ、それは理にかなっています。私は、@ bogdanが単純なものを見落とさないようにしたかっただけで、彼らが何を望んでいるのか正確にはわかりませんでした。
ウィルMcCutchen

3

まず、匿名コードを挿入する方法について話している場合、ここではいくつかの使用例がありません。

code.compile_command()
code.interact()
imp.load_compiled()
imp.load_dynamic()
imp.load_module()
__builtin__.compile()
loading C compiled shared objects? example: _socket?)

しかし、本当の問題は、あなたの目標は何ですか?ある種のセキュリティを強制しようとしているのですか?または、ロードされているものに興味がありますか?

セキュリティに関心がある場合、exec / execfileを介してインポートされるファイル名は重要ではありません。以下を提供するrexecを使用する必要があります。

このモジュールにはRExecクラスが含まれ、r_eval()、r_execfile()、r_exec()、およびr_import()メソッドをサポートします。これらのメソッドは、標準のPython関数eval()、execfile()、execおよびimportステートメントの制限付きバージョンです。この制限された環境で実行されるコードは、安全と見なされるモジュールと機能にのみアクセスできます。必要に応じて、RExecの追加または削除機能をサブクラス化できます。

ただし、これがより学術的な目的である場合は、少し掘り下げることができるかもしれない2つの間抜けなアプローチを次に示します。

スクリプトの例:

./deep.py

print ' >> level 1'
execfile('deeper.py')
print ' << level 1'

./deeper.py

print '\t >> level 2'
exec("import sys; sys.path.append('/tmp'); import deepest")
print '\t << level 2'

/tmp/deepest.py

print '\t\t >> level 3'
print '\t\t\t I can see the earths core.'
print '\t\t << level 3'

./codespy.py

import sys, os

def overseer(frame, event, arg):
    print "loaded(%s)" % os.path.abspath(frame.f_code.co_filename)

sys.settrace(overseer)
execfile("deep.py")
sys.exit(0)

出力

loaded(/Users/synthesizerpatel/deep.py)
>> level 1
loaded(/Users/synthesizerpatel/deeper.py)
    >> level 2
loaded(/Users/synthesizerpatel/<string>)
loaded(/tmp/deepest.py)
        >> level 3
            I can see the earths core.
        << level 3
    << level 2
<< level 1

もちろん、これはリソースを集中的に使用する方法であり、すべてのコードをトレースすることになります。あまり効率的ではありません。しかし、巣に深く入り込んでも機能し続けるので、これは斬新なアプローチだと思います。'eval'を上書きすることはできません。できます)(execfileをオーバーライドします。

このアプローチは、「インポート」ではなく、exec / execfileのみを対象としています。より高いレベルの「モジュール」ロードフックでは、sys.path_hooksを使用できる場合があります (PyMOTWの厚意により提供)。

私が頭から離れているのはそれだけです。


2

これは部分的な解決策ですが、これまでに公開されたすべての解決策よりも優れています。

import sys, os, os.path, inspect

#os.chdir("..")

if '__file__' not in locals():
    __file__ = inspect.getframeinfo(inspect.currentframe())[0]

print os.path.dirname(os.path.abspath(__file__))

今これはすべての呼び出しで機能しますが、誰かが使用した場合 chdir()が現在のディレクトリを変更するし、これも失敗します。

ノート:

  • sys.argv[0]は機能しません。-cスクリプトを実行すると戻りますpython -c "execfile('path-tester.py')"
  • 私はhttps://gist.github.com/1385555で完全なテストを公開しました。あなたはそれを改善することを歓迎します。

1

これはほとんどの場合に機能するはずです。

import os,sys
dirname=os.path.dirname(os.path.realpath(sys.argv[0]))

5
このソリューションは現在のディレクトリを使用し、そのようなソリューションが失敗することを質問で明示的に述べています。
16

1

うまくいけばこれが役立つ:-どこからでもスクリプト/モジュールを実行すると、スクリプト__file__の場所を表すモジュール変数である変数にアクセスできるようになります。

一方、インタープリターを使用している場合は、その変数にアクセスできず、名前を取得します NameErroros.getcwd()ん。別の場所からファイルを実行している場合はを、誤ったディレクトリをます。

このソリューションは、すべての場合にあなたが探しているものを提供するはずです:

from inspect import getsourcefile
from os.path import abspath
abspath(getsourcefile(lambda:0))

十分にテストしていませんが、問題は解決しました。


これにより、ディレクトリではなくファイルが
取得され
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.