Як отримати шлях і ім’я файлу, який зараз виконується?


482

У мене є сценарії, що викликають інші файли сценаріїв, але мені потрібно пройти шлях файлу, який зараз працює в процесі.

Наприклад, скажімо, у мене є три файли. Використання execfile :

  • script_1.pyдзвінки script_2.py.
  • У свою чергу script_2.pyдзвінки script_3.py.

Як я можу отримати ім'я файлу і шлях script_3.py, з коду всерединіscript_3.py , без необхідності передавати цю інформацію в якості аргументів від script_2.py?

(Виконання os.getcwd()повертає вихідний файл файлового шляху початкового сценарію, а не поточний файл.)



2
os.path.realpath ( файл )
Гіріш Гупта

Відповіді:


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

11
ПОПЕРЕДЖЕННЯ: Цей виклик не дає однакового результату для різних середовищ. Розгляньте прийняття відповіді Усагі нижче: stackoverflow.com/a/6628348/851398
faraday

3
@faraday: Чи можете ви навести приклад? Я відповів на подібне запитання, використовуючи,inspect.getabsfile() і він працював у всіх випадках, які я намагався.
jfs

1
Чи справді відповідь @Usagi краща?
Dror

4
nah user13993 прибив її набагато краще
osirisgothra

6
користувач13993 дійсно прибив це. Має бутиos.path.realpath(__file__)
uchuugaka

565
__file__

як казали інші. Ви також можете використовувати os.path.realpath для усунення символьних посилань:

import os

os.path.realpath(__file__)

14
Слід бути обережним із таким підходом, оскільки іноді __file__повертає 'script_name.py', а інколи 'script_name.pyc'. Таким чином, вихід не стабільний.
mechatroner

27
Але оскільки ми використовуємо лише шлях до цього файлу, це не має значення.
Уве Колоська

5
це дивно: при запуску з командного рядка "__file__"в лапках (як рядок) дає dir, з якого запускається cmd, але __file__ (без лапок дає шлях до вихідного файлу ... чому це
muon

7
@muon не проводиться перевірка того, чи існує передана рядок імені файлу, і оскільки шляхи до файлу відносяться до cwd, саме ця os.path.realpathфункція передбачає dirчастину шляху. os.path.dirname(os.path.realpath(__file__))повертає каталог з файлом. os.path.dirname(os.path.realpath("__file__"))повертає cwd. os.path.dirname(os.path.realpath("here_be_dragons"))також повертає cwd.
Джеймі Булл

86

Оновлення 2018-11-28:

Ось підсумок експериментів із Python 2 та 3. С

main.py - запускає foo.py
foo.py - запускає lib / bar.py
lib / bar.py - друкує вирази filepath

| 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__                               |

Для Python 2 може бути зрозуміліше перейти на пакунки, щоб можна було використовувати from lib import bar- просто додайте порожні __init__.pyфайли до двох папок.

Для Python 3 execfileне існує - найближча альтернатива exec(open(<filename>).read()), хоча це впливає на кадри стека. Це найпростіше у використанні import fooі import lib.bar- __init__.pyфайли не потрібні.

Дивіться також Різниця між імпортом та execfile


Оригінальний відповідь:

Ось експеримент, заснований на відповідях у цій темі - з Python 2.7.10 в Windows.

На основі стека - це єдині, які, здається, дають надійні результати. Останні два мають найкоротший синтаксис , тобто -

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

Ось до них додаються до sys як функції! Кредит @Usagi та @pablog

На основі наступних трьох файлів та запуску main.py зі своєї папки з python main.py(також випробувані execfiles з абсолютними шляхами та викликами з окремої папки).

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

72

Я думаю, що це чистіше:

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

і отримує таку ж інформацію, як:

print inspect.getfile(inspect.currentframe())

Якщо [0] - поточний кадр у стеці (вершина стека), а [1] - це ім'я файлу, збільште, щоб повернутися назад у стеку, тобто

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

буде ім'ям файлу сценарію, який викликав поточний кадр. Також, використовуючи [-1], ви перейдете до нижньої частини стека, оригінального сценарію виклику.


4
Не рекомендуйте покладатися на положення всередині кортежу. Зовсім не зрозуміло, які дані ви намагаєтеся отримати, читаючи код.
jpmc26

5
inspect.getfile()повертає __file__атрибут, якщо переданий об'єкт модуля. inspect.currentframe()повертає модуль. Ерго, це дорогий спосіб сказати __file__.
Martijn Pieters

inspect.stack()є досить дорогою функцією, більше ніж просто inspect.currentframe(), і вона також закликає inspect.getfile()модульний об'єкт.
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

1
Я просто спробував коментар @Pat Notz Я думаю, ви могли просто отримати ім'я файлу __file__.
hlin117

У Python3 os.path.dirnameдасть вам відносний шлях від того, де ви запускаєте сценарій. наприклад , в буде і не абсолютний шлях до сценарію. Я шукав apspath, але видалив назву сценарію. Перший елемент sys.path, мабуть, - це відповідь . python ../../test.pyos.path.dirname../../sys.path[0]
aerijman

37

Пропозиції, позначені як найкращі, справджуються, якщо ваш сценарій складається з одного файлу.

Якщо ви хочете дізнатися ім'я виконуваного файлу (тобто кореневий файл, переданий інтерпретатору python для поточної програми) з файлу, який може бути імпортований як модуль, вам потрібно зробити це (припустимо, це у файлі з ім'ям foo.py ):

