वर्तमान स्क्रिप्ट निर्देशिका को ठीक से कैसे निर्धारित करें?


260

मैं यह देखना चाहूंगा कि अजगर में वर्तमान स्क्रिप्ट निर्देशिका को निर्धारित करने का सबसे अच्छा तरीका क्या है?

मैंने पाया कि अजगर कोड को कॉल करने के कई तरीकों के कारण, एक अच्छा समाधान खोजना मुश्किल है।

यहाँ कुछ समस्याएं हैं:

  • __file__स्क्रिप्ट के साथ निष्पादित होने पर परिभाषित नहीं किया जाता है exec,execfile
  • __module__ केवल मॉड्यूल में परिभाषित किया गया है

बक्सों का इस्तेमाल करें:

  • ./myfile.py
  • python myfile.py
  • ./somedir/myfile.py
  • python somedir/myfile.py
  • execfile('myfile.py') (किसी अन्य स्क्रिप्ट से, जो किसी अन्य निर्देशिका में स्थित हो सकती है और जिसमें एक और वर्तमान निर्देशिका हो सकती है।

मुझे पता है कि कोई सही समाधान नहीं है, लेकिन मैं सबसे अच्छे दृष्टिकोण की तलाश कर रहा हूं जो अधिकांश मामलों को हल करता है।

सबसे अधिक उपयोग किया जाने वाला तरीका है, os.path.dirname(os.path.abspath(__file__))लेकिन अगर आप स्क्रिप्ट को दूसरे के साथ निष्पादित करते हैं तो यह वास्तव में काम नहीं करता है exec()

चेतावनी

वर्तमान निर्देशिका का उपयोग करने वाला कोई भी समाधान विफल हो जाएगा, यह स्क्रिप्ट को कॉल करने के तरीके के आधार पर भिन्न हो सकता है या इसे रनिंग स्क्रिप्ट के अंदर बदला जा सकता है।


1
क्या आप अधिक विशिष्ट हो सकते हैं जहां आपको यह जानना होगा कि फाइल कहां से आती है? - फ़ाइल आयात करने वाले कोड में (शामिल-जागरूक होस्ट) या आयातित फ़ाइल फ़ाइल में? (आत्म-जागरूक दास)
सिंथेसाइज़रेल

3
pathlibयदि आप अजगर 3.4 या उच्चतर का उपयोग कर रहे हैं तो Ron Kalian का समाधान देखें : stackoverflow.com/a/48931294/1011724
Dan

तो समाधान कोड में किसी भी वर्तमान निर्देशिका का उपयोग करने के लिए नहीं है, लेकिन कुछ कॉन्फ़िगर फ़ाइल का उपयोग करने के लिए?
झाओगंग

दिलचस्प खोज, मैंने अभी बनाया: जब python myfile.pyएक शेल से किया जाता है, तो यह काम करता है, लेकिन दोनों के भीतर :!python %और विम के साथ सिस्टम को निर्दिष्ट पथ नहीं मिल सकता है। यह काफी कष्टप्रद है। क्या कोई इसके पीछे के कारण और संभावित कार्यदक्षता पर टिप्पणी कर सकता है? :!python myfile.py
inVader

जवाबों:


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

वास्तव में सबसे अच्छा आप प्राप्त करने जा रहे हैं।

exec/ के साथ स्क्रिप्ट निष्पादित करना असामान्य है execfile; आमतौर पर आपको स्क्रिप्ट को लोड करने के लिए मॉड्यूल के बुनियादी ढांचे का उपयोग करना चाहिए। यदि आपको इन विधियों का उपयोग करना चाहिए, तो मेरा सुझाव __file__है कि globalsआप स्क्रिप्ट को पास करें ताकि यह फ़ाइल नाम पढ़ सके।

निष्पादित कोड में फ़ाइल नाम प्राप्त करने का कोई अन्य तरीका नहीं है: जैसा कि आप ध्यान दें, CWD पूरी तरह से अलग जगह पर हो सकता है।


2
नेवर से नेवर? इस के अनुसार: stackoverflow.com/a/18489147 एक क्रॉस-प्लेटफ़ॉर्म समाधान का जवाब है abspath (getourcefile (lambda: 0)))? या कुछ और है जो मुझे याद आ रहा है?
जेफ एलेन

131

