मैं कमांड प्रतिस्थापन के माध्यम से किसी अन्य कमांड में तर्क कैसे उत्पन्न कर सकता हूं


11

निम्नलिखित पर से: शेल कमांड प्रतिस्थापन में अप्रत्याशित व्यवहार

मेरे पास एक आदेश है जो तर्कों की एक विशाल सूची ले सकता है, जिनमें से कुछ में वैध रूप से रिक्त स्थान हो सकते हैं (और शायद अन्य चीजें)

मैंने एक स्क्रिप्ट लिखी, जो मेरे लिए उन तर्कों को उद्धरणों के साथ उत्पन्न कर सकती है, लेकिन मुझे आउटपुट जैसे कॉपी और पेस्ट करना होगा

./somecommand
<output on stdout with quoting>
./othercommand some_args <output from above>

मैंने इसे सरलता से करने की कोशिश की

./othercommand $(./somecommand)

और उपरोक्त प्रश्न में वर्णित अप्रत्याशित व्यवहार में भाग गया। सवाल यह है - क्या आदेश प्रतिस्थापन को तर्क othercommandदिए जाने के लिए मज़बूती से इस्तेमाल किया जा सकता है ताकि कुछ तर्कों को उद्धृत करने की आवश्यकता हो और इसे बदला नहीं जा सकता है?


निर्भर करता है कि आप "मज़बूती" से क्या मतलब है। यदि आप चाहते हैं कि अगला कमांड आउटपुट को ठीक उसी तरह ले, जैसा वह स्क्रीन पर दिखाई देता है और शेल नियमों को लागू करता है, तो शायद evalइसका उपयोग किया जा सकता है, लेकिन यह आमतौर पर अनुशंसित नहीं है। xargsके रूप में अच्छी तरह से विचार करने के लिए कुछ है
Sergiy Kolodyazhnyy

मैं चाहता हूँ (उम्मीद है) उत्पादन से somecommandनियमित खोल पार्सिंग से गुजरना होगा
user1207217

जैसा कि मैंने अपने जवाब में कहा, फील्ड बंटवारे (जैसे :) के लिए कुछ अन्य चरित्र का उपयोग करें ... यह मानते हुए कि चरित्र मज़बूती से आउटपुट में नहीं होगा।
Olorin

लेकिन यह वास्तव में सही नहीं है क्योंकि यह नियमों का पालन नहीं करता है, जो कि इस बारे में है
user1207217

2
क्या आप एक वास्तविक दुनिया का उदाहरण पोस्ट कर सकते हैं? मेरा मतलब है, पहले कमांड का वास्तविक आउटपुट और आप इसे दूसरे कमांड के साथ कैसे इंटरैक्ट करना चाहते हैं।
nxnev

जवाबों:


10

मैंने एक स्क्रिप्ट लिखी, जो उद्धरण के साथ मेरे लिए उन तर्कों को उत्पन्न कर सकती है

यदि शेल के लिए आउटपुट ठीक से उद्धृत किया गया है, और आप आउटपुट पर भरोसा करते हैं , तो आप इस evalपर चल सकते हैं ।

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

यदि ./gen_args.shउत्पादन जैसा होता है 'foo bar' '*' asdf, तो हम परिणामों के साथ eval "args=( $(./gen_args.sh) )"एक सरणी को पॉप्युलेट करने के लिए चला सकते हैं args। यही कारण है कि तीन तत्वों होगा foo bar, *, asdf

हम "${args[@]}"सरणी तत्वों को व्यक्तिगत रूप से विस्तारित करने के लिए हमेशा की तरह उपयोग कर सकते हैं :

$ eval "args=( $(./gen_args.sh) )"
$ for var in "${args[@]}"; do printf ":%s:\n" "$var"; done
:foo bar:
:*:
:asdf:

(उद्धरण नोट करें। "${array[@]}"सभी तत्वों को अलग-अलग तर्कों के रूप में अनमॉडिफाइड किया गया है । उद्धरणों के बिना सरणी तत्व शब्द विभाजन के अधीन हैं। उदाहरण के लिए बशगाइड पर ऐरे पेज देखें ।)