import inspect

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

Тому що остання річ ( [-1]) у стеці - це перше, що увійшло в нього (стеки - це структури даних LIFO / FILO).

Потім у файлі bar.py, якщо ви import fooбудете друкувати bar.py , а не foo.py , яке було б значенням для всіх цих:

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

Це добре працює, але не для одиничного тесту на затемнення, я отримав /home/user/Softwares/eclipse/plugins/org.python.pydev_4.5.5.201603221110/pysrc
hayj

2
Це sys.modules['__main__'].__file__справді дорогий спосіб написання .
Martijn Pieters

14
import os
print os.path.basename(__file__)

це дасть нам лише ім'я файлу. тобто якщо abspath файлу є c: \ abcd \ abc.py, тоді другий рядок надрукує abc.py


14

Не зовсім зрозуміло, що ви маєте на увазі під "файловим файлом, який зараз працює в процесі". sys.argv[0]зазвичай містить розташування скрипту, на який викликався інтерпретатор Python. Перегляньте документацію на sys для отримання більш детальної інформації.

Як вказували @Tim та @Pat Notz, атрибут __file__ забезпечує доступ до

файл, з якого завантажений модуль, якщо він завантажений з файлу


1
"print os.path.abspath ( файл )" і "print inspect.getfile (inspect.currentframe ())" не працює, коли ми передаємо програму python програмі win32 exe. Працює лише sys.argv [0]! :) але ви тільки отримаєте ім'я!
aF.

11

У мене є сценарій, який повинен працювати в середовищі Windows. Цей фрагмент коду - це те, що я закінчив:

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

це досить хакітне рішення. Але для цього не потрібні зовнішні бібліотеки, і це найважливіше в моєму випадку.


1
Я повинен був "імпортувати os, sys" для цього, але поки що це найкраща відповідь, яка насправді повертає лише шлях без імені файлу в кінці рядка.
еммаграс


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

Не потрібно оглядати чи будь-яку іншу бібліотеку.

Це працювало для мене, коли мені довелося імпортувати скрипт (з іншого каталогу, а потім виконаного сценарію), який використовував файл конфігурації, що знаходиться в тій же папці, що і імпортований.


Це не дасть бажаної відповіді, якщо поточна робоча директорія (os.getcwd) відрізняється від каталогу, в якому знаходиться файл.
Залізна подушка

2
Як так? Для мене це добре працює в цьому випадку. Я отримую каталог, у якому знаходиться файл.
Майкл Міор

@IronPillow, можливо, ця відповідь допоможе тобі, що потрібно. stackoverflow.com/a/41546830/3123191
Kwuite


6
import sys

print sys.path[0]

це надрукує шлях поточного виконуваного сценарію


2
sys.path [0] дуже корисний, але дає шлях script1, а не script3, як цього вимагали
Джеймс

