बैश: संक्षिप्त चर निष्पादित करना


1

मेरे पास एक लिनक्स बैश स्क्रिप्ट है, के प्रभाव के लिए:

COMMAND_TO_EXECUTE="foo "$1" --option1 --option2 "$2
exec $COMMAND_TO_EXECUTE

संकट: स्क्रिप्ट विफल

डिबगिंग क्लू , अगर मैं echo $COMMAND_TO_EXECUTE फिर कट / पेस्ट इको कि एक टर्मिनल विंडो में, यह पूरी तरह से काम करता है।

तो .. पीछे स्ट्रिंग $COMMAND_TO_EXECUTE एक टर्मिनल में मान्य है, लेकिन एक स्क्रिप्ट में मान्य नहीं है। क्या ऐसा कुछ है जो मुझे करना चाहिए $COMMAND_TO_EXECUTE इसे क्रियान्वित करने की कोशिश करने से पहले?

विस्तृत जानकारी: निष्पादित करने के आदेश दिए गए हैं wget या curl, मैं दोनों के साथ एक ही समस्या है। (मैंने स्ट्रिंग्स को ठीक से उद्धृत किया है और & amp;) जैसे वर्ण से बच गए हैं। जैसा कि पहले उल्लेख किया गया है .. कमांड ठीक काम करता है यदि मैं गूँजता है, तो इसे काट / पेस्ट करें।

मैं हैरान हूं और मुझे लगता है कि मैं कुछ प्राथमिक याद कर रहा हूं क्योंकि कमांड काम करता है कटौती / चिपकाया लेकिन स्क्रिप्ट में नहीं।

अद्यतन करें: नीचे दिए गए पॉलिर्जिक के उत्तर ने मेरे लिए काम किया। bash -c "$COMMAND_TO_EXECUTE" ठीक से चलता है।


एक साइड नोट के रूप में, आपके पास दोहरे उद्धरणों के बाहर चर क्यों हैं?
ShadSterling

उद्धरण उस पाठ का परिसीमन करते हैं, जिसका उद्देश्य मैं चर को समाप्‍त करना था।
Paulb

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

जवाबों:


1

मैंने जो मुहावरा इस्तेमाल किया है वह यह है:

cmd="some command in a string"
"$cmd"

ध्यान दें कि उपयोग करने के विपरीत execकॉलिंग स्क्रिप्ट जारी रहेगी।

यदि आपके आदेश में विशेष वर्ण शामिल हैं, तो कुछ संशोधन आवश्यक हो सकते हैं।

यहां एक और भिन्नता है जिसका मैंने उपयोग किया है (मुझे याद नहीं है कि मैंने इसे इस तरह क्यों किया है):

cmd="some command in a string"
bash -c "$cmd"

यहां एक संपूर्ण उदाहरण स्क्रिप्ट है, जिसका उपयोग मैं क्रोन नौकरियों से अपेक्षित उत्पादन को दबाने के लिए करता हूं:

#!/bin/sh

# otrap
# Copyright © 2007-2014 Shad Sterling <me@shadsterling.com>
# Released under CC-BY-SA, http://creativecommons.org/licenses/by-sa/4.0/

