किसी स्क्रिप्ट की शबंग लाइन में मुझे एक से अधिक संभावनाएं कैसे हो सकती हैं?


16

मैं थोड़ी दिलचस्प स्थिति में हूं जहां मेरे पास एक पायथन स्क्रिप्ट है जो सैद्धांतिक रूप से विभिन्न प्रकार के वातावरण (और PATH) और विभिन्न लिनक्स सिस्टमों के साथ विभिन्न उपयोगकर्ताओं द्वारा चलाया जा सकता है। मैं चाहता हूं कि यह स्क्रिप्ट कृत्रिम प्रतिबंधों के बिना इनमें से कई पर अमल कर सके। यहाँ कुछ ज्ञात सेटअप हैं:

  • अजगर 2.6 प्रणाली पायथन संस्करण है, इसलिए अजगर, अजगर 2, और अजगर 2.6 सभी / usr / bin में मौजूद हैं (और समतुल्य हैं)।
  • पायथन 2.6 सिस्टम पायथन संस्करण है, जैसा कि ऊपर है, लेकिन पायथन 2.7 को python2.7 के रूप में स्थापित किया गया है।
  • पायथन 2.4 सिस्टम पायथन संस्करण है, जो मेरी स्क्रिप्ट का समर्थन नहीं करता है। / Usr / bin में हमारे पास python, python2, और python2.4 हैं जो समकक्ष हैं, और python2.5, जो स्क्रिप्ट समर्थन करती है।

मैं इन तीनों पर समान निष्पादन योग्य स्क्रिप्ट चलाना चाहता हूं। यह अच्छा होगा यदि यह पहले /usr/bin/python2.7 का उपयोग करने की कोशिश करता है, यदि यह मौजूद है, तो /usr/bin/python2.6 पर वापस गिरें, फिर वापस /usr/bin/python2.5 पर जाएं, फिर यदि उनमें से कोई भी मौजूद नहीं था, तो बस त्रुटि। जब तक यह संभव है, तब तक मैं सबसे हाल ही में 2.x का उपयोग करके इसे लटका नहीं सकता, हालांकि जब तक यह सही व्याख्याकारों में से एक को खोजने में सक्षम नहीं है।

मेरा पहला झुकाव शेबंग लाइन को इससे बदलना था:

#!/usr/bin/python

सेवा

#!/usr/bin/python2.[5-7]

चूँकि यह बैश में ठीक काम करता है। लेकिन स्क्रिप्ट चलाना देता है:

/usr/bin/python2.[5-7]: bad interpreter: No such file or directory

ठीक है, इसलिए मैं निम्नलिखित कोशिश करता हूं, जो भी काम करता है:

#!/bin/bash -c /usr/bin/python2.[5-7]

लेकिन फिर, इस के साथ विफल रहता है:

/bin/bash: - : invalid option

ठीक है, जाहिर है कि मैं एक अलग शेल स्क्रिप्ट लिख सकता हूं जो सही दुभाषिया पाता है और जो भी दुभाषिया मिला है उसका उपयोग करके अजगर स्क्रिप्ट चलाता है। मैं इसे केवल दो फ़ाइलों को वितरित करने के लिए एक परेशानी पाऊँगा जहाँ किसी को सबसे अधिक समय तक चलने वाले पायथन 2 इंटरप्रेटर के साथ चलना चाहिए। लोगों से दुभाषिया को स्पष्ट रूप से लागू करने के लिए $ python2.5 script.pyकहना (उदाहरण के लिए ) एक विकल्प नहीं है। उपयोगकर्ता के पेट पर एक निश्चित तरीके से स्थापित होना भी एक विकल्प नहीं है।

संपादित करें:

