185


51

現在のスクリプトディレクトリを適切に判断する方法

Pythonで現在のスクリプトディレクトリを決定する最良の方法は何ですか?

pythonコードを呼び出す多くの方法のために、良い解決策を見つけるのは難しいことを発見しました。

以下にいくつかの問題を示します。

  • スクリプトが exec`で実行される場合、 file`は定義されません。 execfile

  • `module`はモジュールでのみ定義されます

ユースケース

  • 。/ myfile.py

  • python myfile.py

  • 。/ somedir / myfile.py

  • python somedir / myfile.py

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

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

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

警告

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

13 回答


168


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

確かにあなたが得ようとしている最高です。

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

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


112


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

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


38


#!/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`ベースのソリューションはここで失敗します)。 スクリプトがhttps://stackoverflow.com/a/5356563/4279 [実行可能なzipファイル(/ an egg)]内にある場合に機能します。 スクリプトがzipファイルから「インポート」された場合( `PYTHONPATH = / path / to / library.zip python -mscript_to_run)に機能します。この場合、アーカイブパスを返します。 スクリプトがスタンドアロンの実行可能ファイル( sys.frozen)にコンパイルされている場合に機能します。 シンボリックリンクに対して機能します( `realpath`はシンボリックリンクを削除します)。 インタラクティブなインタープリターで動作します。この場合、現在の作業ディレクトリを返します。


18


Python 3.4以降では、より簡単なhttps://docs.python.org/3/library/pathlib.html [pathlib]モジュールを使用できます。

from inspect import currentframe, getframeinfo
from pathlib import Path

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


5


`os.path.dirname(os.path.abspath( file ))`を使用し、 `exec`を使用する場合に本当に必要があるかどうかを慎重に調べてください。 スクリプトをモジュールとして使用できない場合、問題のあるデザインの兆候である可能性があります。

Python#8の_Zenを念頭に置いてください。また、 `exec`で動作する必要があるユースケースに適切な引数があると思われる場合は、問題の背景についての詳細をお知らせください。


3


最初.. 匿名コードを挿入する方法について話している場合、ここではいくつかのユースケースがありません。

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

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

securityに関心がある場合、exec / execfileを介してインポートされるファイル名は重要ではありません-http://docs.python.org/libraryを使用する必要があります/rexec.html[rexec]。以下を提供します。

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

ただし、これが学問的な追求の場合は。 ここでは、もう少し掘り下げることができるかもしれないいくつかの間抜けなアプローチを示します。

サンプルスクリプト:

*。/ 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/)
loaded(/tmp/deepest.py)
        >> level 3
            I can see the earths core.
        << level 3
    << level 2
<< level 1

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

このアプローチはexec / execfileのみを対象とし、「インポート」は対象としていないことに注意してください。 より高いレベルの「モジュール」ロードフックでは、http://blog.doughellmann.com/2009/11/pymotw-sys-part-7-modules-and-imports.html [sys.path_hooks]を使用できる場合があります(書き込みはPyMOTWの厚意による)。

それが私の頭上にあるすべてです。


3


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

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


3


Python 2では、「os.path …​」アプローチが「完了」しました。

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

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


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()`を使用して現在のディレクトリを変更すると、これも失敗します。

ノート:


2


import os
import sys

def get_script_path():
    return os.path.dirname(os.path.realpath(sys.argv[0]))


my_script_dir = get_script_path()
print my_script_dir

これにより、スタックの一番上にあるスクリプトのディレクトリが得られます(つまり、 実行されているもの-Pythonではなく、通常は最初に実行され、 `C:/`を返します)