हालाँकि , evalकिसी भी शेल प्रतिस्थापन को खुशी से चलाएगा, इसलिए $HOMEआउटपुट में आपके होम डायरेक्टरी का विस्तार होगा, और कमांड प्रतिस्थापन वास्तव में शेल चलाने में एक कमांड चलाएगा eval। का एक आउटपुट "$(date >&2)"एक एकल खाली ऐरे तत्व बनाता है और स्टडआउट पर वर्तमान तिथि प्रिंट करता है। यह एक चिंता का विषय है अगर gen_args.shकुछ अविश्वसनीय स्रोत से डेटा मिलता है, जैसे नेटवर्क पर एक अन्य होस्ट, अन्य उपयोगकर्ताओं द्वारा बनाए गए फ़ाइल नाम। आउटपुट में मनमाना आदेश शामिल हो सकते हैं। (यदि get_args.shस्वयं दुर्भावनापूर्ण था, तो उसे कुछ भी आउटपुट करने की आवश्यकता नहीं होगी, यह सीधे दुर्भावनापूर्ण आदेशों को चला सकता है।)


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

चलो चुनते हैं #, और स्क्रिप्ट आउटपुट है foo bar#*#asdf। अब हम कमांड के आउटपुट को तर्कों में विभाजित करने के लिए अनक्लोडेटेड कमांड एक्सटेंशन का उपयोग कर सकते हैं ।

$ IFS='#'                          # split on '#' signs
$ set -f                           # disable globbing
$ args=( $( ./gen_args3.sh ) )     # assign the values to the arrayfor var in "${args[@]}"; do printf ":%s:\n" "$var"; done
:foo bar:
:*:
:asdf:

आपको IFSबाद में वापस सेट करने की आवश्यकता होगी यदि आप स्क्रिप्ट में कहीं और शब्द विभाजन पर निर्भर करते हैं ( unset IFSइसे डिफ़ॉल्ट बनाने के लिए काम करना चाहिए), और set +fयदि आप बाद में ग्लोबिंग का उपयोग करना चाहते हैं तो भी इसका उपयोग करें।

यदि आप बैश या किसी अन्य शेल का उपयोग नहीं कर रहे हैं जिसमें सरणियाँ हैं, तो आप उसके लिए स्थितीय मापदंडों का उपयोग कर सकते हैं। बदलें args=( $(...) )के साथ set -- $(./gen_args.sh)और प्रयोग "$@"के बजाय "${args[@]}"तो। (यहां भी, आपको उद्धरण चिह्नों की आवश्यकता है "$@", अन्यथा स्थितीय पैरामीटर शब्द विभाजन के अधीन हैं।)


दोनों ओर से लाभदायक!
ओलोरीन

क्या आप उद्धरण के महत्व को दिखाते हुए एक टिप्पणी जोड़ सकते हैं ${args[@]}- यह मेरे लिए अन्यथा काम नहीं आया
user1207217

@ user1207217, हाँ, आप सही कह रहे हैं। यह सरणियों के साथ और के "${array[@]}"रूप में एक ही बात है "$@"। दोनों को उद्धृत करने की आवश्यकता है, या अन्यथा शब्द विभाजन भागों को सरणी तत्वों को तोड़ता है।
लकक्चु

6

मुद्दा यह है कि एक बार जब आपकी somecommandस्क्रिप्ट के लिए विकल्पों का आउटपुट होता है othercommand, तो विकल्प वास्तव में सिर्फ पाठ होते हैं और शेल के मानक पार्सिंग की दया पर (जो कुछ भी $IFSहोता है उससे प्रभावित होता है और शेल विकल्प क्या प्रभाव में हैं, जो आप सामान्य स्थिति में नहीं करेंगे नियंत्रण में हो)।

