नोट: @ jw013 नीचे टिप्पणी में निम्नलिखित असमर्थित आपत्ति बनाता है :
डाउनवोट इसलिए है क्योंकि आमतौर पर स्व-संशोधित कोड को बुरा व्यवहार माना जाता है। छोटे विधानसभा कार्यक्रमों के पुराने दिनों में यह सशर्त शाखाओं को कम करने और प्रदर्शन में सुधार करने का एक चतुर तरीका था, लेकिन आजकल सुरक्षा जोखिमों से अधिक लाभ मिलता है। यदि स्क्रिप्ट चलाने वाले उपयोगकर्ता ने स्क्रिप्ट पर विशेषाधिकार नहीं लिखे हैं तो आपका दृष्टिकोण काम नहीं करेगा।
मैं उनका कहना है कि उसके द्वारा सुरक्षा आपत्तियों के जवाब दिए किसी भी विशेष अनुमति की कर रहे हैं केवल एक बार की आवश्यकता के अनुसार स्थापित / अद्यतन क्रम में कार्रवाई स्थापित / अद्यतन स्वयं को स्थापित करने से जो मैं व्यक्तिगत रूप से बहुत सुरक्षित कहेंगे - स्क्रिप्ट। मैंने उसे man sh
इसी तरह से समान लक्ष्यों को प्राप्त करने के संदर्भ में भी इंगित किया । मैंने उस समय यह इंगित करने की जहमत नहीं उठाई कि जो भी सुरक्षा खामियां हैं या अन्यथा आम तौर पर असंबद्ध प्रथाएँ हैं जिनका मेरे उत्तर में प्रतिनिधित्व किया भी जा सकता है या नहीं भी किया जा सकता है, वे इस प्रश्न की जड़ में होने की संभावना से कहीं अधिक अपने आप में थीं।
मैं शेबांग कैसे सेट कर सकता हूं ताकि स्क्रिप्ट को /path/to/script.sh के रूप में चला रहा हो, वह हमेशा PATH में उपलब्ध Zsh का उपयोग करता है?
संतुष्ट नहीं, @ jw013 ने कम से कम कुछ गलत बयानों के साथ अभी तक असमर्थित तर्क को आगे बढ़ाते हुए आपत्ति जारी रखी :
आप एक फाइल का उपयोग करते हैं, दो फाइलों का नहीं। [ man sh
संदर्भित]
पैकेज एक फ़ाइल किसी अन्य फ़ाइल को संशोधित किया है। आपके पास एक फाइल ही है। इन दोनों मामलों में एक अलग अंतर है। एक फाइल जो इनपुट लेती है और आउटपुट उत्पन्न करती है वह ठीक है। एक निष्पादन योग्य फ़ाइल जो अपने आप को बदल देती है क्योंकि यह आमतौर पर एक बुरा विचार है। आपने जो उदाहरण दिया वह ऐसा नहीं करता है।
पहली जगह में:
केवल निष्पादन योग्य किसी में कोड निष्पादन योग्य खोल स्क्रिप्ट है #!
खुद को
(हालांकि भी #!
है आधिकारिक तौर पर अनिर्दिष्ट )
{ cat >|./file
chmod +x ./file
./file
} <<-\FILE
#!/usr/bin/sh
{ ${l=lsof -p} $$
echo "$l \$$" | sh
} | grep \
"COMMAND\|^..*sh\| [0-9]*[wru] "
#END
FILE
##OUTPUT
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
file 8900 mikeserv txt REG 0,33 774976 2148676 /usr/bin/bash
file 8900 mikeserv mem REG 0,30 2148676 /usr/bin/bash (path dev=0,33)
file 8900 mikeserv 0r REG 0,35 108 15496912 /tmp/zshUTTARQ (deleted)
file 8900 mikeserv 1u CHR 136,2 0t0 5 /dev/pts/2
file 8900 mikeserv 2u CHR 136,2 0t0 5 /dev/pts/2
file 8900 mikeserv 255r REG 0,33 108 2134129 /home/mikeserv/file
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sh 8906 mikeserv txt REG 0,33 774976 2148676 /usr/bin/bash
sh 8906 mikeserv mem REG 0,30 2148676 /usr/bin/bash (path dev=0,33)
sh 8906 mikeserv 0r FIFO 0,8 0t0 15500515 pipe
sh 8906 mikeserv 1w FIFO 0,8 0t0 15500514 pipe
sh 8906 mikeserv 2u CHR 136,2 0t0 5 /dev/pts/2
{ sed -i \
'1c#!/home/mikeserv/file' ./file
./file
sh -c './file ; echo'
grep '#!' ./file
}
##OUTPUT
zsh: too many levels of symbolic links: ./file
sh: ./file: /home/mikeserv/file: bad interpreter: Too many levels of symbolic links
#!/home/mikeserv/file
एक शेल स्क्रिप्ट सिर्फ एक पाठ फ़ाइल है - इसके लिए किसी भी प्रभाव के लिए इसे किसी अन्य निष्पादन योग्य फ़ाइल द्वारा पढ़ा जाना चाहिए , इसके निर्देशों को फिर उस अन्य निष्पादन योग्य फ़ाइल द्वारा व्याख्या की जाती है, इससे पहले कि अन्य निष्पादन योग्य फ़ाइल फिर उसकी व्याख्या को निष्पादित करती है। शेल स्क्रिप्ट। यह है संभव नहीं एक खोल स्क्रिप्ट फ़ाइल के निष्पादन के लिए के लिए कम से कम दो फ़ाइलों शामिल है। zsh
अपने स्वयं के संकलक में एक संभावित अपवाद है , लेकिन इसके साथ मुझे बहुत कम अनुभव है और यह किसी भी तरह से यहां का प्रतिनिधित्व नहीं करता है।
एक शेल स्क्रिप्ट का हैशबैंग अपने इच्छित दुभाषिया को इंगित करना चाहिए या अप्रासंगिक के रूप में त्याग दिया जाना चाहिए।
शेल के पास अपने इनपुट को पार्स करने और व्याख्या करने के दो बुनियादी तरीके हैं: या तो इसका वर्तमान इनपुट परिभाषित <<here_document
कर रहा है या इसे परिभाषित कर रहा है { ( command |&&|| list ) ; } &
- दूसरे शब्दों में, शेल या तो एक टोकन को एक कमांड के लिए एक सीमांकक के रूप में व्याख्या करता है जिसे इसे पढ़ने के बाद इसे निष्पादित करना चाहिए। या एक फ़ाइल बनाने के निर्देश के रूप में और दूसरे कमांड के लिए फाइल डिस्क्रिप्टर में मैप करें। बस।
जब आरक्षित शब्दों के सेट पर शेल परिसीमन टोकन को निष्पादित करने के लिए कमांड की व्याख्या करता है । जब लागू - - या बंद करने की तरह टोकन खोल एक उद्घाटन टोकन यह इस तरह एक नई पंक्ति के रूप में एक आदेश सूची में पढ़ने के लिए जारी रखना चाहिए जब तक सूची या तो टोकन एक बंद से सीमांकित किया जाता है का सामना करना पड़ता है जब })
के लिए ({
क्रियान्वयन से पहले।
शेल एक साधारण कमांड और एक कंपाउंड कमांड के बीच अंतर करता है । यौगिक आदेश आदेशों क्रियान्वयन से पहले में पढ़ा जाना चाहिए का सेट है, लेकिन खोल प्रदर्शन नहीं करता $expansion
उसके घटक में से किसी पर सरल आदेशों जब तक यह अकेले हर एक निष्पादित करता है।
इसलिए, निम्नलिखित उदाहरण में, ;semicolon
आरक्षित शब्द व्यक्तिगत सरल आदेशों का परिसीमन करते हैं जबकि गैर-बचा हुआ \newline
चरित्र दो यौगिक आदेशों के बीच परिसीमन करता है :
{ cat >|./file
chmod +x ./file
./file
} <<-\FILE
#!/usr/bin/sh
echo "simple command ${sc=1}" ;\
: > $0 ;\
echo "simple command $((sc+2))" ;\
sh -c "./file && echo hooray"
sh -c "./file && echo hooray"
#END
FILE
##OUTPUT
simple command 1
simple command 3
hooray
यह दिशानिर्देशों का सरलीकरण है। यह बहुत अधिक जटिल हो जाता है जब आप शेल-बिल्डिंस, सबहेल, वर्तमान वातावरण और आदि पर विचार करते हैं, लेकिन, यहां मेरे उद्देश्यों के लिए, यह पर्याप्त है।
और की बात कर बनाया-इन और आदेश-सूचियों, एक function() { declaration ; }
महज एक बताए का एक साधन है यौगिक आदेश एक करने के लिए साधारण आदेश। शेल को $expansions
घोषणा के बयान पर कोई भी प्रदर्शन नहीं करना चाहिए - शामिल करने के लिए <<redirections>
- लेकिन इसके बजाय परिभाषा को एक एकल, शाब्दिक स्ट्रिंग के रूप में संग्रहीत करना चाहिए और इसे एक विशेष शेल के रूप में निष्पादित करना चाहिए जब इसे बुलाया जाता है।
इसलिए एक निष्पादन योग्य शेल स्क्रिप्ट में घोषित एक शेल फ़ंक्शन को अपने शाब्दिक स्ट्रिंग रूप में शेल की मेमोरी की व्याख्या करने में संग्रहीत किया जाता है - इनपुट के रूप में यहां संलग्न दस्तावेजों को सम्मिलित करने के लिए अनएक्सपेन्डेड - और इसके स्रोत फ़ाइल के स्वतंत्र रूप से निष्पादित होने पर हर बार इसे शेल शेल के रूप में कहा जाता है- जब तक शेल का वर्तमान वातावरण रहता है, तब तक।
पुनर्निर्देशन ऑपरेटर <<
और <<-
दोनों एक कमांड के इनपुट के लिए एक शेल इनपुट फ़ाइल में निहित लाइनों के पुनर्निर्देशन की अनुमति देते हैं, जिसे यहां दस्तावेज़ के रूप में जाना जाता है ।
यहां-दस्तावेज़ एक शब्द है कि अगले बाद शुरू होता है के रूप में माना जाएगा \newline
और जब तक वहाँ केवल रखने वाली पंक्ति है जारी है सीमांकक और एक \newline
, कोई साथ [:blank:]
के बीच में है। फिर अगला यहाँ-दस्तावेज़ शुरू होता है, अगर वहाँ एक है। प्रारूप इस प्रकार है:
[n]<<word
here-document
delimiter
... जहां वैकल्पिक n
फ़ाइल डिस्क्रिप्टर नंबर का प्रतिनिधित्व करता है। यदि संख्या छोड़ दी जाती है, तो यहां दस्तावेज़ मानक इनपुट (फाइल डिस्क्रिप्टर 0) को संदर्भित करता है ।
for shell in dash zsh bash sh ; do sudo $shell -c '
{ readlink /proc/self/fd/3
cat <&3
} 3<<-FILE
$0
FILE
' ; done
#OUTPUT
pipe:[16582351]
dash
/tmp/zshqs0lKX (deleted)
zsh
/tmp/sh-thd-955082504 (deleted)
bash
/tmp/sh-thd-955082612 (deleted)
sh
आप समझ सकते हैं? शेल के ऊपर प्रत्येक शेल के लिए एक फाइल बनाता है और इसे फाइल डिस्क्रिप्टर में मैप करता है। में zsh, (ba)sh
खोल में एक नियमित रूप से फ़ाइल बनाता है /tmp
, उत्पादन उदासीनता किसी वर्णनकर्ता करने के लिए इसे नक्शे, तो हटाता /tmp
फ़ाइल इसलिए वर्णनकर्ता की गिरी के प्रति सब अवशेष है। dash
उस बकवास के सभी से बचा जाता है और बस अपने आउटपुट प्रोसेसिंग को एक अनाम |pipe
फाइल में रीडायरेक्ट <<
लक्ष्य पर लक्षित करता है।
यह बनाता है dash
:
cmd <<HEREDOC
$(cmd)
HEREDOC
कार्यात्मक रूप से इसके समकक्ष bash
:
cmd <(cmd)
जबकि dash
कार्यान्वयन कम से कम POSIXly पोर्टेबल है।
जिससे कई फ़ाइलें
इसलिए नीचे दिए गए उत्तर में जब मैं करता हूं:
{ cat >|./file
chmod +x ./file
./file
} <<\FILE
#!/usr/bin/sh
_fn() { printf '#!' ; command -v zsh ; cat
} <<SCRIPT >$0
[SCRIPT BODY]
SCRIPT
_fn ; exec $0
FILE
निम्नलिखित होता है:
मैं पहली बार cat
के लिए जो भी फ़ाइल खोल के लिए बनाई गई सामग्री FILE
में ./file
, यह निष्पादन योग्य बनाने, तो यह निष्पादित।
कर्नेल ने एक फाइल डिस्क्रिप्टर को सौंपा #!
और उसके /usr/bin/sh
साथ कॉल करता है ।<read
./file
sh
स्मृति में एक स्ट्रिंग है जो कंपाउंड कमांड से शुरू _fn()
और अंत में सम्मिलित है SCRIPT
।
जब _fn
कहा जाता है, sh
पहले तो एक विवरणक के लिए फाइल में परिभाषित नक्शा व्याख्या करना चाहिए <<SCRIPT...SCRIPT
से पहले लागू _fn
निर्मित उपयोगिता एक विशेष रूप क्योंकि SCRIPT
है _fn
की<input.
इसके द्वारा किए गए तार आउटपुट printf
और 's' के मानक-command
आउट के लिए लिखे गए हैं, जिन्हें वर्तमान शेल - या में पुनर्निर्देशित किया गया है ।_fn
>&1
ARGV0
$0
cat
अपने <&0
मानक-इनपुट फ़ाइल-डिस्क्रिप्टर - SCRIPT
को >
काटे गए वर्तमान शेल के ARGV0
तर्क पर, या $0
।
अपने पहले से ही पढ़े- लिखे वर्तमान कंपाउंड कमांड में , sh exec
निष्पादन योग्य - और नए पुनर्लेखन - $0
तर्क को पूरा करना।
समय ./file
तक अपने निहित निर्देश निर्दिष्ट है कि यह किया जाना चाहिए कहा जाता है exec
फिर d, sh
एक एकल में इसे पढ़ता यौगिक आदेश एक समय में के रूप में यह उन्हें कार्यान्वित करता है, जबकि ./file
खुद को बिल्कुल भी कुछ नहीं करता है खुशी से अपनी नई सामग्री स्वीकार छोड़कर। फ़ाइलें जो वास्तव में काम पर हैं/usr/bin/sh, /usr/bin/cat, /tmp/sh-something-or-another.
धन्यवाद, सभी के बाद
तो जब @ jw013 निर्दिष्ट करता है कि:
एक फाइल जो इनपुट लेती है और आउटपुट उत्पन्न करती है, ठीक है ...
... इस जवाब की अपनी गलत आलोचना के बीच, वह वास्तव में अनजाने में यहां इस्तेमाल की जाने वाली एकमात्र विधि की निंदा कर रहा है, जो मूल रूप से सिर्फ काम करता है:
cat <new_file >old_file
उत्तर
यहां सभी उत्तर अच्छे हैं, लेकिन उनमें से कोई भी पूरी तरह से सही नहीं है। हर कोई दावा करता है कि आप गतिशील और स्थायी रूप से अपना रास्ता नहीं बना सकते #!bang
। यहां एक पथ स्वतंत्र शेबांग की स्थापना का प्रदर्शन है:
डेमो
{ cat >|./file
chmod +x ./file
./file
} <<\FILE
#!/usr/bin/sh
_rewrite_me() { printf '#!' ; command -v zsh
${out+cat} ; ${out+:} . /dev/fd/0 >&2
} <<\SCRIPT >|${out-/dev/null}
printf "
\$0 :\t$0
lines :\t$((c=$(wc -l <$0)))
!bang :\t$(sed 1q "$0")
shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
sed -e 'N;s/\n/ >\t/' -e 4a\\...
SCRIPT
_rewrite_me ; out=$0 _rewrite_me ; exec $0
FILE
आउटपुट
$0 : ./file
lines : 13
!bang : #!/usr/bin/sh
shell : /usr/bin/sh
1 > #!/usr/bin/sh
2 > _rewrite_me() { printf '#!' ; command -v zsh
...
12 > SCRIPT
13 > _rewrite_me ; out=$0 _rewrite_me ; exec $0
$0 : /home/mikeserv/file
lines : 8
!bang : #!/usr/bin/zsh
shell : /usr/bin/zsh
1 > #!/usr/bin/zsh
2 > printf "
...
7 > sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
8 > sed -e 'N;s/\n/ >\t/' -e 4a\\...
आप समझ सकते हैं? हम सिर्फ स्क्रिप्ट को ओवरराइट करते हैं। और यह केवल एक बार git
सिंक के बाद ही होता है । उस बिंदु से इसे # बैंग लाइन में सही रास्ता मिल गया है।
अब लगभग सभी वहाँ बस फुलाना है। इसे सुरक्षित रूप से करने के लिए आपको आवश्यकता है:
एक फ़ंक्शन शीर्ष पर परिभाषित किया गया और नीचे लिखा गया है जो लेखन करता है। इस तरह हम अपनी जरूरत की हर चीज को मेमोरी में स्टोर कर लेते हैं और पूरी फाइल पढ़ने से पहले यह सुनिश्चित कर लेते हैं कि हम इस पर लिखना शुरू कर दें।
यह निर्धारित करने का कोई तरीका कि रास्ता क्या होना चाहिए। command -v
उसके लिए बहुत अच्छा है।
Heredocs वास्तव में मदद करते हैं क्योंकि वे वास्तविक फाइलें हैं। वे इस बीच आपकी स्क्रिप्ट को संग्रहीत करेंगे। आप तार का उपयोग कर सकते हैं लेकिन ...
आपको यह सुनिश्चित करना होगा कि शेल कमांड में पढ़ता है जो आपकी स्क्रिप्ट को उसी कमांड सूची में लिखता है जो इसे निष्पादित करता है।
देखो:
{ cat >|./file
chmod +x ./file
./file
} <<\FILE
#!/usr/bin/sh
_rewrite_me() { printf '#!' ; command -v zsh
${out+cat} ; ${out+:} . /dev/fd/0 >&2
} <<\SCRIPT >|${out-/dev/null}
printf "
\$0 :\t$0
lines :\t$((c=$(wc -l <$0)))
!bang :\t$(sed 1q "$0")
shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
sed -e 'N;s/\n/ >\t/' -e 4a\\...
SCRIPT
_rewrite_me ; out=$0 _rewrite_me
exec $0
FILE
ध्यान दें कि मैंने केवल exec
कमांड को एक पंक्ति में स्थानांतरित किया है । अभी:
#OUTPUT
$0 : ./file
lines : 14
!bang : #!/usr/bin/sh
shell : /usr/bin/sh
1 > #!/usr/bin/sh
2 > _rewrite_me() { printf '#!' ; command -v zsh
...
13 > _rewrite_me ; out=$0 _rewrite_me
14 > exec $0
मुझे आउटपुट का दूसरा भाग नहीं मिलता क्योंकि स्क्रिप्ट अगले कमांड में नहीं पढ़ सकती। फिर भी, क्योंकि एकमात्र कमांड गायब था अंतिम था:
cat ./file
#!/usr/bin/zsh
printf "
\$0 :\t$0
lines :\t$((c=$(wc -l <$0)))
!bang :\t$(sed 1q "$0")
shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
sed -e 'N;s/\n/ >\t/' -e 4a\\...
स्क्रिप्ट के माध्यम से आया था जैसा कि होना चाहिए - ज्यादातर क्योंकि यह सब हेरेडोक में था - लेकिन अगर आप इसे सही तरीके से योजना नहीं बनाते हैं, तो आप अपनी फाइलस्ट्रीम को छोटा कर सकते हैं, जो कि मेरे ऊपर हुआ है।
env
दोनों / बिन और / usr / बिन में नहीं हैं?which -a env
पुष्टि करने का प्रयास करें।