Geçerli komut dizini nasıl düzgün belirlenir?


261

Python geçerli komut dizini belirlemek için en iyi yolu nedir görmek istiyorum?

Python kodunu çağırmanın birçok yolu nedeniyle, iyi bir çözüm bulmak zor olduğunu keşfettim.

İşte bazı sorunlar:

  • __file__komut dosyası yürütülürse tanımlanmaz exec,execfile
  • __module__ yalnızca modüllerde tanımlanır

Kullanım örnekleri:

  • ./myfile.py
  • python myfile.py
  • ./somedir/myfile.py
  • python somedir/myfile.py
  • execfile('myfile.py') (başka bir dizinde bulunup başka bir dizinde bulunabilir ve başka bir geçerli dizine sahip olabilir.

Mükemmel bir çözüm olmadığını biliyorum, ancak vakaların çoğunu çözen en iyi yaklaşımı arıyorum.

En çok kullanılan yaklaşım, os.path.dirname(os.path.abspath(__file__))ancak betiği başka bir komuttan çalıştırırsanız gerçekten işe yaramaz exec().

Uyarı

Geçerli dizini kullanan herhangi bir çözüm başarısız olur, bu komut dosyasının çağrılma biçimine göre farklı olabilir veya çalışan komut dosyasının içinde değiştirilebilir.


1
Dosyanın nereden geldiğini bilmeniz gereken daha spesifik olabilir misiniz? - dosyayı içe aktarılan kodda (içerme farkında ana bilgisayar) veya içe aktarılan dosyada? (kendini tanıyan köle)
synthesizerpatel

3
pathlibPython 3.4 veya daha yenisini kullanıyorsanız Ron Kalian'ın çözümüne bakın : stackoverflow.com/a/48931294/1011724
Dan

Yani çözüm kod herhangi bir geçerli dizini kullanmak değil, bazı yapılandırma dosyası kullanmaktır?
ZhaoGang

İlginç bir keşif, yeni yaptım: Bir python myfile.pykabuktan yaparken , işe yarıyor, ancak vim içinden :!python %ve :!python myfile.pyiçinden başarısız oluyor Sistem belirtilen yolu bulamıyor. Bu oldukça can sıkıcı. Bunun arkasındaki neden ve olası geçici çözümler hakkında yorum yapan var mı?
inVader

Yanıtlar:


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

gerçekten elde edeceğiniz en iyi şey.

exec/ İle bir betik yürütmek olağandışıdır execfile. normalde komut dosyalarını yüklemek için modül altyapısını kullanmanız gerekir. Eğer bu yöntemleri kullanmanız gerekiyorsa, ben ayarı önermek __file__de globalso dosya adını okuyabilir böylece komut dosyasına geçmektedir.

Yürütülen kodda dosya adını almanın başka bir yolu yoktur: not ettiğiniz gibi CWD tamamen farklı bir yerde olabilir.


2
Asla asla Deme? Buna göre: stackoverflow.com/a/18489147 Bir çapraz platform çözümünün yoludur (getsourcefile (lambda: 0))? Yoksa özlediğim başka bir şey var mı?
Jeff Ellen

131

Bir komut dosyasının çağrıldığı durumu gerçekten kapsamak istiyorsanız , dosya adını (yol dahil) çıkarmak için modülü execfile(...)kullanabilirsiniz inspect. Bildiğim kadarıyla, bu, listelediğiniz tüm durumlar için işe yarayacaktır:

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

4
Bence bu gerçekten en sağlam yöntem, ama OP'nin buna olan ihtiyacını sorguluyorum. Genellikle geliştiricilerin bunu yürütme modülüne göre konumlarda veri dosyaları kullandıklarında görüyorum, ancak IMO veri dosyaları bilinen bir konuma yerleştirilmelidir.
Ryan Ginstrom

14
@Ryan LOL, eğer çok platformlu ve modülle birlikte gelen bir "bilinen konum" tanımlayabilseydiniz harika olurdu. Tek güvenli konumun komut dosyası konumu olduğuna bahse girmeye hazırım. Bu, betiğin bu konuma yazması gerektiği anlamına gelmez, ancak veri okumak için güvenlidir.
sorin

1
Yine de, çözüm iyi değil, sadece chdir()fonksiyondan önce aramayı deneyin , sonucu değiştirecektir. Ayrıca başka bir dizinden python komut dosyasını çağırmak sonucu değiştirir, bu yüzden iyi bir çözüm değildir.
sorin

2
os.path.expanduser("~")kullanıcının dizinini almanın platformlar arası bir yoludur. Ne yazık ki, bu uygulama verilerinin nereye yapıştırılacağı konusunda en iyi uygulama değildir.
Ryan Ginstrom

6
@sorin: chdir()Senaryoyu çalıştırmadan önce denedim ; doğru sonuç verir. Komut dosyasını başka bir dizinden çağırmayı denedim ve aynı zamanda çalışıyor. Sonuçlar inspect.getabsfile()tabanlı çözümle aynıdır .
jfs

43
#!/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 üzerinde çalışır. Komut dosyası kullanılarak yürütülürse çalışır execfile()( sys.argv[0]ve __file__tabanlı çözümler burada başarısız olur). Komut dosyası yürütülebilir bir zip dosyasının (/ yumurta) içinde çalışıyorsa çalışır . Komut dosyası PYTHONPATH=/path/to/library.zip python -mscript_to_runbir zip dosyasından "içe aktarılmışsa" ( ) çalışır ; bu durumda arşiv yolunu döndürür. Komut dosyası bağımsız bir yürütülebilir dosya ( sys.frozen) halinde derlenmişse çalışır . Simgeler için çalışır ( realpathsembolik bağlantıları ortadan kaldırır). Etkileşimli bir tercümanda çalışır; bu durumda geçerli çalışma dizinini döndürür.


PyInstaller ile mükemmel çalışır.
gaborous

1
Belgelerdegetabsfile(..) belirtilmemesinin bir nedeni var mı ? Bu sayfadan bağlanan kaynakta görünür. inspect
Evgeni Sergeev

@EvgeniSergeev bir hata olabilir. Bu etrafında basit bir sarıcı getsourcefile(), getfile()belgelenir.
jfs

24

Python 3.4+ sürümünde daha basit pathlibmodülü kullanabilirsiniz :

from inspect import currentframe, getframeinfo
from pathlib import Path

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

2
Mükemmel sadelik!
Şubat'ta Cometsong

3
Muhtemelen kullanabilirsiniz Path(__file__)( inspectmodüle gerek yoktur ).
Peque

@Peque, üst dizini değil, geçerli dosyanın adını içeren bir yol üretir. Aynı dizindeki bir dosyaya işaret etmek için geçerli komut dizini almaya çalışıyorum, örneğin komut dosyasıyla aynı dizine bir yapılandırma dosyası yüklemeyi bekliyorsanız , OP almak istediğinde Path(__file__)verir/path/to/script/currentscript.py/path/to/script/
Davos

8
Oh yanlış anladım, inceleme modülünden kaçınmak ve sadece parent = Path(__file__).resolve().parent çok daha hoş bir şey kullanmak istiyorsun .
Davos

3
Bunun için .joinpath()(veya /operatörü) kullanmalısınız , kullanmamalısınız +.
Eugene Yarmash

13

os.path...Yaklaşım Python 2 yapılabileceği 'şey' oldu.

Python 3'te kod dizini aşağıdaki gibi bulunabilir:

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

11
Veya sadece Path(__file__).parent. Ancak cwdyanlış bir adlandırmadır, geçerli çalışma dizini değil, dosyanın dizini budur . Aynı olabilirler, ancak genellikle durum böyle değildir.
Nuno André

5

Kullanılan os.path.dirname(os.path.abspath(__file__))kasaya gerçekten ihtiyaç olup olmadığını çok dikkatli bir şekilde kullanın ve inceleyin exec. Senaryonuzu bir modül olarak kullanamıyorsanız, sorunlu tasarımın bir işareti olabilir.

Python # 8'den Zen'i unutmayın ve işe yaraması gereken bir kullanım durumu için iyi bir argüman olduğuna inanıyorsanız exec, lütfen sorunun arka planı hakkında bize daha fazla ayrıntı bildirin.


2
Exec () ile çalışmazsanız hata ayıklayıcı içeriğini kaybedersiniz. Ayrıca exec () 'nin yeni bir işlem başlatmaktan çok daha hızlı olması beklenir.
sorin

@sorin Bu, exec ile yeni bir süreç başlatmak meselesi değil, bu yüzden bu bir strawman argümanı. Bu, içe aktarma veya işlev çağrısı kullanma exec vs sorunudur.
wim

4

Would

import os
cwd = os.getcwd()

ne istiyorsan onu yap? "Mevcut script dizini" ile tam olarak ne demek istediğinizden emin değilim. Verdiğiniz kullanım durumları için beklenen çıktı ne olur?


3
Yardımcı olmaz. @Bogdan çağrı yığınının en üstünde komut dosyası için dizin arıyor inanıyorum. yani tüm durumlarında, 'dosyam.py'ın bulunduğu dizini yazdırmalıdır. Ancak yönteminiz yalnızca ve ile exec('myfile.py')aynı şekilde çağrılan dosyanın dizinini yazdırır . __file__sys.argv[0]
Zhang18

Evet, bu mantıklı. @Bogdan'ın basit bir şeye bakmadığından emin olmak istedim ve tam olarak ne istediklerini söyleyemedim.
Will McCutchen

3

İlk olarak, anonim kod enjekte etmenin yollarından bahsediyorsak, burada birkaç eksik kullanım örneği var.

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

Ama asıl soru şu: Amacınız nedir - bir çeşit güvenliği zorlamaya mı çalışıyorsunuz? Yoksa sadece neyin yüklendiğiyle ilgileniyor musunuz?

Eğer ilgilenen ediyorsanız güvenlik yoluyla ithal edilmesi dosya exec / execfile is önemsiz - kullanmanız gereken rexec , aşağıdaki hangi teklifler:

Bu modül, standart Python işlevleri eval (), execfile () ile exec ve import deyimlerinin sınırlı sürümleri olan r_eval (), r_execfile (), r_exec () ve r_import () yöntemlerini destekleyen RExec sınıfını içerir. Bu kısıtlı ortamda yürütülen kod yalnızca güvenli kabul edilen modüllere ve işlevlere erişebilir; RExec yeteneklerini istediğiniz gibi alt sınıflandırabilir veya kaldırabilirsiniz.

Ancak, bu daha akademik bir arayış ise .. işte size biraz daha derine inebileceğiniz birkaç aptalca yaklaşım var ..

Örnek komut dosyaları:

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

Çıktı

loaded(/Users/synthesizerpatel/deep.py)
>> level 1
loaded(/Users/synthesizerpatel/deeper.py)
    >> level 2
loaded(/Users/synthesizerpatel/<string>)
loaded(/tmp/deepest.py)
        >> level 3
            I can see the earths core.
        << level 3
    << level 2
<< level 1

Tabii ki, bu bunu yapmak için kaynak yoğun bir yoldur, tüm kodunuzu izliyorsunuz .. Çok verimli değil. Ama bence bu yeni bir yaklaşım çünkü yuvaya daha da yaklaştıkça çalışmaya devam ediyor. 'Eval' değerini geçersiz kılamazsınız. Eğer rağmen yapabilirsiniz execfile geçersiz ().

Bu yaklaşımın yalnızca 'import' öğesini değil, yalnızca exec / execfile dosyasını kapsadığını unutmayın. Daha yüksek seviyeli 'modül' yük kancası için sys.path_hooks (PyMOTW'un yazma izniyle) kullanabilirsiniz.

Hepsi bu kadar.


2

İşte kısmi bir çözüm, şimdiye kadar yayınlanmış olanlardan daha iyi.

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

Şimdi bu tüm aramalar çalışır ama birisi chdir()geçerli dizini değiştirmek için kullanırsa , bu da başarısız olur.

Notlar:


1

Bu çoğu durumda işe yarar:

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

5
Bu çözüm geçerli dizini kullanır ve bu çözümün başarısız olacağı sorusunda açıkça belirtilir.
9'da skyking

1

Umarım bu yardımcı olur: - Herhangi bir yerden bir komut dosyası / modül çalıştırırsanız, komut dosyasının __file__konumunu temsil eden bir modül değişkeni olan değişkene erişebilirsiniz .

Eğer tercüman kullanıyorsanız Öte yandan, sen bir isim alırsınız değişkene erişimi yok NameErrorve os.getcwd()başka bir yere dosyayı çalıştırıyorsanız size yanlış dizini verecektir.

Bu çözüm her durumda aradığınızı vermelidir:

from inspect import getsourcefile
from os.path import abspath
abspath(getsourcefile(lambda:0))

İyice test etmedim ama sorunumu çözdü.


Bu, dizin değil dosya verecektir
Shital Shah
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.