यदि आप वास्तव में उस मामले को कवर करना चाहते हैं जिसे स्क्रिप्ट के माध्यम से कहा जाता है execfile(...), तो आप inspectफ़ाइलनाम (पथ सहित) को कम करने के लिए मॉड्यूल का उपयोग कर सकते हैं । जहां तक ​​मुझे जानकारी है, यह आपके द्वारा सूचीबद्ध सभी मामलों के लिए काम करेगा:

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

4
मुझे लगता है कि यह वास्तव में सबसे मजबूत तरीका है, लेकिन मैं इसके लिए ओपी की बताई गई जरूरत पर सवाल उठाता हूं। मैं अक्सर डेवलपर्स को ऐसा करते हुए देखता हूं जब वे निष्पादन मॉड्यूल के सापेक्ष स्थानों में डेटा फ़ाइलों का उपयोग कर रहे हैं, लेकिन आईएमओ डेटा फ़ाइलों को एक ज्ञात स्थान पर रखा जाना चाहिए।
रयान जिंस्ट्रोम

14
@ रियान एलओएल, अगर आप एक "ज्ञात स्थान" को परिभाषित करने में सक्षम होंगे तो यह बहुत अच्छा होगा जो कि मल्टीप्लायर है और यह मॉड्यूल के साथ भी आता है। मैं शर्त लगाने के लिए तैयार हूं कि एकमात्र सुरक्षित स्थान स्क्रिप्ट स्थान है। ध्यान दें, यह मांस नहीं है कि स्क्रिप्ट को इस स्थान पर लिखना चाहिए, लेकिन डेटा पढ़ने के लिए यह सुरक्षित है।
सोरिन

1
फिर भी, समाधान अच्छा नहीं है, बस chdir()फ़ंक्शन से पहले कॉल करने का प्रयास करें , यह परिणाम बदल देगा। एक और निर्देशिका से अजगर स्क्रिप्ट को कॉल करने से परिणाम बदल जाएगा, इसलिए यह एक अच्छा समाधान नहीं है।
सोरिन

2
os.path.expanduser("~")उपयोगकर्ता की निर्देशिका प्राप्त करने के लिए एक क्रॉस-प्लेटफ़ॉर्म तरीका है। दुर्भाग्य से, यह जहां अनुप्रयोग डेटा छड़ी करने के लिए विंडोज सबसे अच्छा अभ्यास नहीं है।
रयान जिंस्ट्रॉम

6
@ सोरिन: मैंने chdir()स्क्रिप्ट चलाने से पहले कोशिश की है ; यह सही परिणाम पैदा करता है। मैंने स्क्रिप्ट को किसी अन्य निर्देशिका से कॉल करने का प्रयास किया है और यह काम भी करती है। परिणाम- inspect.getabsfile()आधारित समाधान के समान हैं
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 पर काम करता है। यह काम करता है यदि स्क्रिप्ट का उपयोग करके निष्पादित किया जाता है execfile()( sys.argv[0]और- __file__आधारित समाधान यहां विफल हो जाएगा)। यह काम करता है अगर स्क्रिप्ट एक निष्पादन योग्य ज़िप फ़ाइल (/ एक अंडा) के अंदर है । यह काम करता है अगर PYTHONPATH=/path/to/library.zip python -mscript_to_runएक ज़िप फ़ाइल से स्क्रिप्ट "आयातित" ( ) है; यह इस मामले में संग्रह पथ देता है। यह काम करता है अगर स्क्रिप्ट एक स्टैंडअलोन निष्पादन योग्य ( sys.frozen) में संकलित है । यह सिमिलिंक ( realpathप्रतीकात्मक लिंक को समाप्त) के लिए काम करता है । यह एक इंटरैक्टिव दुभाषिया में काम करता है; यह इस मामले में वर्तमान कामकाजी निर्देशिका लौटाता है।


PyInstaller के साथ पूरी तरह से अच्छी तरह से काम करता है।
विस्‍तृत

1
क्या कोई कारण है कि प्रलेखनgetabsfile(..) में उल्लेख नहीं किया गया हैinspect ? यह उस स्रोत से प्रकट होता है जो उस पृष्ठ से जुड़ा हुआ है।
इवगेनी सर्गेव

@EvgeniSergeev यह एक बग हो सकता है। यह एक साधारण आवरण है getsourcefile(), getfile()जो प्रलेखित है।
jfs

24

पायथन 3.4+ में आप सरल pathlibमॉड्यूल का उपयोग कर सकते हैं :

from inspect import currentframe, getframeinfo
from pathlib import Path

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

2
बहुत ही सरलता!
Cometsong

3
आप शायद उपयोग कर सकते हैं Path(__file__)( inspectमॉड्यूल की कोई आवश्यकता नहीं )।
पेक

