Jak uzyskać ścieżkę i nazwę aktualnie wykonywanego pliku?

482

Mam skrypty wywołujące inne pliki skryptów, ale muszę uzyskać ścieżkę do pliku, który jest aktualnie uruchomiony w procesie.

Załóżmy na przykład, że mam trzy pliki. Za pomocą pliku wykonywalnego :

  • script_1.pypołączenia script_2.py.
  • Z kolei script_2.pypołączenia script_3.py.

Jak mogę uzyskać nazwę pliku i ścieżkę script_3.py, z kodu wewnątrzscript_3.py , bez konieczności przekazywania tych informacji jako argumentów script_2.py?

(Wykonanie os.getcwd()zwraca ścieżkę pliku oryginalnego skryptu startowego, a nie bieżący plik.)

Promień
źródło
2
os.path.realpath ( plik )
Girish Gupta

Odpowiedzi:

256

p1.py:

execfile("p2.py")

p2.py:

import inspect, os
print (inspect.getfile(inspect.currentframe()) # script filename (usually with path)
print (os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) # script directory
Pat Notz
źródło
11
UWAGA: To połączenie nie daje tego samego wyniku w różnych środowiskach. Zastanów się nad odpowiedzią Usagi poniżej: stackoverflow.com/a/6628348/851398
faraday
3
@faraday: Czy możesz podać przykład? Odpowiedziałem na podobne pytanieinspect.getabsfile() i zadziałało we wszystkich przypadkach, które próbowałem.
jfs
1
Czy rzeczywiście odpowiedź @Usagi jest lepsza?
Dror,
4
nah user13993 przybił to znacznie lepiej
osirisgothra
6
user13993 rzeczywiście to zrobił. Powinien byćos.path.realpath(__file__)
uchuugaka,
565
__file__

jak powiedzieli inni. Możesz także użyć os.path.realpath, aby wyeliminować dowiązania symboliczne:

import os

os.path.realpath(__file__)
użytkownik13993
źródło
14
Należy zachować ostrożność przy takim podejściu, ponieważ czasami __file__zwraca „script_name.py”, a innym razem „script_name.pyc”. Więc wyjście nie jest stabilne.
mechatroner
27
Ale ponieważ używamy tylko ścieżki tego pliku, która jest nieistotna.
Uwe Koloska
5
jest to dziwne: podczas uruchamiania z wiersza poleceń "__file__"w cudzysłowach (jako ciąg) podaje __file__ katalog, z którego uruchamiane jest cmd, ale (bez cudzysłowów ścieżka do pliku źródłowego ... dlaczego tak jest
muon
7
@muon nie jest sprawdzane, czy istnieje przekazany ciąg nazwy pliku, a ponieważ ścieżki do plików są względne w stosunku do pliku cwd, os.path.realpathfunkcja zakłada , że jest to dirczęść ścieżki. os.path.dirname(os.path.realpath(__file__))zwraca katalog z plikiem. os.path.dirname(os.path.realpath("__file__"))zwraca cwd. os.path.dirname(os.path.realpath("here_be_dragons"))zwraca również cwd.
Jamie Bull,
86

Aktualizacja 28.11.2018:

Oto podsumowanie eksperymentów z Pythonem 2 i 3. Z

main.py - uruchamia foo.py
foo.py - uruchamia lib / bar.py
lib / bar.py - drukuje wyrażenia ścieżki pliku

| Python | Run statement       | Filepath expression                    |
|--------+---------------------+----------------------------------------|
|      2 | execfile            | os.path.abspath(inspect.stack()[0][1]) |
|      2 | from lib import bar | __file__                               |
|      3 | exec                | (wasn't able to obtain it)             |
|      3 | import lib.bar      | __file__                               |

W przypadku Python 2 przełączenie na pakiety może być łatwiejsze from lib import bar- wystarczy dodać puste __init__.pypliki do dwóch folderów.

Dla Pythona 3 execfilenie istnieje - najbliższa alternatywa exec(open(<filename>).read()), choć wpływa to na ramki stosu. Jest najprostszy w użyciuimport foo i import lib.bar- nie __init__.pypotrzeba żadnych plików.

Zobacz także Różnica między importem a plikiem wykonywalnym


Oryginalna odpowiedź:

Oto eksperyment oparty na odpowiedziach w tym wątku - z Python 2.7.10 w systemie Windows.

Te oparte na stosie są jedynymi, które wydają się dawać wiarygodne wyniki. Dwa ostatnie mają najkrótszą składnię , tj. -

print os.path.abspath(inspect.stack()[0][1])                   # C:\filepaths\lib\bar.py
print os.path.dirname(os.path.abspath(inspect.stack()[0][1]))  # C:\filepaths\lib

Oto ich dodawanie do sys jako funkcji! Podziękowania dla @Usagi i @pablog

Oparty na następujących trzech plikach i uruchamiający main.py ze swojego folderu z python main.py(wypróbował również pliki wykonywalne z bezwzględnymi ścieżkami i wywoływaniem z oddzielnego folderu).

C: \ filepaths \ main.py: execfile('foo.py')
C: \ filepaths \ foo.py: execfile('lib/bar.py')
C: \ filepaths \ lib \ bar.py:

import sys
import os
import inspect

print "Python " + sys.version
print

print __file__                                        # main.py
print sys.argv[0]                                     # main.py
print inspect.stack()[0][1]                           # lib/bar.py
print sys.path[0]                                     # C:\filepaths
print

print os.path.realpath(__file__)                      # C:\filepaths\main.py
print os.path.abspath(__file__)                       # C:\filepaths\main.py
print os.path.basename(__file__)                      # main.py
print os.path.basename(os.path.realpath(sys.argv[0])) # main.py
print

print sys.path[0]                                     # C:\filepaths
print os.path.abspath(os.path.split(sys.argv[0])[0])  # C:\filepaths
print os.path.dirname(os.path.abspath(__file__))      # C:\filepaths
print os.path.dirname(os.path.realpath(sys.argv[0]))  # C:\filepaths
print os.path.dirname(__file__)                       # (empty string)
print

print inspect.getfile(inspect.currentframe())         # lib/bar.py

print os.path.abspath(inspect.getfile(inspect.currentframe())) # C:\filepaths\lib\bar.py
print os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) # C:\filepaths\lib
print

print os.path.abspath(inspect.stack()[0][1])          # C:\filepaths\lib\bar.py
print os.path.dirname(os.path.abspath(inspect.stack()[0][1]))  # C:\filepaths\lib
print
Brian Burns
źródło
72

Myślę, że to jest czystsze:

import inspect
print inspect.stack()[0][1]

i otrzymuje te same informacje, co:

print inspect.getfile(inspect.currentframe())

Gdzie [0] to bieżąca ramka na stosie (góra stosu), a [1] to nazwa pliku, zwiększ, aby cofnąć się na stosie, tj.

print inspect.stack()[1][1]

będzie nazwą pliku skryptu, który wywołał bieżącą ramkę. Ponadto użycie [-1] spowoduje przejście do dolnej części stosu, oryginalnego skryptu wywołującego.

Usagi
źródło
4
Nie zaleca się polegania na pozycji wewnątrz krotki. W ogóle nie jest jasne, jakie dane próbujesz uzyskać podczas czytania kodu.
jpmc26
5
inspect.getfile()zwraca __file__atrybut, jeśli przekazano obiekt modułu. inspect.currentframe()zwraca moduł. Ergo, to drogi sposób na powiedzenie __file__.
Martijn Pieters
inspect.stack()jest dość kosztowną funkcją, bardziej niż tylko inspect.currentframe(), i także wywołuje inspect.getfile()obiekt modułu.
Martijn Pieters
42
import os
os.path.dirname(__file__) # relative directory path
os.path.abspath(__file__) # absolute file path
os.path.basename(__file__) # the file name only
Neal Xiong
źródło
1
Właśnie spróbowałem komentarza @Pat Notz. Myślę, że możesz po prostu uzyskać nazwę pliku __file__.
hlin117
W Python3 os.path.dirnamepoda względną ścieżkę, z której uruchamiasz skrypt. np będzie , a nie bezwzględna ścieżka do skryptu. Szukałem abspath, ale usunąłem nazwę skryptu. Pierwszy element sys.path najwyraźniej jest odpowiedzią .python ../../test.pyos.path.dirname../../sys.path[0]
aerijman
37

Wszystkie sugestie oznaczone jako najlepsze są prawdziwe, jeśli skrypt składa się tylko z jednego pliku.

Jeśli chcesz znaleźć nazwę pliku wykonywalnego (tj. Plik główny przekazany do interpretera Pythona dla bieżącego programu) z pliku, który można zaimportować jako moduł, musisz to zrobić (załóżmy, że jest to plik o nazwie foo.py ):

import inspect

print inspect.stack()[-1][1]

Ponieważ ostatnia rzecz ( [-1]) na stosie jest pierwszą rzeczą, która weszła w nią (stosy to struktury danych LIFO / FILO).

Następnie w pliku bar.py, jeśli import foowydrukujesz bar.py , zamiast foo.py , co byłoby wartością wszystkich tych:

  • __file__
  • inspect.getfile(inspect.currentframe())
  • inspect.stack()[0][1]

źródło
Działa dobrze, ale nie do testu jednostkowego na zaćmieniu, mam /home/user/Softwareres/eclipse/plugins/org.python.pydev_4.5.5.201603221110/pysrc
hayj
2
To naprawdę drogi sposób na pisownię sys.modules['__main__'].__file__.
Martijn Pieters
14
import os
print os.path.basename(__file__)

da nam to tylko nazwę pliku. tzn. jeśli ścieżka pliku to c: \ abcd \ abc.py, wówczas druga linia wypisze abc.py

vishal ekhe
źródło
14

Nie jest do końca jasne, co rozumiesz przez „ścieżkę pliku pliku, który jest obecnie uruchomiony w procesie”. sys.argv[0]zwykle zawiera lokalizację skryptu, który został wywołany przez interpreter Pythona. Sprawdź dokumentację sys po więcej szczegółów.

Jak zauważyli @Tim i @Pat Notz, atrybut __file__ zapewnia dostęp do

plik, z którego moduł został załadowany, jeśli został załadowany z pliku

Blair Conrad
źródło
1
„print os.path.abspath ( file )” i „print inspect.getfile (inspect.currentframe ())” nie działa, gdy przekazujemy program python do exe win32. Działa tylko sys.argv [0]! :) ale dostajesz tylko nazwę!
aF.
11

Mam skrypt, który musi działać w środowisku Windows. Ten fragment kodu został zakończony:

import os,sys
PROJECT_PATH = os.path.abspath(os.path.split(sys.argv[0])[0])

to dość hackerska decyzja. Ale nie wymaga zewnętrznych bibliotek i jest to w moim przypadku najważniejsza rzecz.

garmoncheg
źródło
1
Musiałem w tym celu „zaimportować OS, sys”, ale jak dotąd jest to najlepsza odpowiedź, która faktycznie zwraca tylko ścieżkę bez nazwy pliku na końcu łańcucha.
emmagras
10

Spróbuj tego,

import os
os.path.dirname(os.path.realpath(__file__))
Soumyajit
źródło
8
import os
os.path.dirname(os.path.abspath(__file__))

Nie ma potrzeby inspekcji ani żadnej innej biblioteki.

Działa to dla mnie, gdy musiałem zaimportować skrypt (z innego katalogu niż wykonany skrypt), który używał pliku konfiguracyjnego znajdującego się w tym samym folderze co importowany skrypt.

Kwuite
źródło
Nie da to pożądanej odpowiedzi, jeśli bieżący katalog roboczy (os.getcwd) jest inny niż katalog, w którym znajduje się plik.
Żelazna poduszka
2
Jak to? W tym przypadku działa dobrze. Dostaję katalog, w którym znajduje się plik.
Michael Mior,
@IronPillow może ta odpowiedź pomoże ci w tym, czego potrzebujesz. stackoverflow.com/a/41546830/3123191
Kwuite
6
import sys

print sys.path[0]

spowoduje to wydrukowanie ścieżki aktualnie wykonywanego skryptu

appusajeev
źródło
2
sys.path [0] jest bardzo przydatny, ale podaje ścieżkę do script1, a nie script3 zgodnie z żądaniem
James
Przynajmniej w systemie OS X z wersją 2.7 nie wydaje mi się, aby działało to niezawodnie. Działa, jeśli wykonujesz bezpośrednio ten sam plik. Nie działa z repl, zwłaszcza z importowanego jajka
uchuugaka,
5

Myślę, że to po prostu __file__ wygląda na to, że możesz również chcieć sprawdzić moduł inspekcji .

Pat Notz
źródło
Ahhh ... plik wykonywalny jest trudny. Zobacz mój drugi post na temat korzystania z modułu inspekcji.
Pat Notz
4

Możesz użyć inspect.stack()

import inspect,os
inspect.stack()[0]  => (<frame object at 0x00AC2AC0>, 'g:\\Python\\Test\\_GetCurrentProgram.py', 15, '<module>', ['print inspect.stack()[0]\n'], 0)
os.path.abspath (inspect.stack()[0][1]) => 'g:\\Python\\Test\\_GetCurrentProgram.py'
PabloG
źródło
4

Ponieważ Python 3 jest dość popularny, chciałem podać pathlibodpowiedź, ponieważ uważam, że jest to prawdopodobnie lepsze narzędzie do uzyskiwania dostępu do informacji o plikach i ścieżkach.

from pathlib import Path

current_file: Path = Path(__file__).resolve()

Jeśli szukasz katalogu bieżącego pliku, jest to tak proste, jak dodanie .parentdo Path()instrukcji:

current_path: Path = Path(__file__).parent.resolve()
Doug
źródło
3
import sys
print sys.argv[0]
WBAR
źródło
10
Niestety działa to tylko wtedy, gdy skrypt został wywołany z pełną ścieżką, ponieważ zwraca tylko „pierwszy argument” w wierszu poleceń, który jest wywołaniem skryptu.
cregox
2

To powinno działać:

import os,sys
filename=os.path.basename(os.path.realpath(sys.argv[0]))
dirname=os.path.dirname(os.path.realpath(sys.argv[0]))
Jahid
źródło
2
print(__file__)
print(__import__("pathlib").Path(__file__).parent)
BaiJiFeiLong
źródło
4
Odpowiedzi tylko na kod są odradzane. Dodaj wyjaśnienie, w jaki sposób rozwiązuje problem lub jak różni się on od istniejących odpowiedzi. Z recenzji
Nick
1

Aby uzyskać katalog wykonywania skryptu

 print os.path.dirname( inspect.getfile(inspect.currentframe()))
pbarański
źródło
1

Zawsze korzystałem właśnie z funkcji os bieżącego katalogu roboczego lub CWD. Jest to część standardowej biblioteki i jest bardzo łatwa do wdrożenia. Oto przykład:

    import os
    base_directory = os.getcwd()
Ethan J.
źródło
1
Czy możesz zmodyfikować swoją odpowiedź, aby dotyczyła postawionego pytania? Ta odpowiedź nie dotyczy pytania „Jak uzyskać ścieżkę i nazwę aktualnie wykonywanego pliku?
Marco
1
Podaje ścieżkę, z której uruchomiono polecenie python. Wydaje się więc, że działa, gdy uruchomisz python script.pypolecenie w tym samym folderze, w którym już jesteś, podczas gdy w rzeczywistości działa tak samo, jak pwdw Linuksie.
George
0

Zastosowałem podejście z __plikiem__,
os.path.abspath(__file__)
ale jest mała sztuczka, zwraca plik .py, gdy kod jest uruchamiany przy pierwszym uruchomieniu, kolejne uruchomienia podają nazwę pliku * .pyc,
więc zostałem z:
inspect.getfile(inspect.currentframe())
lub
sys._getframe().f_code.co_filename

mik80
źródło
0

Napisałem funkcję, która bierze pod uwagę debugger zaćmienia i najbardziej nieprzystosowany . Zwraca folder pierwszego uruchomionego skryptu. Opcjonalnie możesz określić zmienną __plik___ , ale najważniejsze jest to, że nie musisz udostępniać tej zmiennej w całej hierarchii wywołań .

Może poradzisz sobie ze stosami innych przypadków, których nie widziałem, ale dla mnie jest w porządku.

import inspect, os
def getRootDirectory(_file_=None):
    """
    Get the directory of the root execution file
    Can help: http://stackoverflow.com/questions/50499/how-do-i-get-the-path-and-name-of-the-file-that-is-currently-executing
    For eclipse user with unittest or debugger, the function search for the correct folder in the stack
    You can pass __file__ (with 4 underscores) if you want the caller directory
    """
    # If we don't have the __file__ :
    if _file_ is None:
        # We get the last :
        rootFile = inspect.stack()[-1][1]
        folder = os.path.abspath(rootFile)
        # If we use unittest :
        if ("/pysrc" in folder) & ("org.python.pydev" in folder):
            previous = None
            # We search from left to right the case.py :
            for el in inspect.stack():
                currentFile = os.path.abspath(el[1])
                if ("unittest/case.py" in currentFile) | ("org.python.pydev" in currentFile):
                    break
                previous = currentFile
            folder = previous
        # We return the folder :
        return os.path.dirname(folder)
    else:
        # We return the folder according to specified __file__ :
        return os.path.dirname(os.path.realpath(_file_))
Hayj
źródło
0

Aby zachować spójność migracji na różnych platformach (macOS / Windows / Linux), spróbuj:

path = r'%s' % os.getcwd().replace('\\','/')

Qiao Zhang
źródło
2
To jest źle. Otrzymasz bieżącą ścieżkę, ale nie ścieżkę bieżącego pliku.
Charles Plager
0

Najprostszym sposobem jest:

w script_1.py:

import subprocess
subprocess.call(['python3',<path_to_script_2.py>])

w script_2.py:

sys.argv[0]

PS: Próbowałem execfile, ale ponieważ odczytuje skrypt_2.py jako ciąg, sys.argv[0]zwróciło <string>.

Lucas Azevedo
źródło
0

Oto, czego używam, dzięki czemu mogę bez problemu rzucać kodem w dowolnym miejscu. __name__jest zawsze zdefiniowany, ale __file__jest definiowany tylko wtedy, gdy kod jest uruchamiany jako plik (np. nie w IDLE / iPython).

    if '__file__' in globals():
        self_name = globals()['__file__']
    elif '__file__' in locals():
        self_name = locals()['__file__']
    else:
        self_name = __name__

Alternatywnie można to zapisać jako:

self_name = globals().get('__file__', locals().get('__file__', __name__))
Craymichael
źródło
-2

Większość tych odpowiedzi została napisana w Pythonie w wersji 2.x lub wcześniejszej. W Pythonie 3.x zmieniono składnię funkcji drukowania, aby wymagała nawiasów, tj. Print ().

Tak więc, ta wcześniejsza odpowiedź o wysokim wyniku od user13993 w Python 2.x:

import inspect, os
print inspect.getfile(inspect.currentframe()) # script filename (usually with path)
print os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) # script directory

Staje się w Pythonie 3.x:

import inspect, os
print(inspect.getfile(inspect.currentframe())) # script filename (usually with path)
print(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) ) # script directory
użytkownik3339488
źródło
-3

jeśli chcesz tylko nazwę pliku bez ./lub .pymożesz spróbować

filename = testscript.py
file_name = __file__[2:-3]

file_name wypisze testcript, który możesz wygenerować co chcesz, zmieniając indeks wewnątrz []

sapam
źródło
-3
import os

import wx


# return the full path of this file
print(os.getcwd())

icon = wx.Icon(os.getcwd() + '/img/image.png', wx.BITMAP_TYPE_PNG, 16, 16)

# put the icon on the frame
self.SetIcon(icon)
jscabuzzo
źródło
7
os.getcwd () zwraca bieżący katalog PROCESU, a nie katalog aktualnie wykonywanego pliku. Może to wydawać się zwracać prawidłowe wyniki, ale istnieje wiele przypadków, w których wynikiem nie byłby katalog macierzysty bieżącego pliku. Znacznie lepiej jest użyć os.path.dirname ( plik ), jak sugerowano powyżej.
samaspin