if [ "" = "$2" ]; then
    echo traps the output of a command and echos the output only if the size doesn\'t match
    echo USAGE: $0 \<size\> [\"test\"] \<command\>
    echo size is the expected output size
    echo \"test\" means echo the output even if the size does match
    echo command is the command to silence unless it\'s output doesn\'t match 
    exit 2
fi;

test=false
size=$1; shift
echo=false
if [ "test" == "$1" ]; then
test=true; shift
echo=true
fi
cmd="$*"

TF=~/tmp/otrap.#$PPID.log
if [ "false" != "$echo" ]; then
    echo file: "$TF"
    echo running: "$cmd"
fi
starttime=`date +%R`
$SHELL -c "$cmd" > $TF 2>&1
ret=$?
endtime=`date +%R`
ST=`\`dirname $0\`/filesize $TF 2>&1`
if [ "$size" != "$ST" ]; then
    echo=true;
fi;
if [ "false" != "$echo" ]; then
    echo " command:" "$cmd"
    echo "   start:" $starttime
    echo "  finish:" $endtime
    echo "returned:" $ret
    echo "    size:" $ST
    echo "expected:" $size
    echo --------------------------------------------
    cat $TF
fi
rm $TF
exit $ret

bash -c "$ cmd" ने मेरे लिए काम किया। मैंने कभी नहीं सोचा था कि स्ट्रिंग (-c विकल्प) को लागू करने से पहले स्ट्रिंग को और प्रसंस्करण की आवश्यकता होगी। लेकिन यह काम करता है ..
Paulb

मैं उपयोग करने के खिलाफ सलाह दूंगा bash -c इस तरह की बात के लिए; यह अनिवार्य रूप से है eval जोड़ा जटिलता के साथ कि यह एक उपधारा के तहत कमांड चलाता है। eval बग के स्रोत के रूप में अच्छी तरह से लायक प्रतिष्ठा है। क्या eval तथा bash -c अनिवार्य रूप से, कमांड में शेल पार्सिंग / प्रीप्रोसेसिंग की एक अतिरिक्त परत जोड़ दी जाती है, और यदि आपको समझ में नहीं आता है कि शेल पार्सिंग कैसे काम करता है ... तो इसे और जोड़ना अधिक कुछ आप काफी समझ में नहीं आता है।
Gordon Davisson

मुझे नहीं पता कि ओपी किस तरह की चीज का इस्तेमाल कर रहा है; मेरी स्क्रिप्ट में, जैसे काम करना eval बात है। ऐसे कई मामले नहीं हैं जहाँ मैं यह करना चाहता हूँ, लेकिन मुझे लगता है कि कुछ मामले ऐसे हैं जहाँ यह उचित है। इसके बजाय आप क्या उपयोग करेंगे?
ShadSterling

@ पॉलीगर्जिक: यह वास्तव में इस बात पर निर्भर करता है कि कमांड कितना जटिल है (यानी सिर्फ एक कमांड और उसके तर्क, या एक कंपाउंड कमांड, या रीडायरेक्ट के साथ कुछ, या ...) और इसे सीधे निष्पादित करने के बजाय एक चर में क्यों डाला जा रहा है। विकल्पों की संख्या है, और कुछ मामलों में eval सबसे अच्छा है - लेकिन बहुत सारे मामलों में नहीं।
Gordon Davisson

3

संक्षिप्त उत्तर: देखें BashFAQ # 50: मैं एक चर में एक कमांड डालने की कोशिश कर रहा हूं, लेकिन जटिल मामले हमेशा विफल होते हैं!

लंबा उत्तर: जब शेल एक कमांड लाइन को पार्स करता है, तो यह ऐसा करता है जैसे कि यह पता लगाना कि लाइन के कौन से भाग उद्धरण में हैं (या बच गए या जो भी हो) से पहले यह चर का विकल्प देता है; इस प्रकार, यदि आपके पास चर के मूल्यों के अंदर कोई उद्धरण या भाग है, तो जब तक वे कमांड में प्रतिस्थापित नहीं हो जाते, तब तक उन्हें कुछ भी करने के लिए बहुत देर हो चुकी होती है। यह प्रतिस्थापित चरों के मूल्यों पर थोड़ी-बहुत पार्सिंग करता है: यह उन्हें रिक्त स्थान, टैब, आदि के आधार पर "शब्द" में विभाजित करता है (चाहे वे उद्धरण में हों), और यह वाइल्डकार्ड (फिर, यहां तक ​​कि वे) का विस्तार 'फिर से उद्धरण में)। BTW, यह शेल सिंटैक्स के अन्य बिट्स जैसे पाइप, रीडायरेक्ट, आदि को प्रभावी होने में बहुत देर कर देता है। इसका वर्णन करने के लिए, मेरे पास एक आदेश है printargs वह अपने तर्कों को छापता है। जब मैं एक चर में एक जटिल कमांड को स्टोर करने की कोशिश करता हूं तो यहां क्या होता है:

$ cmd='printargs " * " | cat >outfile &'
$ $cmd
Got 8 arguments:
    '"'
    'file1.txt'
    'file2.txt'
    '"'
    '|'
    'cat'
    '>outfile'
    '&'

ध्यान दें कि उद्धरण, पाइप, आदि सभी को सामान्य वर्णों के रूप में माना जाता है, न कि सिंटैक्स।

कई समाधान हैं, इस बात पर निर्भर करता है कि आपने कमांड को पहले वेरिएबल में क्यों रखा है:

  • यदि आपको वास्तव में कमांड को एक चर में रखने की आवश्यकता नहीं है, नहीं । कमांड को निष्पादित किया जाना है, इसलिए जब तक कोई अच्छा कारण नहीं है, बस इसे सीधे निष्पादित करें।

  • यदि आप अनिवार्य रूप से एक ही कमांड का कई बार उपयोग करना चाहते हैं & amp; हर बार पूरी बात लिखना नहीं चाहते हैं (प्रोग्रामिंग के "खुद को दोहराएं नहीं"), फ़ंक्शन का उपयोग करें

    execute_command() {
        foo "$1" --option1 --option2 "$2"
    }
    

    ... और फिर उसे बार-बार फोन करते हैं। ध्यान दें कि मैंने चर संदर्भों को दोहरे-उद्धरणों में रखा है; आपको (लगभग) हमेशा ऐसा करना चाहिए जिससे उन्हें शब्द विभाजन और उन पर लागू वाइल्डकार्ड विस्तार को रोका जा सके।

  • यदि आपको कमांड को गतिशील रूप से बनाने की आवश्यकता है, तो एक ऐरे का उपयोग करें:

    post_data=("var1=value1" "var2=value2" ...)
    
    post_args=()
    for post_arg in "${post_data{@]}"; do   # Note that this is the correct idiom for expanding an array in bash
        post_data+=(-d "$post_arg")
    done
    curl "${post_args[@]}" "$url"
    

ध्यान दें कि यह एकल कमांड के लिए जटिल तर्कों के लिए काम करता है, लेकिन पाइप, रीडायरेक्ट और बैकग्राउंडिंग जैसी चीजों के लिए काम नहीं करेगा ( & ), क्योंकि फिर से चर को प्रतिस्थापित करने से पहले उन्हें पार्स किया जाता है।

अंत में, कुछ चेतावनियाँ:

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

  • अनपेक्षित शब्द विभाजन और वाइल्डकार्ड विस्तार को रोकने के लिए डबल-उद्धरण चर संदर्भ।

  • आप शायद उपयोग नहीं करना चाहते हैं exec - यह वर्तमान शेल (& amp; शेल स्क्रिप्ट) से बाहर निकलता है, और इसे कमांड के साथ बदल देता है; यह शायद वह नहीं है जिसका आपने इरादा किया था।


मैं आपके द्वारा कही गई अधिकांश बातों से सहमत हूं, लेकिन मुझे यकीन नहीं है कि जटिल मामलों के बारे में लिंक प्रासंगिक है। ओपी बहुत ही साधारण मामले के बारे में पूछ रहा था।
ShadSterling

1
@ पॉलीगैरिक: दिया गया उदाहरण जटिल नहीं है (अच्छी तरह से, $ 1 और $ 2 क्या है, इस पर निर्भर करता है), लेकिन वह "सही ढंग से उद्धृत उद्धरण और & amp जैसे पात्रों से बच गया" का उल्लेख करता है। इसके अलावा, यदि वास्तविक कमांड शामिल थे, तो यह सरल था, exec $COMMAND_TO_EXECUTE होगा काम (को छोड़कर) exec अंश)।
Gordon Davisson