पायथन लिपि के भीतर संस्करण की जाँच से काम चलने वाला नहीं है क्योंकि मैं "विथ" कथन का उपयोग कर रहा हूँ जो कि पायथन 2.6 के रूप में मौजूद है (और इसका उपयोग 2.5 के साथ किया जा सकता है from __future__ import with_statement)। इसका कारण यह है कि स्क्रिप्ट उपयोगकर्ता-अनफ्रेंडली सिंटेक्सट्राय के साथ तुरंत विफल हो जाती है, और मुझे पहले संस्करण की जांच करने और एक उपयुक्त त्रुटि का मौका देने से कभी भी रोकती है।

उदाहरण: (इसे पायथन इंटरप्रेटर के साथ 2.6 से कम करके देखें)

#!/usr/bin/env python

import sys

print "You'll never see this!"
sys.exit()

with open('/dev/null', 'w') as out:
    out.write('something')

वास्तव में आप क्या चाहते हैं, टिप्पणी नहीं। लेकिन आप यह import sys; sys.version_info()जांचने के लिए उपयोग कर सकते हैं कि उपयोगकर्ता के पास आवश्यक अजगर संस्करण है या नहीं।
बर्नहार्ड

2
@ बर्नहार्ड हाँ यह सच है, लेकिन तब तक इसके बारे में कुछ भी करने के लिए बहुत देर हो चुकी है। तीसरी स्थिति के लिए जो मैंने स्क्रिप्ट को सीधे चलाने के ऊपर सूचीबद्ध किया था (यानी, ./script.py) इसे निष्पादित करने के लिए python2.4 का कारण बनेगा, जो आपके कोड को यह पता लगाने का कारण होगा कि यह गलत संस्करण था (और संभवतः, छोड़ दिया गया)। लेकिन वहाँ एक अच्छा python2.5 है कि दुभाषिया के रूप में इस्तेमाल किया जा सकता है!
user108471

2
एक उपयुक्त अजगर और execयह पता लगाने के लिए एक आवरण स्क्रिप्ट का उपयोग करें कि यदि ऐसा है, तो अन्यथा एक त्रुटि प्रिंट करें।
केविन

1
तो इसे पहले उसी फ़ाइल में करें।
केविन

9
@ user108471: आप मान रहे हैं कि शेबिंग लाइन बैश द्वारा नियंत्रित की जाती है। ऐसा नहीं है, यह एक सिस्टम कॉल है ( execve)। तर्क स्ट्रिंग शाब्दिक हैं, कोई ग्लोबिंग नहीं, कोई रेगीक्स नहीं। बस। भले ही पहला arg "/ bin / bash" हो और दूसरा विकल्प ("-c ...") उन विकल्पों को शेल द्वारा पार्स नहीं किया जाता है। उन्हें बैश निष्पादन योग्य नहीं सौंपा गया है, यही वजह है कि आपको वे त्रुटियां मिलती हैं। इसके अलावा, शेबंग केवल तभी काम करता है जब यह शुरुआत में हो। तो आप यहाँ भाग्य से बाहर हैं, मुझे डर है (एक स्क्रिप्ट की कमी जो एक अजगर दुभाषिया को ढूंढती है और इसे एक एचआरओ डॉक्टर को खिलाती है, जो एक भयानक गड़बड़ लगता है)।
गोल्डीलॉक्स

जवाबों:


13

मैं विशेषज्ञ नहीं हूं, लेकिन मेरा मानना ​​है कि आपको उस विकल्प को सिस्टम / उपयोगकर्ता के लिए उपयोग करने और छोड़ने के लिए सटीक अजगर संस्करण निर्दिष्ट नहीं करना चाहिए।

इसके अलावा आपको स्क्रिप्ट में अजगर के लिए हार्डकोडिंग पथ के बजाय इसका उपयोग करना चाहिए:

#!/usr/bin/env python

या

#!/usr/bin/env python3 (or python2)

यह सभी संस्करणों में अजगर डॉक्टर द्वारा अनुशंसित है :

एक अच्छा विकल्प आमतौर पर है

#!/usr/bin/env python

जो पूरे पैट में अजगर दुभाषिया के लिए खोज करता है। हालाँकि, कुछ यूनियनों में env कमांड नहीं हो सकती है, इसलिए आपको दुभाषिया पथ के रूप में हार्डकोड / usr / bin / python की आवश्यकता हो सकती है।

