Uzyskaj nazwę bieżącego skryptu w Pythonie

407

Próbuję uzyskać nazwę aktualnie działającego skryptu Python.

Mam wywołany skrypt foo.pyi chciałbym zrobić coś takiego, aby uzyskać nazwę skryptu:

print Scriptname
SubniC
źródło

Odpowiedzi:

620

Możesz użyć, __file__aby uzyskać nazwę bieżącego pliku. W przypadku użycia w module głównym jest to nazwa skryptu, który został pierwotnie wywołany.

Jeśli chcesz pominąć część katalogu (która może być obecna), możesz użyć os.path.basename(__file__).

Sven Marnach
źródło
15
Python 3.2: „ Exception NameError: NameError("global name '__file__' is not defined",)
sdaau
20
@ sdaau: __file__nie jest zdefiniowany w interaktywnym tłumaczu , ponieważ nie ma tam znaczenia. Jest on ustawiany przez implementację importu, więc jeśli użyjesz niestandardowego mechanizmu importu, może on również zostać rozbrojony.
Sven Marnach
8
Przynajmniej dla Pythona 2.7 uważam, że import osjest wymagany, aby to zadziałało. Dodałbym to do odpowiedzi.
Nick Chammas
14
@ cdunn2001: import osi import os.pathsą całkowicie równoważne.
Sven Marnach,
2
@ sven-marnach: Oh, masz rację . Robię to źle od lat!
cdunn2001
136
import sys
print sys.argv[0]

Spowoduje to wydrukowanie foo.pyna python foo.py, dir/foo.pyna python dir/foo.py, itd. Jest to pierwszy argument python. (Zauważ, że po py2exe będzie foo.exe.)

Chris Morgan
źródło
33
@DenisMalinovsky: zdefiniuj „nie będzie działać”. Jeśli zadzwonisz python linkfile.py, gdzie linkfile.pyjest dowiązanie symboliczne realfile.py, sys.argv[0]będzie 'linkfile.py', co może lub nie być tym, czego chcesz; z pewnością tego oczekuję . __file__jest to samo: będzie linkfile.py. Jeśli chcesz się dowiedzieć 'realfile.py'od 'linkfile.py'spróbować os.path.realpath('linkfile.py').
Chris Morgan
6
+1, ponieważ jest to (a) trochę starsze, a (b) nadal będzie działać w module (gdzie zmienną pliku byłby plik modułu, a nie wykonywany).
Robert
Ta odpowiedź jest miła, ponieważ działa również w IDLE. Uwaga: aby uzyskać tylko nazwę pliku, możesz napisać os.path.basename (sys.argv [0])
Steven Bluen
Co ważniejsze, to nie działa inaczej niż w głównym pliku. Nie używaj tego, użyj __file__zamiast tego.
Apollys popiera Monikę
CO!!! Próbowałeś nawet tego? Dokładnie odwrotnie. Pytający zapytał o nazwę działającego skryptu python - nie o plik, który jest aktualnie wykonywany. Wyobraź sobie, że masz skrypt, który w przypadku wystąpienia błędu drukuje nazwę skryptu wraz z dozwolonymi argumentami. Umieszczasz to w funkcji, używając jednej z tych 2 technik. W pewnym momencie zdecydujesz się przenieść funkcję do biblioteki zewnętrznej. Czy chcesz wydrukować nazwę uruchomionego skryptu głównego, czy nazwę uruchamianego pliku biblioteki?
John Deighan
70