Принаймні, на OS X, з 2.7, я не вважаю, що це робить щось надійне. Він працює, якщо виконувати безпосередньо той самий файл. Не працює з repl, особливо з імпортного яйця
uchuugaka


4

Можна використовувати 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'

4

Оскільки Python 3 є досить мейнстрімом, я хотів включити pathlibвідповідь, оскільки вважаю, що це, мабуть, зараз кращий інструмент для доступу до інформації про файли та шляхи.

from pathlib import Path

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

Якщо ви шукаєте каталог поточного файлу, це так само просто, як додати .parentдо Path()заяви:

current_path: Path = Path(__file__).parent.resolve()

3
import sys
print sys.argv[0]

10
На жаль, це працює лише в тому випадку, якщо скрипт викликався повним шляхом, тому що він повертає лише "перший аргумент" у командному рядку, який є викликом до сценарію.
Крегокс

2

Це має працювати:

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

2
print(__file__)
print(__import__("pathlib").Path(__file__).parent)

4
Відповіді лише на коди відмовляють. Будь ласка, додайте пояснення, як це вирішує проблему чи чим вона відрізняється від існуючих відповідей. З перегляду
Нік


1

Я завжди тільки використовував функцію os поточного робочого каталогу або CWD. Це частина стандартної бібліотеки і дуже проста у виконанні. Ось приклад:

    import os
    base_directory = os.getcwd()

1
Чи можете ви змінити свою відповідь так, щоб вона стосувалася поставленого питання? Ця відповідь не стосується питання "Як отримати шлях та ім'я файлу, який зараз виконується? "
Марко

1
Це дає шлях, звідки ви запустили команду python. Так що, здається, працює, коли ви запускаєте python script.pyкоманду в тій самій папці, що ви вже є, коли насправді це те саме, що працювати pwdна Linux.
Джордж

0

Я використовував підхід з __file__,
os.path.abspath(__file__)
але є невелика хитрість, він повертає файл .py при першому запуску коду, наступні запуски дають ім’я файлу * .pyc,
щоб я залишився з:
inspect.getfile(inspect.currentframe())
або
sys._getframe().f_code.co_filename


0

Я написав функцію, яка враховує налагоджувач eclipse і unittest . Він повертає папку першого запущеного сценарію. Ви можете необов'язково вказати var __file__ , але головне, що вам не доведеться ділитися цією змінною у всій ієрархії викликів .

Можливо, ви можете впоратися з іншими стеками конкретних справ, яких я не бачив, але для мене це нормально.

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_))

0

Щоб зберегти послідовність міграції на платформах (macOS / Windows / Linux), спробуйте:

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


2
Це неправильно. Це дає вам поточний шлях, але не шлях поточного файлу.
Чарльз Плегер

0

Найпростіший спосіб:

у script_1.py:

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

у script_2.py:

sys.argv[0]

PS: Я спробував execfile, але оскільки він читає script_2.py як рядок, sys.argv[0]повернувся <string>.


0

Ось що я використовую, щоб я міг кинути свій код куди завгодно без проблем. __name__завжди визначається, але __file__визначається лише тоді, коли код запускається як файл (наприклад, не в IDLE / iPython).

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

Крім того, це можна записати так:

self_name = globals().get('__file__', locals().get('__file__', __name__))

-2

Більшість цих відповідей були написані у версії 2.x або пізнішої програми Python. У Python 3.x синтаксис функції друку змінився та вимагає дужок, тобто print ().

Отже, ця відповідь, отримана раніше, від користувача13993 в 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

Стає в Python 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

-3

якщо ви хочете просто назву файлу без, ./або .pyви можете спробувати це

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

file_name надрукує тестовий сценарій, ви можете генерувати все, що завгодно, змінивши індекс всередині []


-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)

7
os.getcwd () повертає поточний каталог РОБОТИ ПРОЦЕСУ, а не каталог файлу, що виконується в даний час. Це може призвести до повернення правильних результатів, але є маса випадків, коли результатом не буде батьківський каталог поточного файлу. Набагато краще використовувати os.path.dirname ( файл ), як було запропоновано вище.
самаспін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.