शेबंग में कई तर्क


32

मैं सोच रहा हूं कि क्या शबंग रेखा ( #!) के माध्यम से एक निष्पादन योग्य के लिए कई विकल्पों को पारित करने का एक सामान्य तरीका है ।

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

उदाहरण के लिए, मान लीजिए कि मैं एक स्क्रिप्ट लिखना चाहता हूं जिसे bashपॉज़िक्स मोड में निष्पादित करना है । शबंग लिखने का भोला तरीका होगा:

#!/usr/bin/env bash --posix

लेकिन परिणामी स्क्रिप्ट को निष्पादित करने की कोशिश करने से निम्नलिखित त्रुटि उत्पन्न होती है:

/usr/bin/env: ‘bash --posix’: No such file or directory

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


संपादित करें : मुझे पता है कि गिलिकल स्क्रिप्ट्स के लिए, मैं जो चाहता हूं, उसे प्राप्त करने का एक तरीका है, जिसे मैनुअल की धारा 4.3.4 में प्रलेखित किया गया है:

 #!/usr/bin/env sh
 exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
 !#

चाल, यहाँ, यह है कि दूसरी पंक्ति (के साथ शुरू exec) को कोड के रूप में व्याख्या की जाती है sh, लेकिन #!... !#ब्लॉक में, एक टिप्पणी के रूप में, और इस तरह से गुइल दुभाषिया द्वारा अनदेखा किया जाता है।

क्या इस विधि को किसी दुभाषिया के लिए सामान्यीकृत करना संभव नहीं होगा?


दूसरा EDIT : थोड़ा सा खेलने के बाद, ऐसा लगता है कि, दुभाषियों के लिए जो उनके इनपुट को पढ़ सकते हैं stdin, निम्नलिखित विधि काम करेगी:

#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;

यह संभवत: इष्टतम नहीं है, हालांकि, shजब तक यह प्रक्रिया जीवित रहती है, जब तक दुभाषिया अपना काम पूरा नहीं कर लेता। किसी भी प्रतिक्रिया या सुझाव की सराहना की जाएगी।



जवाबों:


27

कोई सामान्य समाधान नहीं है, कम से कम यदि आपको लिनक्स का समर्थन करने की आवश्यकता नहीं है, क्योंकि लिनक्स कर्नेल एक तर्क के रूप में शेबंग लाइन में पहले "शब्द" के बाद सब कुछ मानता है

मुझे यकीन नहीं है कि निक्सोस की अड़चन क्या है, लेकिन आम तौर पर मैं सिर्फ आपका शेबंग लिखूंगा

#!/bin/bash --posix

या, जहां संभव हो, स्क्रिप्ट में विकल्प सेट करें :

set -o posix

वैकल्पिक रूप से, आपके पास उपयुक्त शेल इनवोकेशन के साथ स्क्रिप्ट पुनः आरंभ हो सकती है:

#!/bin/sh -

if [ "$1" != "--really" ]; then exec bash --posix -- "$0" --really "$@"; fi

shift

# Processing continues

इस दृष्टिकोण को अन्य भाषाओं के लिए सामान्यीकृत किया जा सकता है, जब तक कि आप लक्ष्य भाषा द्वारा अनदेखा किए जाने वाले पहले दो पंक्तियों (जिसे शेल द्वारा व्याख्या की जाती है) के लिए एक रास्ता खोजते हैं।

GNU coreutils' envसंस्करण 8.30 के बाद से वर्कअराउंड प्रदान करता है, विवरण के लिए अनोड का उत्तर देखें। (यह डेबियन 10 और बाद में, आरएचईएल 8 और बाद में, उबंटू 19.04 और बाद में, आदि) में उपलब्ध है।


18

हालाँकि बिल्कुल पोर्टेबल नहीं है, कोरुटिल्स 8.30 से शुरू होता है और इसके प्रलेखन के अनुसार आप इसका उपयोग कर पाएंगे:

#!/usr/bin/env -S command arg1 arg2 ...

इसलिए दिया गया:

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too

आपको मिल जायेगा:

% ./test.sh 
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'

और अगर आप उत्सुक हैं showargsतो:

#!/usr/bin/env sh
echo "\$0 is '$0'"

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

भविष्य के संदर्भ के लिए यह जानना बहुत अच्छा है।
जॉन मैकगी 21

उस विकल्प को FreeBSD से कॉपी किया गया था envजहां -S2005 में जोड़ा गया था। देखें lists.gnu.org/r/coreutils/2018-04/msg00011.html
Stéphane Chazelas

फेडोरा 29
एरिक

@unode के कुछ सुधार showargs: pastebin.com/q9m6xr8H और pastebin.com/gS8AQ5WA (एक-लाइनर)
Eric

जानकारी के लिए: coreutils 8.31 के रूप में, envअपने स्वयं के शामिल showargs: -v विकल्प जैसे#!/usr/bin/env -vS --option1 --option2 ...
chocolateboy

9

POSIX मानक वर्णन करने में बहुत ही जटिल है #!:

से के प्रलेखन का तर्क खंड exec()प्रणाली इंटरफेस के परिवार :

एक और तरीका है कि कुछ ऐतिहासिक कार्यान्वयन शेल स्क्रिप्ट को संभालते हैं, फ़ाइल के पहले दो बाइट्स को चरित्र स्ट्रिंग के रूप में पहचानते हैं और फ़ाइल #!के पहले लाइन के शेष का उपयोग करने के लिए कमांड दुभाषिया के नाम के रूप में निष्पादित करते हैं।

से शैल परिचय अनुभाग :

शेल अपने इनपुट को एक फ़ाइल (देखें sh) से, POSIX.1-2008 के सिस्टम इंटरफेसेस वॉल्यूम में परिभाषित -cविकल्प system()और popen()कार्यों से पढ़ता है। यदि शेल कमांड की फ़ाइल की पहली पंक्ति वर्णों से शुरू होती है #!, तो परिणाम अनिर्दिष्ट होते हैं

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

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

इस प्रकार शेबंग लाइन पर भरोसा करना नासमझी है जो एक से अधिक तर्क देने में सक्षम है।

विकिपीडिया पर शेबंग लेख की पोर्टेबिलिटी अनुभाग भी देखें ।


एक आसान समाधान, जो किसी भी उपयोगिता या भाषा के लिए सामान्य है, एक आवरण स्क्रिप्ट बनाना है जो वास्तविक स्क्रिप्ट को उपयुक्त कमांड लाइन तर्कों के साथ निष्पादित करता है:

#!/bin/sh
exec /bin/bash --posix /some/path/realscript "$@"

मुझे नहीं लगता कि मैं व्यक्तिगत रूप से इसे फिर से अमल करने की कोशिश करेंगे ही है कि के रूप में कुछ हद तक कमजोर महसूस करता है।


7

शबांग का वर्णन निम्नानुसार execve(2) मैन पेज में किया गया है:

#! interpreter [optional-arg]

इस सिंटैक्स में दो स्थान स्वीकार किए जाते हैं:

  1. दुभाषिया पथ से पहले एक स्थान , लेकिन यह स्थान वैकल्पिक है।
  2. दुभाषिया पथ और उसके वैकल्पिक तर्क को अलग करने वाला एक स्थान ।

ध्यान दें कि मैंने वैकल्पिक तर्क की बात करते समय बहुवचन का उपयोग नहीं किया था, न ही सिंटैक्स ऊपर उपयोग करता है [optional-arg ...], जैसा कि आप अधिकांश एक एकल तर्क प्रदान कर सकते हैं

जहां तक ​​शेल स्क्रिप्टिंग का संबंध है, आप setअपनी स्क्रिप्ट की शुरुआत के पास अंतर्निहित कमांड का उपयोग कर सकते हैं जो दुभाषियों के मापदंडों को सेट करने की अनुमति देगा, वही परिणाम प्रदान करेगा जैसे कि आपने कमांड-लाइन तर्कों का उपयोग किया था।

आपके मामले में:

set -o posix

बैश प्रॉम्प्ट से, help setसभी उपलब्ध विकल्पों को प्राप्त करने के लिए आउटपुट की जांच करें।


1
आपको दो से अधिक स्थान रखने की अनुमति है, उन्हें केवल वैकल्पिक तर्क का हिस्सा माना जाता है।
स्टीफन किट

@StephenKitt: वास्तव में, यहां सफेद स्थान को वास्तविक अंतरिक्ष चार की तुलना में एक श्रेणी के रूप में अधिक लिया जाना है। मुझे लगता है कि अन्य सफेद स्थानों जैसे कि टैब को भी व्यापक रूप से स्वीकार किया जाना चाहिए।
व्हाइटवॉन्डवॉल्फ

3

लिनक्स पर, शेबंग बहुत लचीला नहीं है; कई जवाबों के अनुसार ( स्टीफन किट का जवाब और जोर्ज डब्ल्यू मित्तग के ), एक शेबिंग लाइन में कई तर्कों को पारित करने का कोई निर्दिष्ट तरीका नहीं है।

मुझे यकीन नहीं है कि यह किसी के लिए भी उपयोगी होगा, लेकिन मैंने कमी की सुविधा को लागू करने के लिए एक छोटी स्क्रिप्ट लिखी है। Https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1aa देखें ।

एम्बेडेड वर्कअराउंड लिखना भी संभव है। Bellow, मैं चार भाषा-अज्ञेयवादी कार्यपट्टियों को एक ही परीक्षण स्क्रिप्ट पर लागू करता हूं और परिणाम प्रत्येक प्रिंट करता है। मुझे लगता है कि स्क्रिप्ट निष्पादन योग्य है और अंदर है /tmp/shebang


प्रक्रिया प्रतिस्थापन के अंदर एक बैश वंशानुगत में अपनी स्क्रिप्ट लपेटकर

जहाँ तक मुझे पता है, यह इसे करने का सबसे विश्वसनीय भाषा-अज्ञेय तरीका है। यह तर्कों को पारित करने और स्टड को संरक्षित करने की अनुमति देता है। दोष यह है कि दुभाषिया उस फ़ाइल के वास्तविक (वास्तविक) स्थान को नहीं जानता जो वह पढ़ता है।

#!/bin/bash
exec python3 -O <(cat << 'EOWRAPPER'
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv
try:
    print("input() 0 ::", input())
    print("input() 1 ::", input())
except EOFError:
    print("input() caused EOFError")
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")
EOWRAPPER
) "$@"

कॉलिंग echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'प्रिंट:

PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /dev/fd/62
argv[1:]  :: ['arg1', 'arg2 contains spaces', 'arg3\\ uses\\ \\\\escapes\\\\']
__debug__ :: False
PYTHON_SCRIPT_END

ध्यान दें कि प्रतिस्थापन की प्रक्रिया एक विशेष फ़ाइल का उत्पादन करती है। यह सभी निष्पादन योग्य के अनुरूप नहीं हो सकता है। उदाहरण के लिए, #!/usr/bin/lessशिकायत करता है:/dev/fd/63 is not a regular file (use -f to see it)

मुझे नहीं पता कि क्या डैश में प्रक्रिया प्रतिस्थापन के अंदर हेरेडोक होना संभव है।


एक साधारण वंशानुगत में अपनी स्क्रिप्ट लपेटना

छोटा और सरल, लेकिन आप stdinअपनी स्क्रिप्ट तक नहीं पहुंच पाएंगे और इसके लिए दुभाषिया को एक स्क्रिप्ट को पढ़ने और निष्पादित करने में सक्षम होना चाहिए stdin

#!/bin/sh
exec python3 - "$@" << 'EOWRAPPER'
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

try:
    print("input() 0 ::", input())
    print("input() 1 ::", input())
except EOFError:
    print("input() caused EOFError")
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")
EOWRAPPER

कॉलिंग echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'प्रिंट:

PYTHON_SCRIPT_BEGINNING
input() caused EOFError
argv[0]   :: -
argv[1:]  :: ['arg1', 'arg2 contains spaces', 'arg3\\ uses\\ \\\\escapes\\\\']
__debug__ :: True
PYTHON_SCRIPT_END

अजीब system()कॉल का उपयोग करें लेकिन तर्कों के बिना

सही ढंग से निष्पादित फ़ाइल का नाम गुजरता है, लेकिन आपकी स्क्रिप्ट आपके द्वारा दिए गए तर्कों को प्राप्त नहीं करेगी। ध्यान दें कि awk एकमात्र ऐसी भाषा है जिसे मैं जानता हूं कि दोनों दुभाषिया डिफ़ॉल्ट रूप से लिनक्स पर स्थापित हैं और इसके निर्देशों को डिफ़ॉल्ट रूप से कमांड लाइन से पढ़ते हैं।

#!/usr/bin/gawk BEGIN {system("python3 -O " ARGV[1])}
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

print("input() 0 ::", input())
print("input() 1 ::", input())
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")

कॉलिंग echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'प्रिंट:

PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /tmp/shebang
argv[1:]  :: []
__debug__ :: False
PYTHON_SCRIPT_END

4.1+ system()कॉल का उपयोग करें, बशर्ते आपके तर्कों में रिक्त स्थान न हों

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

#!/usr/bin/gawk @include "join"; BEGIN {system("python3 -O " join(ARGV, 1, ARGC, " "))}
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

print("input() 0 ::", input())
print("input() 1 ::", input())
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")

कॉलिंग echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'प्रिंट:

PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /tmp/shebang
argv[1:]  :: ['arg1', 'arg2', 'contains', 'spaces', 'arg3 uses \\escapes\\']
__debug__ :: False
PYTHON_SCRIPT_END

4.1 से नीचे के awk संस्करणों के लिए, आपको लूप के लिए स्ट्रिंग कॉन्टेनेशन का उपयोग करना होगा, उदाहरण के लिए कार्य देखें https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html


1
निषेध $variableया `command`प्रतिस्थापन के लिए यहाँ दस्तावेज़ टर्मिनेटर को exec python3 -O <(cat <<'EOWRAPPER'
उद्धृत करें

2

LD_LIBRARY_PATHअजगर #!(शेबंग) लाइन पर अजगर के साथ प्रयोग करने की एक ट्रिक जो शेल के अलावा किसी और चीज पर निर्भर नहीं करती है और ट्रीट का काम करती है:

#!/bin/sh
'''' 2>/dev/null; exec /usr/bin/env LD_LIBRARY_PATH=. python -x "$0" "$@" #'''

__doc__ = 'A great module docstring'

जैसा कि इस पृष्ठ में कहीं और समझाया गया है जैसे कुछ गोले shअपने मानक इनपुट पर एक स्क्रिप्ट ले सकते हैं।

हम जो स्क्रिप्ट देते हैं, वह shउस कमांड को निष्पादित करने की कोशिश करता है ''''जो ''(खाली स्ट्रिंग) सरलीकृत है shऔर निश्चित रूप से इसे निष्पादित करने में विफल रहता है क्योंकि कोई ''कमांड नहीं है , इसलिए यह सामान्य रूप से आउटपुट करता हैline 2: command not found से मानक त्रुटि विवरणक पर करता है, लेकिन हम इस संदेश का उपयोग करके पुनर्निर्देशित 2>/dev/nullकरते हैं निकटतम ब्लैक होल क्योंकि यह गड़बड़ है और उपयोगकर्ता को इसे shप्रदर्शित करने के लिए भ्रमित करना होगा।

हम फिर हमारे लिए ब्याज की कमान के लिए आगे बढ़ते हैं: exec जो मौजूदा शेल प्रक्रिया को इस प्रकार से प्रतिस्थापित करता है, हमारे मामले में: /usr/bin/env pythonपर्याप्त मापदंडों के साथ:

  • "$0" अजगर को यह बताने के लिए कि उसे किस स्क्रिप्ट को खोलना और व्याख्या करना चाहिए, और सेट भी करना चाहिए sys.argv[0]
  • "$@" अजगर की स्थापना के लिए sys.argv[1:]स्क्रिप्ट कमांड लाइन पर पास किए गए तर्कों लिए करना।

और हम envसेट करने के लिए भी कहते हैंLD_LIBRARY_PATH पर्यावरण चर , जो हैक का एकमात्र बिंदु है।

शेल कमांड टिप्पणी के साथ शुरू होता है, #ताकि शेल ट्रेलिंग ट्रिपल कोट्स को अनदेखा कर दे'''

shइसके बाद अजगर दुभाषिया के एक नए सिरे से बदल दिया जाता है जो पहले तर्क ( "$0") के रूप में दिए गए अजगर स्रोत की पटकथा को खोलता और पढ़ता है ।

पायथन फ़ाइल को खोलता है और -xतर्क के लिए स्रोत की पहली पंक्ति के ऊपर छोड़ देता है। ध्यान दें: यह भी बिना काम करता है -xक्योंकि पायथन के लिए एक शेबंग सिर्फ एक टिप्पणी है

पाइथन इसके बाद वर्तमान माड्यूल फ़ाइल के लिए डॉकस्ट्रिंग के रूप में दूसरी पंक्ति की व्याख्या करता है, इसलिए यदि आपको एक मान्य मॉड्यूल डॉकस्ट्रिंग की आवश्यकता है, तो बस __doc__अपने पायथन प्रोग्राम में पहली चीज़ को ऊपर दिए गए उदाहरण में सेट करें।



यह देखते हुए कि एक खाली स्ट्रिंग है ... उम ... खाली, आप अपने आदेश को छोड़ने में सक्षम होना चाहिए बंदर व्यवसाय नहीं मिला: ''''exec ...काम करना चाहिए। ध्यान दें कि निष्पादन से पहले कोई स्थान नहीं है या यह खाली कमांड की तलाश करेगा। आप पहले arg पर खाली को अलग करना चाहते हैं इसलिए ऐसा $0है exec
कालेब

1

मुझे एक निष्पादन योग्य की तलाश में एक मूर्खतापूर्ण वर्कअराउंड मिला, जो स्क्रिप्ट को एक एकल तर्क के रूप में स्वीकार करता है:

#!/usr/bin/awk BEGIN{system("bash --posix "ARGV[1])}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.