@Peque करंट फाइल के नाम सहित एक पथ बनाता है, न कि पैरेंट डायरेक्टरी। मैं एक ही निर्देशिका में फाइल करने के लिए बात करने के लिए आदेश में वर्तमान स्क्रिप्ट निर्देशिका पाने के लिए कोशिश कर रहा हूँ, तो स्क्रिप्ट के रूप में एक ही निर्देशिका में कॉन्फ़िग फ़ाइल को लोड करने की उम्मीद कर जैसे, Path(__file__)देता है /path/to/script/currentscript.pyजब ओपी प्राप्त करना चाहता था/path/to/script/
दावोस

8
ओह, मुझे गलत समझ में आया, आपका मतलब है कि इंस्पेक्ट मॉड्यूल से बचना चाहिए और बस कुछ ऐसा इस्तेमाल करना चाहिए जो parent = Path(__file__).resolve().parent बहुत अच्छा हो।
दावोस

3
@ ड। A. आपको इसके लिए .joinpath()(या /ऑपरेटर) का उपयोग करना चाहिए , नहीं +
यूजीन यर्मश

13

os.path...दृष्टिकोण 'किया बात' अजगर 2 में था।

पायथन 3 में, आप स्क्रिप्ट की निर्देशिका निम्नानुसार पा सकते हैं:

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

11
या बस Path(__file__).parent। लेकिन cwdएक मिथ्या नाम है, यह वर्तमान कार्य निर्देशिका नहीं है , लेकिन फ़ाइल की निर्देशिका है । वे समान हो सकते हैं, लेकिन आमतौर पर ऐसा नहीं है।
नूनो एंड्रे

5

बस उपयोग करें os.path.dirname(os.path.abspath(__file__))और बहुत सावधानी से जांच करें कि क्या उस मामले की वास्तविक आवश्यकता है जहां execउपयोग किया जाता है। यह परेशान डिजाइन का संकेत हो सकता है यदि आप मॉड्यूल के रूप में अपनी स्क्रिप्ट का उपयोग करने में सक्षम नहीं हैं।

पायथन # 8 के ज़ेन को ध्यान में रखें , और यदि आप मानते हैं कि उपयोग-मामले के लिए एक अच्छा तर्क है जहां इसके लिए काम करना चाहिए exec, तो कृपया हमें समस्या की पृष्ठभूमि के बारे में कुछ और विवरण बताएं।


