स्क्रिप्ट शुरू होने के बाद दुभाषिया चुनें जैसे कि हैशबंग के अंदर


16

क्या स्क्रिप्ट को निष्पादित करने वाले दुभाषिया को गतिशील रूप से चुनने का कोई तरीका है? मेरे पास एक स्क्रिप्ट है जिसे मैं दो अलग-अलग प्रणालियों पर चला रहा हूं, और जिस दुभाषिया का मैं उपयोग करना चाहता हूं वह दो प्रणालियों पर विभिन्न स्थानों में स्थित है। हर बार जब मैं स्विच करता हूं तो मैं हैशबैंग लाइन को बदल देता हूं। मैं ऐसा कुछ करना चाहता हूं जो इस के तार्किक समकक्ष हो (मुझे पता है कि यह सटीक निर्माण असंभव है):

if running on system A:
    #!/path/to/python/on/systemA
elif running on system B:
    #!/path/on/systemB

#Rest of script goes here

या इससे भी बेहतर यह होगा, ताकि यह पहले दुभाषिया का उपयोग करने की कोशिश करे, और अगर यह नहीं मिलता है तो यह दूसरा उपयोग करता है:

try:
    #!/path/to/python/on/systemA
except: 
    #!path/on/systemB

#Rest of script goes here

जाहिर है, मैं इसके बजाय इसे निष्पादित कर सकता हूं /path/to/python/on/systemA myscript.py या /path/on/systemB myscript.py जहां मैं हूं, उसके आधार पर, लेकिन मेरे पास वास्तव में एक आवरण स्क्रिप्ट है जो लॉन्च करता है myscript.py, इसलिए मैं हाथ से करने के बजाय अजगर इंटरप्रेटर को पथ निर्दिष्ट करना चाहूंगा।


3
शेब के बिना दुभाषिया के लिए एक फ़ाइल के रूप में 'बाकी स्क्रिप्ट' पास करना, और ifशर्त का उपयोग करना आपके लिए कोई विकल्प नहीं है? जैसे,if something; then /bin/sh restofscript.sh elif...

यह एक विकल्प है, मैंने भी इस पर विचार किया, लेकिन जितना मैं चाहूंगा, उससे कहीं ज्यादा गड़बड़। चूंकि हैशबैंग लाइन में तर्क असंभव है, मुझे लगता है कि मैं वास्तव में उस मार्ग पर जाऊंगा।
dkv

मुझे अलग-अलग जवाबों की एक विस्तृत श्रृंखला पसंद है जो इस सवाल ने उत्पन्न की है।
Oskar Skog

जवाबों:


27

नहीं, यह काम नहीं करेगा। दो वर्णों #!को फ़ाइल में पहले दो वर्णों की आवश्यकता है (आप यह कैसे निर्दिष्ट करेंगे कि यदि किसी भी तरह इफ-स्टेटमेंट की व्याख्या की गई है)। यह "मैजिक नंबर" का गठन करता है जो exec()फ़ंक्शन के परिवार का पता लगाता है जब वे यह निर्धारित करते हैं कि क्या वे जिस फ़ाइल को निष्पादित करने वाले हैं वह एक स्क्रिप्ट है (जिसे दुभाषिया की आवश्यकता है) या एक बाइनरी फ़ाइल (जो नहीं करता है)।

शेबंग लाइन का प्रारूप काफी सख्त है। यह एक दुभाषिया के लिए एक निरपेक्ष पथ की जरूरत है और इसे करने के लिए एक तर्क है।

उपयोग करने के लिए आप क्या कर सकते हैं env:

#!/usr/bin/env interpreter

अब, आमतौर पर रास्ता envहै , लेकिन तकनीकी रूप से इसकी कोई गारंटी नहीं है। /usr/bin/env

यह आपको PATHप्रत्येक सिस्टम पर पर्यावरण चर को समायोजित करने की अनुमति देता है ताकि interpreter(यह हो bash, pythonया perlआपके पास जो कुछ भी हो) मिल जाए।

इस दृष्टिकोण के साथ एक नकारात्मक पक्ष यह है कि दुभाषिया के लिए एक तर्क को आंशिक रूप से पारित करना असंभव होगा।

इस का मतलब है कि

