Chciałbym zobaczyć, jaki jest najlepszy sposób określenia bieżącego katalogu skryptów w Pythonie?
Odkryłem, że ze względu na wiele sposobów wywoływania kodu python trudno jest znaleźć dobre rozwiązanie.
Oto kilka problemów:
__file__
nie jest zdefiniowany, jeśli skrypt jest wykonywany za pomocąexec
,execfile
__module__
jest zdefiniowany tylko w modułach
Przypadków użycia:
./myfile.py
python myfile.py
./somedir/myfile.py
python somedir/myfile.py
execfile('myfile.py')
(z innego skryptu, który może znajdować się w innym katalogu i może mieć inny katalog bieżący.
Wiem, że nie ma idealnego rozwiązania, ale szukam najlepszego rozwiązania, które rozwiązałoby większość przypadków.
Najczęściej stosowanym podejściem jest, os.path.dirname(os.path.abspath(__file__))
ale to naprawdę nie działa, jeśli wykonasz skrypt z innego za pomocą exec()
.
Ostrzeżenie
Każde rozwiązanie korzystające z bieżącego katalogu zawiedzie, może być inaczej w zależności od sposobu wywołania skryptu lub można je zmienić w działającym skrypcie.
python
pythonpath
dirname
bogdan
źródło
źródło
pathlib
rozwiązanie jeśli używasz Pythona 3.4 lub nowszy: stackoverflow.com/a/48931294/1011724python myfile.py
z powłoki, działa, ale zarówno:!python %
i:!python myfile.py
wewnątrz vima zawodzą z System nie może znaleźć określonej ścieżki. To dość denerwujące. Czy ktoś może skomentować przyczynę tego i potencjalne obejścia?Odpowiedzi:
jest naprawdę najlepszy, co możesz dostać.
Wykonywanie skryptu przy pomocy
exec
/execfile
; normalnie powinieneś używać infrastruktury modułów do ładowania skryptów. Jeśli musisz użyć tych metod, sugeruję ustawienie__file__
wglobals
skrypcie, aby mógł odczytać tę nazwę pliku.Nie ma innego sposobu na uzyskanie nazwy pliku w wykonywanym kodzie: jak zauważyłeś, CWD może znajdować się w zupełnie innym miejscu.
źródło
Jeśli naprawdę chcesz uwzględnić przypadek wywołania skryptu
execfile(...)
, możesz użyćinspect
modułu, aby wydedukować nazwę pliku (w tym ścieżkę). O ile mi wiadomo, będzie to działać we wszystkich przypadkach, które wymieniłeś:źródło
chdir()
przed funkcją, zmieni to wynik. Również wywołanie skryptu python z innego katalogu zmieni wynik, więc nie jest to dobre rozwiązanie.os.path.expanduser("~")
to wieloplatformowy sposób na uzyskanie katalogu użytkownika. Niestety, nie jest to najlepsza praktyka systemu Windows dotycząca miejsca, w którym należy przykleić dane aplikacji.chdir()
przed uruchomieniem skryptu; daje poprawny wynik. Próbowałem wywołać skrypt z innego katalogu i to również działa. Wyniki są takie same, jakinspect.getabsfile()
rozwiązania oparte na .Działa na CPython, Jython, Pypy. Działa, jeśli skrypt jest wykonywany przy użyciu
execfile()
(sys.argv[0]
i__file__
rozwiązania oparte na rozwiązaniu nie powiedzie się tutaj). Działa, jeśli skrypt znajduje się w wykonywalnym pliku zip (/ jajko) . Działa, jeśli skrypt jest „importowany” (PYTHONPATH=/path/to/library.zip python -mscript_to_run
) z pliku zip; w tym przypadku zwraca ścieżkę archiwum. Działa, jeśli skrypt jest skompilowany w samodzielny plik wykonywalny (sys.frozen
). Działa dla dowiązań symbolicznych (realpath
eliminuje dowiązania symboliczne). Działa w interaktywnym tłumaczu; w tym przypadku zwraca bieżący katalog roboczy.źródło
getabsfile(..)
nie wymieniono go w dokumentacjiinspect
? Pojawia się w źródle połączonym z tą stroną.getsourcefile()
,getfile()
które jest udokumentowane.W Pythonie 3.4+ możesz użyć prostszego
pathlib
modułu:źródło
Path(__file__)
(nie potrzebujeszinspect
modułu).Path(__file__)
podaje,/path/to/script/currentscript.py
kiedy OP chciał uzyskać/path/to/script/
parent = Path(__file__).resolve().parent
To o wiele ładniejsze..joinpath()
(lub/
operatora)+
.os.path...
Podejście było „coś zrobić” w Pythonie 2.W Pythonie 3 możesz znaleźć katalog skryptów w następujący sposób:
źródło
Path(__file__).parent
. Alecwd
jest mylące, to nie jest bieżący katalog roboczy , ale katalog pliku . Mogą być takie same, ale zwykle tak nie jest.Po prostu zastosuj
os.path.dirname(os.path.abspath(__file__))
i sprawdź bardzo dokładnie, czy istnieje rzeczywista potrzeba w przypadkuexec
użycia. Może to oznaczać problem z projektowaniem, jeśli nie możesz użyć skryptu jako modułu.Pamiętaj o Zen w Pythonie # 8 , a jeśli uważasz, że istnieje dobry argument za przypadkiem użycia, w którym musi on działać
exec
, to daj nam znać więcej szczegółów na temat tła problemu.źródło
By
rób co chcesz? Nie jestem pewien, co dokładnie masz na myśli przez „bieżący katalog skryptów”. Jaka byłaby oczekiwana wydajność dla podanych przypadków użycia?
źródło
exec('myfile.py')
, tak samo jak__file__
isys.argv[0]
.Po pierwsze ... kilka brakujących przypadków użycia, jeśli mówimy o sposobach wstrzykiwania anonimowego kodu ..
Ale prawdziwe pytanie brzmi: jaki jest twój cel - czy próbujesz egzekwować jakieś zabezpieczenia? A może interesuje Cię tylko ładowanie.
Jeśli interesuje Cię bezpieczeństwo , nazwa pliku importowanego przez plik wykonywalny / wykonywalny jest nieistotna - powinieneś użyć rexec , który oferuje:
Jeśli jednak jest to raczej akademickie zajęcie ... oto kilka głupich podejść, do których możesz być może zagłębić się trochę głębiej ...
Przykładowe skrypty:
./deep.py
./deeper.py
/tmp/deepest.py
./codespy.py
Wynik
Oczywiście jest to sposób wymagający dużych nakładów, śledzenie całego kodu. Niezbyt wydajne. Ale myślę, że jest to nowatorskie podejście, ponieważ działa nadal, nawet gdy zagłębiasz się w gnieździe. Nie można zastąpić „eval”. Chociaż może przesłonić execfile ().
Zauważ, że to podejście obejmuje tylko exec / execfile, a nie „import”. Do przechwytywania obciążenia „modułu” na wyższym poziomie możesz użyć sys.path_hooks (Zapis dzięki uprzejmości PyMOTW).
To wszystko, co mam z głowy.
źródło
Oto częściowe rozwiązanie, wciąż lepsze niż wszystkie dotychczas opublikowane.
Teraz to zadziała wszystkie połączenia, ale jeśli ktoś użyje
chdir()
do zmiany bieżącego katalogu, to również się nie powiedzie.Uwagi:
sys.argv[0]
nie będzie działać, powróci,-c
jeśli wykonasz skrypt za pomocąpython -c "execfile('path-tester.py')"
źródło
Powinno to działać w większości przypadków:
źródło
Mam nadzieję, że to pomoże: - Jeśli uruchomisz skrypt / moduł z dowolnego miejsca, będziesz mieć dostęp do
__file__
zmiennej, która jest zmienną modułową reprezentującą lokalizację skryptu.Z drugiej strony, jeśli używasz interpretera, nie masz dostępu do tej zmiennej, gdzie uzyskasz nazwę
NameError
ios.getcwd()
poda ci niepoprawny katalog, jeśli uruchomisz plik z innego miejsca.To rozwiązanie powinno dać ci to, czego szukasz we wszystkich przypadkach:
Nie przetestowałem go dokładnie, ale rozwiązało to mój problem.
źródło