2
यदि आप निष्पादन के साथ नहीं चल रहे हैं (तो आप डिबगर संदर्भ को ढीला कर देंगे। नई प्रक्रिया शुरू करने की तुलना में निष्पादन () भी काफी तेज माना जाता है।
सोरिन

@ सोरिन यह एक नई प्रक्रिया को शुरू करने के बनाम निष्पादन का सवाल नहीं है, इसलिए यह एक स्ट्रोमैन तर्क है। यह एक आयात या फ़ंक्शन कॉल का उपयोग करके निष्पादित करने का सवाल है।
विम

4

चाहेंगे

import os
cwd = os.getcwd()

तुम्हें जो करना है करो? मुझे यकीन नहीं है कि आप वास्तव में "वर्तमान स्क्रिप्ट निर्देशिका" से क्या मतलब है। आपके द्वारा दिए गए उपयोग मामलों के लिए अपेक्षित आउटपुट क्या होगा?


3
यह मदद नहीं करेगा। मेरा मानना ​​है कि @bogdan उस स्क्रिप्ट की निर्देशिका की तलाश कर रहा है जो कॉल स्टैक के शीर्ष पर है। यानी उसके सभी मामलों में, उसे उस निर्देशिका को प्रिंट करना चाहिए जहां 'myfile.py' बैठता है। फिर भी आपका तरीका केवल exec('myfile.py')उसी फ़ाइल की डायरेक्टरी को प्रिंट करेगा , जो कॉल करती है , उसी तरह __file__और sys.argv[0]
झांग 18

हाँ, यह समझ में आता है। मैं बस यह सुनिश्चित करना चाहता था कि @bogdan कुछ सरल दिखाई नहीं दे रहा था, और मैं वास्तव में बता नहीं सकता था कि वे क्या चाहते थे।
विल मैककिचेन

3

पहले .. एक जोड़ी गुम-उपयोग के मामले यहां अगर हम गुमनाम कोड को इंजेक्ट करने के तरीकों के बारे में बात कर रहे हैं ..

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

लेकिन, असली सवाल यह है कि आपका लक्ष्य क्या है - क्या आप किसी प्रकार की सुरक्षा को लागू करने की कोशिश कर रहे हैं? या क्या आप सिर्फ लोड किए जा रहे व्हाट्सएप में रुचि रखते हैं।

यदि आप सुरक्षा में रुचि रखते हैं , तो फ़ाइल नाम जिसे निष्पादन / निष्पादन के माध्यम से आयात किया जा रहा है वह असंगत है - आपको rexec का उपयोग करना चाहिए , जो निम्नलिखित प्रदान करता है:

इस मॉड्यूल में RExec वर्ग है, जो r_eval (), r_execfile (), r_exec (), और r_import () विधियों का समर्थन करता है, जो मानक पायथन फ़ंक्शन eval (), execfile () और निष्पादन और आयात विवरणों के प्रतिबंधित संस्करण हैं। इस प्रतिबंधित वातावरण में निष्पादित कोड में केवल सुरक्षित समझे जाने वाले मॉड्यूल और कार्यों तक पहुंच होगी; आप वांछित के रूप में क्षमताओं को जोड़ने या हटाने को हटा सकते हैं।

हालांकि, अगर यह एक अकादमिक खोज का अधिक है .. यहाँ एक जोड़ी नासमझ दृष्टिकोण है कि आप थोड़ा गहरा खुदाई करने में सक्षम हो सकते हैं ..

उदाहरण स्क्रिप्ट:

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

उत्पादन

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

बेशक, यह करने के लिए एक संसाधन-गहन तरीका है, आप अपने सभी कोड को ट्रेस करेंगे। बहुत कुशल नहीं है। लेकिन, मुझे लगता है कि यह एक उपन्यास दृष्टिकोण है क्योंकि यह तब भी काम करना जारी रखता है जब तक आप घोंसले में गहराई तक नहीं जाते। आप 'eval' को ओवरराइड नहीं कर सकते। यद्यपि आप निष्पादन योग्य () को ओवरराइड कर सकते हैं।

ध्यान दें, यह दृष्टिकोण केवल क्रियान्वयन / क्रियान्वयन को कवर करता है, 'आयात' को नहीं। उच्च स्तर के 'मॉड्यूल' लोड हुकिंग के लिए आप sys.path_hooks ( PyMOTW के शिष्टाचार लिखें) का उपयोग करने में सक्षम हो सकते हैं ।

Thats सब मैं अपने सिर के ऊपर से दूर है।


2

यहाँ एक आंशिक समाधान है, अभी भी सभी प्रकाशित लोगों की तुलना में बेहतर है।

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

अब यह कार्य सभी कॉल करेगा लेकिन यदि कोई chdir()वर्तमान निर्देशिका को बदलने के लिए उपयोग करता है, तो यह भी विफल हो जाएगा।

टिप्पणियाँ:

  • sys.argv[0]-cयदि आप स्क्रिप्ट पर अमल करते हैं तो काम नहीं करेंगेpython -c "execfile('path-tester.py')"
  • मैंने https://gist.github.com/1385555 पर एक संपूर्ण परीक्षण प्रकाशित किया और इसे बेहतर बनाने के लिए आपका स्वागत है।

1

यह ज्यादातर मामलों में काम करना चाहिए:

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

5
यह समाधान वर्तमान निर्देशिका का उपयोग करता है और यह प्रश्न में स्पष्ट रूप से कहा गया है कि ऐसा समाधान विफल हो जाएगा।
9

1

उम्मीद है कि यह मदद करता है: - यदि आप किसी स्क्रिप्ट / मॉड्यूल को कहीं से भी चलाते हैं तो आप उस __file__वेरिएबल को एक्सेस कर पाएंगे, जो स्क्रिप्ट के स्थान का प्रतिनिधित्व करने वाला मॉड्यूल वैरिएबल है।

दूसरी ओर, यदि आप दुभाषिया का उपयोग कर रहे हैं, तो आपके पास उस चर तक पहुंच नहीं है, जहां आपको एक नाम मिलेगा NameErrorऔर os.getcwd()यदि आप कहीं और से फ़ाइल चला रहे हैं तो आपको गलत निर्देशिका देगा।

यह समाधान आपको वह देना चाहिए जो आप सभी मामलों में देख रहे हैं:

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

मैंने इसका पूरी तरह से परीक्षण नहीं किया है, लेकिन इससे मेरी समस्या हल हो गई।


यह फ़ाइल देगा, निर्देशिका नहीं
शीतल शाह
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.