#!/usr/bin/env awk -f

तथा

#!/usr/bin/env sed -f

कुछ प्रणालियों पर काम करने की संभावना नहीं है।

एक अन्य स्पष्ट दृष्टिकोण है कि इंटरप्रेटर को खोजने के लिए GNU ऑटोटूल (या कुछ सरल टेम्प्लेटिंग सिस्टम) का उपयोग करें और एक ./configureकदम में सही रास्ते को फ़ाइल में रखें , जो प्रत्येक सिस्टम पर स्क्रिप्ट को स्थापित करने पर चलाया जाएगा।

एक स्पष्ट व्याख्याकार के साथ स्क्रिप्ट चलाने का भी सहारा ले सकता है, लेकिन यह स्पष्ट रूप से है कि आप क्या बचने की कोशिश कर रहे हैं:

$ sed -f script.sed

ठीक है, मुझे पता है कि #!शुरुआत में आने की जरूरत है, क्योंकि यह उस प्रक्रिया को खोल नहीं रहा है। मैं सोच रहा था कि क्या हैशबैंग लाइन के अंदर तर्क रखने का कोई तरीका है जो कि इफ / के बराबर होगा। मैं भी अपने साथ खिलवाड़ से बचने की उम्मीद कर रहा था, PATHलेकिन मुझे लगता है कि वे मेरे एकमात्र विकल्प हैं।
dkv

1
जब आप उपयोग करते हैं #!/usr/bin/awk, तो आप बिल्कुल एक तर्क प्रदान कर सकते हैं, जैसा कि #!/usr/bin/awk -f। यदि आप जिस बाइनरी की ओर इशारा कर रहे हैं env, वह तर्क वह बाइनरी है जिसे आप envदेखने के लिए कह रहे हैं , जैसे कि #!/usr/bin/env awk
डोपघोटी

2
@dkv यह नहीं है। यह दो तर्कों के साथ एक दुभाषिया का उपयोग करता है, और यह कुछ प्रणालियों पर काम कर सकता है, लेकिन निश्चित रूप से बिल्कुल नहीं।
Kusalananda

3
@dkv लिनक्स पर यह /usr/bin/envएकल तर्क के साथ चलता है awk -f
इलकाचू

1
@ कुसलानंद, नहीं, यह बात थी। अगर आपके पास कोई स्क्रिप्ट हैfoo.awk हैशबैंग लाइन के साथ#!/usr/bin/env awk -f और इसे ./foo.awkतब लिनक्स पर कॉल करें , जो envदेखता है कि दो पैरामीटर हैं awk -fऔर ./foo.awk। यह वास्तव में /usr/bin/awk -fएक अंतरिक्ष के साथ (आदि) की तलाश में जाता है।
इलकाचु

27

वास्तविक प्रोग्राम के लिए सही दुभाषिया खोजने के लिए आप हमेशा एक रैपर स्क्रिप्ट बना सकते हैं:

#!/bin/bash
if something ; then
    interpreter=this
    script=/some/path/to/program.real
    flags=()
else
    interpreter=that
    script=/other/path/to/program.real
    flags=(-x -y)
fi
exec "$interpreter" "${flags[@]}" "$script" "$@"

आवरण को उपयोगकर्ताओं के PATHरूप में सहेजें programऔर वास्तविक कार्यक्रम को एक तरफ या किसी अन्य नाम के साथ रखें।

मैंने #!/bin/bashहैशबैंग में flagsसरणी के कारण उपयोग किया । यदि आपको झंडे की एक चर संख्या या इस तरह के स्टोर करने की आवश्यकता नहीं है और इसके बिना कर सकते हैं, तो स्क्रिप्ट को आंशिक रूप से काम करना चाहिए #!/bin/sh


2
मैंने देखा exec "$interpreter" "${flags[@]}" "$script" "$@"है कि इस प्रक्रिया का उपयोग पेड़ को साफ रखने के लिए भी किया जाता है। यह बाहर निकलने के कोड का प्रचार भी करता है।
रौज़ेना

@rrauenza, आह हां, स्वाभाविक रूप से exec
इलकाचू १६'१ach को १ach