विभिन्न वितरणों में विभिन्न स्थानों में अजगर स्थापित किए जा सकते हैं, इसलिए envइसमें खोज करेंगे PATH। यह सभी प्रमुख लिनक्स वितरणों में उपलब्ध होना चाहिए और फ्रीबीएसडी में मुझे जो दिखता है।

स्क्रिप्ट को पायथन के उस संस्करण के साथ निष्पादित किया जाना चाहिए जो आपके पैट में है और जिसे आपके वितरण द्वारा चुना गया है *।

यदि आपकी स्क्रिप्ट 2.4 के अलावा सभी संस्करणों के साथ संगत है, तो आपको बस इसके अंदर की जांच करनी चाहिए अगर यह पायथन 2.4 में चला गया है और कुछ जानकारी प्रिंट करें और बाहर निकलें।

अधिक पढ़ने के लिए

  • यहां आप उदाहरण पा सकते हैं कि विभिन्न प्रणालियों में पायथन को किन स्थानों पर स्थापित किया जा सकता है।
  • यहां आप उपयोग करने के कुछ फायदे और नुकसान पा सकते हैं env
  • यहां आप पाथ हेरफेर और विभिन्न परिणामों के उदाहरण पा सकते हैं।

पाद लेख

* जेंटू में उपकरण कहा जाता है eselect। इसका उपयोग करते हुए आप विभिन्न अनुप्रयोगों के डिफ़ॉल्ट संस्करणों (जैसे पायथन सहित) को डिफ़ॉल्ट के रूप में सेट कर सकते हैं:

$ eselect python list
Available Python interpreters:
  [1]   python2.6
  [2]   python2.7 *
  [3]   python3.2
$ sudo eselect python set 1
$ eselect python list
Available Python interpreters:
  [1]   python2.6 *
  [2]   python2.7
  [3]   python3.2

2
मैं सराहना करता हूं कि मैं जो करना चाहता हूं वह अच्छा व्यवहार माना जाएगा। आपने जो पोस्ट किया है वह पूरी तरह से उचित है, लेकिन साथ ही यह वह नहीं है जो मैं पूछ रहा हूं। मैं नहीं चाहता कि मेरे उपयोगकर्ता पायथन के उपयुक्त संस्करण में अपनी स्क्रिप्ट को स्पष्ट रूप से इंगित करें जब मुझे उन सभी स्थितियों में पायथन के उपयुक्त संस्करण का पता लगाना पूरी तरह से संभव हो, जिनकी मुझे परवाह है।
user108471

1
कृपया मेरे अपडेट को देखें कि क्यों मैं "इसके अंदर की जांच नहीं कर सकता अगर यह पायथन 2.4 में चलाया जाता है और कुछ जानकारी प्रिंट करें और बाहर निकलें।"
user108471

तुम सही हो। मुझे सिर्फ एसओ पर यह सवाल मिला और अब मैं देख सकता हूं कि ऐसा करने का कोई विकल्प नहीं है कि अगर आप सिर्फ एक फाइल रखना चाहते हैं ...
pbm

9

कुछ टिप्पणियों के कुछ विचारों के आधार पर, मैं काम करने के लिए लगता है कि वास्तव में बदसूरत हैक को एक साथ मिलाने में कामयाब रहा। स्क्रिप्ट एक बैश स्क्रिप्ट बन जाती है, पायथन स्क्रिप्ट को लपेटता है और इसे "यहां दस्तावेज़" के माध्यम से पायथन दुभाषिया के पास भेज देता है।

शुरू में:

#!/bin/bash

''':'
vers=( /usr/bin/python2.[5-7] )
latest="${vers[$((${#vers[@]} - 1))]}"
if !(ls $latest &>/dev/null); then
    echo "ERROR: Python versions < 2.5 not supported"
    exit 1
fi
cat <<'# EOF' | exec $latest - "$@"
''' #'''