Ze względu na kompletność, pomyślałem, że warto podsumować różne możliwe wyniki i podać odniesienia do dokładnego zachowania każdego z nich:

  • __file__jest aktualnie wykonywanym plikiem, jak wyszczególniono w oficjalnej dokumentacji :

    __file__jest ścieżką do pliku, z którego moduł został załadowany, jeśli został załadowany z pliku. W __file__przypadku niektórych typów modułów, takich jak moduły C , które są statycznie połączone z tłumaczem, może brakować atrybutu ; w przypadku modułów rozszerzeń ładowanych dynamicznie z biblioteki współdzielonej jest to ścieżka do pliku biblioteki współdzielonej.

    Od Python3.4 roku, za numerze 18416 , __file__zawsze jest ścieżka bezwzględna, chyba że plik aktualnie wykonywany jest skrypt, który został wykonany bezpośrednio (nie przez tłumacza z -mopcji wiersza polecenia) przy użyciu ścieżki względnej.

  • __main__.__file__(wymaga importu __main__) po prostu uzyskuje dostęp do wyżej wymienionego __file__atrybutu modułu głównego , np. skryptu, który został wywołany z wiersza poleceń.

  • sys.argv[0](wymaga importu sys) to nazwa skryptu, która została wywołana z wiersza poleceń i może być ścieżką bezwzględną, jak opisano w oficjalnej dokumentacji :

    argv[0]to nazwa skryptu (jest to zależne od systemu operacyjnego, czy jest to pełna nazwa ścieżki, czy nie). Jeśli polecenie zostało wykonane przy użyciu -copcji wiersza polecenia interpretera, argv[0]ustawia się na ciąg '-c'. Jeśli do interpretera języka Python nie przekazano żadnej nazwy skryptu, argv[0]oznacza to, że jest to pusty ciąg.

    Jak wspomniano w innej odpowiedzi na to pytanie , skrypty Pythona, które zostały przekonwertowane na samodzielne programy wykonywalne za pomocą narzędzi takich jak py2exe lub PyInstaller, mogą nie wyświetlać pożądanego wyniku podczas korzystania z tego podejścia (tzn. Zawierająsys.argv[0] nazwę pliku wykonywalnego zamiast nazwy głównego pliku Pythona w tym pliku wykonywalnym).

  • Jeśli wydaje się, że żadna z wyżej wymienionych opcji nie działa, prawdopodobnie z powodu nieregularnej operacji importowania, moduł inspekcji może okazać się przydatny. W szczególności, powołując się inspect.getfile(...)na inspect.currentframe()może pracować, choć ten ostatni będzie powrócićNone , gdy uruchomiony w implementacji bez Pythona stos ramki.


Obsługa dowiązań symbolicznych

Jeśli bieżący skrypt jest dowiązaniem symbolicznym, wszystkie powyższe zwrócą ścieżkę dowiązania symbolicznego, a nie ścieżkę rzeczywistego pliku, i os.path.realpath(...)należy je wywołać w celu wyodrębnienia tego ostatniego.


Dalsze manipulacje, które wyodrębniają rzeczywistą nazwę pliku

os.path.basename(...)może być wywoływany na dowolnym z powyższych w celu wyodrębnienia rzeczywistej nazwy pliku i os.path.splitext(...)może być wywoływany na rzeczywistej nazwie pliku w celu obcięcia jego sufiksu, jak w os.path.splitext(os.path.basename(...)).

Od Pythona 3.4 roku, za PEP 428 The PurePathklasa z pathlibmodułu mogą być również stosowane na którekolwiek z powyższych. W szczególności pathlib.PurePath(...).namewyodrębnia rzeczywistą nazwę pliku i pathlib.PurePath(...).stemwyodrębnia rzeczywistą nazwę pliku bez przyrostka.

Yoel
źródło
66

Zauważ, że __file__da plik, w którym znajduje się ten kod, który można importować i różni się od interpretowanego pliku głównego. Aby uzyskać główny plik, można użyć specjalnego modułu __main__ :

import __main__ as main
print(main.__file__)

Zauważ, że __main__.__file__działa w Pythonie 2.7, ale nie w 3.2, więc użyj składni import-as jak wyżej, aby była przenośna.

Ambroz Bizjak
źródło
Działa to w wielu przypadkach, ale nie wtedy, gdy używam rPythonpakietu z Rjęzyka. To musi być wyjątkowy przypadek, który jest po prostu zbyt trudny do opanowania.
Leonid
Rzeczywiście, pakiet rPython zawiera interpreter Pythona, co oznacza, że ​​nie ma „głównego” pliku, takiego jak w przypadku, gdy Python działa samodzielnie (to samo zachowanie występuje przy każdym osadzeniu Pythona). Importuje __main__wewnętrznie, do użycia w przekazywaniu zmiennych między Ri python, więc stosunkowo łatwo byłoby ustawić go __main__.__file__przed wywołaniem czegokolwiek innego, ale nie jestem nawet pewien, jaka byłaby odpowiednia wartość w tym przypadku.
Perkins
42

Powyższe odpowiedzi są dobre. Ale uznałem tę metodę za bardziej wydajną, korzystając z powyższych wyników.
Powoduje to, że rzeczywista nazwa pliku skryptu nie jest ścieżką.

import sys    
import os    
file_name =  os.path.basename(sys.argv[0])
Manoj Sahu
źródło
1
Lubię też rozdzielać rozszerzenie, więc używam: os.path.splitext (os.path.basename (sys.argv [0])) [0]
RufusVS
19

W przypadku nowoczesnych wersji Python (3.4+) Path(__file__).namepowinno być bardziej idiomatyczne. Ponadto, Path(__file__).stemdaje nazwę skryptu bez .pyrozszerzenia.

Emil Mielnikow
źródło
NameError: nazwa 'Path' nie jest zdefiniowana
RufusVS
5
Powinieneś from pathlib import Pathpierwszy.
Emil Melnikov
„nowoczesny” oznacza Python 3.x?
einpoklum
1
@einpoklum pathlibzostało wprowadzone w Pythonie 3.4, więc powinno działać od Pythona 3.4.
Andrey Semakin
9