1
के #!/bin/shबजाय बेहतर नहीं होगा #!/bin/bash? यहां तक ​​कि अगर /bin/shएक अलग शेल के लिए सिमलिंक है, तो यह अधिकांश (यदि सभी नहीं है) * निक्स सिस्टम पर मौजूद होना चाहिए, साथ ही यह स्क्रिप्ट लेखक को बैश में गिरने के बजाय एक पोर्टेबल स्क्रिप्ट बनाने के लिए मजबूर करेगा।
सर्गी कोलोडियाज़नी

@SergiyKolodyazhnyy, हे, मैंने उस पहले का उल्लेख करने के बारे में सोचा था, लेकिन तब नहीं। सरणी के लिए उपयोग किया जाने flagsवाला एक गैर-मानक फ़ीचर है, लेकिन यह पर्याप्त संख्या में झंडे को संग्रहीत करने के लिए उपयोगी है, इसलिए मैंने इसे रखने का फैसला किया।
इलकाचू

या उपयोग / बिन / श और बस दुभाषिया को सीधे प्रत्येक शाखा में कॉल करें script=/what/ever; something && exec this "$script" "$@"; exec that "$script" -x -y "$@":। आप निष्पादन विफलताओं के लिए त्रुटि जाँच भी जोड़ सकते हैं।
jrw32982 मोनिका का

11

आप एक पॉलीग्लॉट (दो भाषाओं को मिलाएं) भी लिख सकते हैं। / बिन / श अस्तित्व की गारंटी है।

यह बदसूरत कोड का नकारात्मक पक्ष है और शायद कुछ /bin/shएस संभावित रूप से भ्रमित हो सकते हैं। लेकिन इसका उपयोग तब किया जा सकता है जब env/ usr / bin / env के अलावा कहीं और मौजूद या मौजूद नहीं होता है। यदि आप कुछ सुंदर फैंसी चयन करना चाहते हैं तो इसका उपयोग भी किया जा सकता है।

स्क्रिप्ट का पहला भाग निर्धारित करता है कि दुभाषिया के रूप में / बिन / श के साथ चलने पर कौन से दुभाषिया का उपयोग करना है, लेकिन सही दुभाषिया द्वारा चलाने पर नजरअंदाज कर दिया जाता है। execपहले भाग से अधिक चलने से शेल को रोकने के लिए उपयोग करें ।

पायथन उदाहरण:

#!/bin/sh
'''
' 2>/dev/null
# Python thinks this is a string, docstring unfortunately.
# The shell has just tried running the <newline> program.
find_best_python ()
{
    for candidate in pypy3 pypy python3 python; do
        if [ -n "$(which $candidate)" ]; then
            echo $candidate
            return
        fi
    done
    echo "Can't find any Python" >/dev/stderr
    exit 1
}
interpreter="$(find_best_python)"   # Replace with something fancier.
# Run the rest of the script
exec "$interpreter" "$0" "$@"
'''