0

जैसा आपने टाइप किया है COMMAND-TO-EXECUTE वैध शेल चर नाम नहीं है, डैश हटाएं। यह कुछ सुराग दे सकता है:

$ echo $COMMAND-TO-EXECUTE
-TO-EXECUTE

$ COMMAND-TO-EXECUTE=Test
COMMAND-TO-EXECUTE=Test: command not found

$ COMMAND_TO_EXECUTE=Test

$ echo $COMMAND_TO_EXECUTE
Test

$ 

फिर "foo "$1" --option1 --option2 "$2 थोड़ा अजीब लगता है। अगर तुम चाहो COMMAND_TO_EXECUTE शामिल करने के लिए " -चाहे, इसे बदल दें "foo \"$1\" --option1 --option2 $2" - यह भी ध्यान दें कि मैं अंत में बोली को स्थानांतरित कर दिया। इससे पहले कि आप इसे छोड़ने के लिए $ 2 "अजीब" प्रभाव न डालें exec

जैसा कि आपने एक वास्तविक उदाहरण कमांड नहीं दिया है जिसे आज़माने के लिए मैं / डीबग कर सकता हूं जो मैं अधिक नहीं सोच सकता ...


जैसा कि मैंने देखा कि चर को सौंपे गए स्ट्रिंग में कोई उद्धरण नहीं होगा (जब तक कि $ 1 / $ 2 में उन्हें शामिल न करें): यह बिल्कुल इसके बराबर है "foo $1 --option1 --option2 $2" जब तक $ 2 में विशेष वर्ण नहीं होते। अंतर जो कि प्रतिध्वनि / कॉपी / पेस्ट करता है वह यह है कि शेल विस्तार तब किया जाता है जब चिपकाया गया कमांड निष्पादित होता है, लेकिन यह तब नहीं होता है जब चर सामग्री निष्पादित होती है (प्रयास करें CMD="echo a; echo b" तथा $CMD अंतर देखने के लिए)।
AFH

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