अजगर कोड यहाँ जाता है। फिर बहुत अंत में:

# EOF

जब उपयोगकर्ता स्क्रिप्ट चलाता है, तो 2.5 और 2.7 के बीच सबसे हालिया पायथन संस्करण का उपयोग बाकी स्क्रिप्ट को यहां दस्तावेज़ के रूप में व्याख्या करने के लिए किया जाता है।

कुछ शीनिगनों पर एक स्पष्टीकरण:

ट्रिपल-उद्धरण सामग्री जो मैंने जोड़ी है, वह भी इसी स्क्रिप्ट को पायथन मॉड्यूल के रूप में आयात करने की अनुमति देती है (जिसका उपयोग मैं परीक्षण उद्देश्यों के लिए करता हूं)। जब पायथन द्वारा आयात किया जाता है, तो पहले और दूसरे ट्रिपल-सिंगल-उद्धरण के बीच सब कुछ एक मॉड्यूल-स्तरीय स्ट्रिंग के रूप में व्याख्या किया जाता है, और तीसरे ट्रिपल-सिंगल-उद्धरण में टिप्पणी की जाती है। बाकी साधारण पायथन है।

जब सीधे चलाया जाता है (अब बैश स्क्रिप्ट के रूप में), पहले दो सिंगल-कोट्स एक खाली स्ट्रिंग बन जाते हैं, और तीसरे सिंगल-कोट चौथे एकल-उद्धरण के साथ एक और स्ट्रिंग बनाते हैं, जिसमें केवल एक कोलन होता है। इस तार की व्याख्या बैश ने एक नो-ऑप के रूप में की है। बाकी सब कुछ बश सिंटैक्स के लिए / usr / bin में पायथन बायनेरिज़ को ग्लोब करने के लिए, पिछले एक का चयन करने और निष्पादन को चलाने के लिए, शेष फ़ाइल को यहां दस्तावेज़ के रूप में पारित करना है। यहाँ दस्तावेज़ एक पायथन ट्रिपल-सिंगल-उद्धरण के साथ शुरू होता है जिसमें केवल हैश / पाउंड / ऑक्टोथोरैप संकेत होता है। बाकी स्क्रिप्ट तब तक सामान्य मानी जाती है, जब तक कि '# EOF' पढ़ने वाली रेखा यहाँ दस्तावेज़ को समाप्त न कर दे।

मुझे लगता है कि यह विकृत है, इसलिए मुझे आशा है कि किसी के पास बेहतर समाधान होगा।


शायद यह सब के बाद इतना बुरा नहीं है;) +1
गोल्डीलॉक्स

इसका नुकसान यह है कि यह अधिकांश संपादकों पर सिंटैक्स रंग को गड़बड़ कर देगा
रेयान

@ लाइरियन जो निर्भर करता है। मेरी स्क्रिप्ट में .py फ़ाइल नाम एक्सटेंशन का उपयोग किया गया है, जो कि रंग-विन्यास का उपयोग करने के लिए कौन से वाक्यविन्यास का चयन करते समय अधिकांश पाठ संपादक पसंद करते हैं। हाइपोथेटिक रूप से, अगर मैं इसे .py एक्सटेंशन के बिना नाम बदलने के लिए था, तो मैं कुछ के साथ सही सिंटैक्स (कम से कम विम उपयोगकर्ताओं के लिए) को इंगित करने के लिए एक मॉडल का उपयोग कर सकता हूं # ft=python:।
user108471

7

शेबबैंग लाइन केवल एक दुभाषिया के लिए एक निश्चित पथ निर्दिष्ट कर सकती है। वहाँ #!/usr/bin/envदुभाषिया देखने की चाल है, PATHलेकिन यह बात है। यदि आप अधिक परिष्कार चाहते हैं, तो आपको कुछ आवरण खोल कोड लिखने की आवश्यकता होगी।

सबसे स्पष्ट समाधान एक आवरण स्क्रिप्ट लिखना है। अजगर स्क्रिप्ट को कॉल करें foo.realऔर एक आवरण स्क्रिप्ट बनाएं foo:

#!/bin/sh
if type python2 >/dev/null 2>/dev/null; then
  exec python2 "$0.real" "$@"
else
  exec python "$0.real" "$@"
fi

यदि आप एक फ़ाइल में सब कुछ डालना चाहते हैं, तो आप अक्सर इसे एक पॉलीग्लॉट बना सकते हैं जो एक #!/bin/shपंक्ति के साथ शुरू होता है (इसलिए शेल द्वारा निष्पादित किया जाएगा) लेकिन एक अन्य भाषा में एक मान्य स्क्रिप्ट भी है। भाषा के आधार पर, एक बहुवचन असंभव हो सकता है ( #!उदाहरण के लिए एक सिंटैक्स त्रुटि का कारण बनता है)। अजगर में, यह बहुत मुश्किल नहीं है।

#!/bin/sh
''':'
if type python2 >/dev/null 2>/dev/null; then
  exec python2 "$0.real" "$@"
else
  exec python "$0.real" "$@"
fi
'''
# real Python script starts here
def …

(पूरे पाठ के बीच '''और '''पायल में एक पायथन स्ट्रिंग है, जिसका कोई प्रभाव नहीं है। शेल के लिए, दूसरी पंक्ति वह है, ''':'जो उद्धरण छोड़ने के बाद नो-ऑप कमांड है :।)


दूसरा समाधान अच्छा है क्योंकि इसे इस उत्तर की# EOF तरह अंत में जोड़ने की आवश्यकता नहीं है । आपका दृष्टिकोण मूल रूप से यहाँ उल्लिखित के समान है
sschuberth

6

जैसा कि आपकी आवश्यकताओं में बायनेरिज़ की एक ज्ञात सूची है, आप इसे पायथन में निम्नलिखित के साथ कर सकते हैं। यह पायथन के एक अंक के मामूली / प्रमुख संस्करण से आगे नहीं बढ़ेगा, लेकिन मैं यह नहीं देखता कि ऐसा जल्द ही हो।

डिस्क पर स्थित उच्चतम संस्करण को चलाता है, यदि संस्करण बाइनरी पर टैग किया गया है, तो संस्करण की बढ़ती सूची, बाइनरी पर टैग किया गया संस्करण पायथन निष्पादन के वर्तमान संस्करण से अधिक है। "कोड की बढ़ती हुई सूची" इस कोड के लिए महत्वपूर्ण बिट है।

#!/usr/bin/env python
import os, sys

pythons = [ '/usr/bin/python2.3','/usr/bin/python2.4', '/usr/bin/python2.5', '/usr/bin/python2.6', '/usr/bin/python2.7' ]
py = list(filter( os.path.isfile, pythons ))
if py:
  py = py.pop()
  thepy = int( py[-3:-2] + py[-1:] )
  mypy  = int( ''.join( map(str, sys.version_info[0:2]) ) )
  if thepy > mypy:
    print("moving versions to "+py)
    args = sys.argv
    args.insert( 0, sys.argv[0] )
    os.execv( py, args )

print("do normal stuff")

मेरी खरोंच अजगर के लिए क्षमा याचना


यह जारी रखने के बाद जारी नहीं होगा? इतना सामान्य सामान दो बार निष्पादित किया जाएगा?
Janus Troelsen

1
execv एक नए लोडेड प्रोग्राम इमेज के साथ वर्तमान में निष्पादित प्रोग्राम को बदल देता है
Matt

यह एक महान समाधान की तरह दिखता है जो कि मेरे साथ आया से कम बदसूरत महसूस करता है। मुझे यह देखने की कोशिश करनी होगी कि क्या यह इस उद्देश्य के लिए काम करता है।
user108471

यह सुझाव मुझे क्या चाहिए के लिए बहुत काम करता है। एकमात्र दोष कुछ ऐसा है जिसका मैंने मूल रूप से उल्लेख नहीं किया है: पायथन 2.5 का समर्थन करने के लिए, मैं उपयोग करता हूं from __future__ import with_statement, जो कि पायथन लिपि में पहली चीज होनी चाहिए। मुझे नहीं लगता कि नया दुभाषिया शुरू करते समय आप उस क्रिया को करने का एक तरीका जानते हैं?
user108471

क्या आपको यकीन है कि यह होने की जरूरत है बहुत पहली बात यह है? या इससे पहले कि आप withएक सामान्य आयात की तरह किसी भी एस का उपयोग करने की कोशिश करें और ?. क्या if mypy == 25: from __future__ import with_statement'सामान्य सामान' से ठीक पहले एक अतिरिक्त काम होता है? यदि आप 2.4 का समर्थन नहीं करते हैं, तो आपको शायद इसकी आवश्यकता नहीं है।
मैट

0

आप एक छोटी बैश स्क्रिप्ट लिख सकते हैं जो उपलब्ध फाइटॉन एक्जीक्यूटेबल के लिए जाँच करता है और इसे स्क्रिप्ट के साथ पैरामीटर के रूप में कहता है। फिर आप इस स्क्रिप्ट को शबंग लाइन लक्ष्य बना सकते हैं:

#!/my/python/search/script

और यह स्क्रिप्ट बस (खोज के बाद) करती है:

"$python_path" "$1"

मैं अनिश्चित था कि क्या कर्नेल इस स्क्रिप्ट को अप्रत्यक्ष रूप से स्वीकार करेगा लेकिन मैंने जाँच की और यह काम करता है।

संपादित करें 1

इस शर्मनाक अप्रतिष्ठा को आखिरकार एक अच्छा प्रस्ताव बनाने के लिए:

एक फाइल में दोनों स्क्रिप्ट को मिलाना संभव है। आप बस यहाँ स्क्रिप्ट के रूप में पाइथन लिपि को बैश लिपि में लिखते हैं (यदि आप पाइथन लिपि को बदलते हैं तो आपको स्क्रिप्ट को फिर से कॉपी करने की आवश्यकता है)। या तो आप उदा / tmp में एक अस्थायी फ़ाइल बनाते हैं या (यदि अजगर इसका समर्थन करता है, मुझे नहीं पता) आप दुभाषिया को इनपुट के रूप में स्क्रिप्ट प्रदान करते हैं:

# do the search here and then
# either
cat >"tmpfile" <<"EOF" # quoting EOF is important so that bash leaves the python code alone
# here is the python script
EOF
"$python_path" "tmpfile"
# or
"$python_path" <<"EOF"
# here is the python script
EOF

यह कमोबेश समाधान है जो पहले से ही क्यूई दहन के अंतिम पैराग्राफ में बताया गया था!
बर्नहार्ड

चतुर, लेकिन इसके लिए हर सिस्टम पर कहीं न कहीं इस मैजिक स्क्रिप्ट को स्थापित करना आवश्यक है।
user108471

@ बर्नहार्ड Oooops, पकड़ा गया। भविष्य में मैं अंत तक पढ़ूंगा। मुआवजे के रूप में मैं इसे एक फ़ाइल समाधान में सुधारने जा रहा हूं।
हौके लैजिंग

@ user108471 मैजिक स्क्रिप्ट में कुछ इस तरह हो सकता है: $(ls /usr/bin/python?.? | tail -n1 )लेकिन मैं इस चतुराई का उपयोग एक शबंग में करने में सफल नहीं हुआ।
बर्नहार्ड

@ बर्नहार्ड आप शबंग रेखा के भीतर खोज करना चाहते हैं? IIRC कर्नेल को शेबंग लाइन में उद्धृत करने की परवाह नहीं करता है। अन्यथा (अगर यह इस बीच बदल गया है) तो कोई भी ऐसा कर सकता है जैसे `#? / बिन / बैश -c do_search_here_without_whitespace ..., $ python" $ 1 "निष्पादित करें लेकिन व्हाट्सएप के बिना कैसे करें?
हौके लैजिंग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.