विकल्पों somecommandका उत्पादन करने के लिए उपयोग करने के बजाय , इसे कॉल करने के लिए उपयोग करना अधिक आसान, सुरक्षित और अधिक मजबूत होगा othercommandsomecommandस्क्रिप्ट तो एक होगा आवरण स्क्रिप्ट के आसपास othercommandके बजाय सहायक स्क्रिप्ट के कुछ प्रकार आप में से कमांड लाइन के हिस्से के रूप में कुछ विशेष तरीके से कॉल करने के लिए याद करने के लिए होता है कि otherscript। रैपर स्क्रिप्ट एक उपकरण प्रदान करने का एक बहुत ही सामान्य तरीका है, जो विकल्पों के एक और सेट के साथ कुछ इसी तरह के टूल को कॉल करता है (बस यह जांचें fileकि कमांड /usr/binवास्तव में शेल स्क्रिप्ट रैपर क्या हैं)।

में bash, kshया zsh, आप आसानी से एक रैपर स्क्रिप्ट का उपयोग कर सकते हैं, जो इस othercommandतरह के लिए अलग-अलग विकल्पों को रखने के लिए एक सरणी का उपयोग करता है:

options=( "hi there" "nice weather" "here's a star" "*" )
options+=( "bonus bumblebee!" )  # add additional option

फिर कॉल करें othercommand(अभी भी आवरण स्क्रिप्ट के भीतर):

othercommand "${options[@]}"

का विस्तार "${options[@]}"यह सुनिश्चित करेगा कि optionsसरणी के प्रत्येक तत्व othercommandको अलग-अलग उद्धृत किया जाए और अलग-अलग तर्क के रूप में प्रस्तुत किया जाए ।

रैपर का उपयोगकर्ता इस तथ्य से बेखबर होगा कि यह वास्तव में कॉल कर रहा है othercommand, कुछ ऐसा जो सच नहीं होगा यदि स्क्रिप्ट केवल othercommandआउटपुट के लिए कमांड लाइन विकल्प उत्पन्न करती है ।

में /bin/sh, $@विकल्प रखने के लिए उपयोग करें:

set -- "hi there" "nice weather" "here's a star" "*"
set -- "$@" "bonus bumblebee!"  # add additional option

othercommand "$@"

( setस्थितीय मानकों की स्थापना के लिए इस्तेमाल किया आदेश है $1, $2, $3आदि ये हैं क्या सरणी का निर्माण करता है $@एक मानक POSIX खोल में। प्रारंभिक --करने के लिए संकेत करने के लिए है setकि वहाँ कोई विकल्प दिए, केवल तर्क हैं कि। --है वास्तव में केवल अगर जरूरत पहले मूल्य के साथ कुछ शुरू होता है -)।

ध्यान दें कि यह दोहरे कोट्स के आसपास है $@और ${options[@]}यह सुनिश्चित करता है कि तत्व व्यक्तिगत रूप से शब्द-विभाजित (और फ़ाइल नाम ग्लोबेड) नहीं हैं।


क्या आप समझा सकते हैं set --?
user1207217

@ user1207217 जवाब देने के लिए स्पष्टीकरण जोड़ा गया।
Kusalananda

4

यदि somecommandआउटपुट मज़बूती से शेल शेल में है, तो आप इसका उपयोग कर सकते हैं eval:

$ eval sh test.sh $(echo '"hello " "hi and bye"')
hello 
hi and bye

लेकिन आपको यह सुनिश्चित करना होगा कि आउटपुट में मान्य उद्धरण और ऐसे हैं, अन्यथा आप स्क्रिप्ट के बाहर चल रहे कमांड को भी समाप्त कर सकते हैं:

$ cat test.sh 
for var in "$@"
do
    echo "|$var|"
done
$ ls
bar  baz  test.sh
$ eval sh test.sh $(echo '"hello " "hi and bye"; echo rm *')
|hello |
|hi and bye|
rm bar baz test.sh

ध्यान दें कि echo rm bar baz test.shस्क्रिप्ट के कारण पारित नहीं किया गया था (क्योंकि ;) और एक अलग कमांड के रूप में चलाया गया था। मैंने इसे उजागर करने के लिए |चारों ओर जोड़ा $var


आम तौर पर, जब तक आप पूरी तरह से इसके आउटपुट पर भरोसा नहीं कर सकते somecommand, कमांड स्ट्रिंग बनाने के लिए इसके आउटपुट का मज़बूती से उपयोग करना संभव नहीं है।

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