3
मुझे लगता है कि मैंने इनमें से एक को देखा है, लेकिन यह विचार अभी भी उतना ही भयानक है ... लेकिन, आप शायद exec "$interpreter" "$0" "$@"स्क्रिप्ट का नाम ही वास्तविक दुभाषिया भी प्राप्त करना चाहते हैं। (और फिर आशा है कि स्थापना के समय कोई भी झूठ नहीं $0
बोलता

6
स्काला को वास्तव में अपने सिंटैक्स में पॉलीग्लॉट स्क्रिप्ट के लिए समर्थन है: अगर एक स्काला स्क्रिप्ट के साथ शुरू होता है #!, तो स्काला एक मिलान तक सब कुछ अनदेखा करता है !#; यह आपको मनमाने ढंग से जटिल स्क्रिप्ट कोड को वहां की मनमानी भाषा में रखने की अनुमति देता है, और फिर execस्क्रिप्ट के साथ स्काला निष्पादन इंजन।
जोर्ग डब्ल्यू मित्तग


2

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

#!/usr/bin/ruby -e exec "non-existing-interpreter", ARGV[0] rescue exec "python", ARGV[0]

if True:
  print("hello world!")

ध्यान दें कि आप केवल ऐसा कर सकते हैं जब दुभाषिया पहले तर्क में लिखने की अनुमति देता है। यहाँ,-e और इसके बाद की हर चीज़ को माणिक के तर्क के रूप में शब्दशः लिया जाता है। जहां तक ​​मैं बता सकता हूं, आप शेबंग कोड के लिए बैश का उपयोग नहीं कर सकते, क्योंकि bash -cकोड को एक अलग तर्क में होना चाहिए।

मैंने शेबिंग कोड के लिए अजगर के साथ ऐसा करने की कोशिश की:

#!/usr/bin/python -cexec("import sys,os\ntry: os.execlp('non-existing-interpreter', 'non-existing-interpreter', sys.argv[1])\nexcept: os.execlp('ruby', 'ruby', sys.argv[1])")

if true
  puts "hello world!"
end

लेकिन यह बहुत लंबा और कम से कम निकला (मेरी मशीन पर) कम से कम 127 पात्रों को चंगुल से काटता है। कृपया के उपयोग का बहाना करेंexec नई सूचियों को डालने लिए क्योंकि अजगर importबिना अनुमति के अनुमति नहीं देता है या नए अंक के बिना एस।

मुझे यकीन नहीं है कि यह कितना पोर्टेबल है, और मैं इसे वितरित किए जाने वाले कोड पर नहीं करूंगा। फिर भी, यह उल्लेखनीय है। हो सकता है कि किसी को यह त्वरित-गंदे डिबगिंग या कुछ के लिए उपयोगी लगेगा।


2

हालांकि यह शेल स्क्रिप्ट के भीतर दुभाषिया का चयन नहीं करता है (यह इसे प्रति मशीन का चयन करता है) यह एक आसान विकल्प है यदि आपके पास उन सभी मशीनों तक प्रशासनिक पहुंच है जो आप स्क्रिप्ट चलाने की कोशिश कर रहे हैं।

वांछित दुभाषिया पथ को इंगित करने के लिए एक सिम्लिंक (या एक हार्डलिंक यदि वांछित हो) बनाएं। उदाहरण के लिए, मेरे सिस्टम पर पर्ल और अजगर / usr / बिन में हैं:

cd /bin
ln -s /usr/bin/perl perl
ln -s /usr/bin/python python

हैशबैंग को / बिन / पर्ल आदि के लिए हल करने की अनुमति देने के लिए एक सिमलिंक बनाना होगा। यह स्क्रिप्ट के लिए मापदंडों को पारित करने की क्षमता भी रखता है।


1
+1 यह इतना सरल है। जैसा कि आप ध्यान दें, यह सवाल का बहुत जवाब नहीं देता है, लेकिन यह वही करता है जो ओपी चाहता है। हालांकि मुझे लगता है कि प्रत्येक मशीन के मुद्दे पर एनवी का उपयोग रूट एक्सेस के आसपास होता है।
जो

0

मुझे आज भी इसी तरह की समस्या का सामना करना पड़ा था ( python3एक सिस्टम पर बहुत पुराने अजगर के संस्करण की ओर इशारा करते हुए), और एक दृष्टिकोण के साथ आया था जो यहां चर्चा किए गए लोगों से थोड़ा अलग है: "गलत" संस्करण का उपयोग करें अजगर "सही" में बूटस्ट्रैप करने के लिए। सीमा यह है कि अजगर के कुछ संस्करण को भरोसेमंद रूप से पुनः प्राप्त करने की आवश्यकता है, लेकिन यह आमतौर पर उदाहरण के लिए प्राप्त किया जा सकता है #!/usr/bin/env python3

इसलिए मैं क्या करूँ:

#!/usr/bin/env python3
import sys
import os

# On one of our systems, python3 is pointing to python3.3
# which is too old for our purposes. 'Upgrade' if needed
if sys.version_info[1] < 4:
    for py_version in ['python3.7', 'python3.6', 'python3.5', 'python3.4']:
        try:
            os.execlp(py_version, py_version, *sys.argv)
        except:
            pass # Deliberately ignore errors, pick first available version

यह क्या है:

  • कुछ स्वीकृति मानदंड के लिए दुभाषिया संस्करण की जाँच करें
  • यदि स्वीकार्य नहीं है, तो उम्मीदवार संस्करणों की एक सूची के माध्यम से जाएं, और इनमें से जो पहले उपलब्ध है, उसके साथ फिर से निष्पादित करें
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.