Spróbuj tego:

print __file__
demas
źródło
9

Uwaga: Jeśli używasz Python 3+, powinieneś zamiast tego użyć funkcji print ()

Zakładając, że nazwa pliku to foo.pyponiższy fragment kodu

import sys
print sys.argv[0][:-3]

lub

import sys
print sys.argv[0][::-1][3:][::-1]

Podobnie jak w przypadku innych rozszerzeń z większą liczbą znaków, na przykład nazwa pliku foo.pypy

import sys
print sys.argv[0].split('.')[0]

Jeśli chcesz wyodrębnić z bezwzględnej ścieżki

import sys
print sys.argv[0].split('/')[-1].split('.')[0]

wyjdzie foo

xtonousou
źródło
1
sys.argv [0] [: - 3] zrobiłby
kon psych
@konpsych, który jest bardziej elegancki
xtonousou
Jest duża różnica między __file__i sys.argv[0], patrz stackoverflow.com/questions/5851588/...
Maksym Ganenko
8

Pierwszym argumentem w sys będzie nazwa bieżącego pliku, więc to zadziała

   import sys
   print sys.argv[0] # will print the file name
Charlie OConor
źródło
7

Jeśli wykonujesz nietypowy import (np. Jest to plik opcji), spróbuj:

import inspect
print (inspect.getfile(inspect.currentframe()))

Zauważ, że to zwróci bezwzględną ścieżkę do pliku.

Gajendra D Ambi
źródło
3

możemy spróbować uzyskać bieżącą nazwę skryptu bez rozszerzenia.

import os

script_name = os.path.splitext(os.path.basename(__file__))[0]
Krishn Singh
źródło
2

Ponieważ OP poprosił o nazwę bieżącego pliku skryptu, wolałbym

import os
os.path.split(sys.argv[0])[1]
PeterXX
źródło
0

Moje szybkie, brudne rozwiązanie:

__file__.split('/')[-1:][0]
E.Big
źródło
2
Lepsze wykorzystanie os.pathdo dzielenia nazw plików
Maksym Ganenko
0

wszystkie te odpowiedzi są świetne, ale mają pewne problemy, których możesz nie zobaczyć na pierwszy rzut oka.

pozwala zdefiniować, co chcemy - chcemy nazwę skryptu, który został wykonany, a nie nazwę bieżącego modułu - więc __file__będzie działać tylko wtedy, gdy zostanie użyty w wykonanym skrypcie, a nie w zaimportowanym module. sys.argvjest również wątpliwe - co jeśli twój program został wywołany przez pytest? czy pydoc runner? lub jeśli został nazwany przez uwsgi?

i - istnieje trzecia metoda uzyskania nazwy skryptu, której nie widziałem w odpowiedziach - Możesz sprawdzić stos.

Innym problemem jest to, że Ty (lub inny program) możesz manipulować sys.argvi __main__.__file__- może być obecny, może nie być. Może być poprawny lub nie. Przynajmniej możesz sprawdzić, czy skrypt (pożądany wynik) istnieje!

moja biblioteka bitranox / lib_programname w github robi dokładnie to:

  • sprawdź, czy __main__jest obecny
  • sprawdź, czy __main__.__file__jest obecny
  • daje __main__.__file__prawidłowy wynik (czy ten skrypt istnieje?)
  • jeśli nie: sprawdź sys.argv:
  • czy w sys.argv jest pytest, docrunner itp? -> jeśli tak, zignoruj ​​to
  • czy możemy uzyskać prawidłowy wynik tutaj?
  • jeśli nie: sprawdź stos i uzyskaj stamtąd wynik
  • jeśli również stos nie daje poprawnego wyniku, należy zgłosić wyjątek.

tą drogą, moje rozwiązanie działa do tej pory setup.py test, uwsgi, pytest, pycharm pytest, pycharm docrunner (doctest), dreampie,eclipse

jest też miły artykuł na blogu o tym problemie autorstwa Dough Hellman, „Określanie nazwy procesu w Pythonie”

bitranox
źródło
0

Od wersji Python 3.5 możesz po prostu:

from pathlib import Path
Path(__file__).stem

Zobacz więcej tutaj: https://docs.python.org/3.5/library/pathlib.html#pathlib.PurePath.stem

Na przykład mam plik w katalogu użytkownika o nazwie test.pyz tym wewnątrz:

from pathlib import Path

print(Path(__file__).stem)
print(__file__)

uruchamianie tych wyjść:

>>> python3.6 test.py
test
test.py
elad srebrny
źródło