एक फ़ाइल से लोड किए गए स्ट्रिंग में चर का विस्तार करने के लिए बाध्य करना


88

मैं एक स्ट्रिंग (जो एक फ़ाइल से लोड किया गया था) में चर का विस्तार करने के लिए कैसे (बल?) बनाने की कोशिश कर रहा हूं।

मेरे पास सामग्री के साथ "something.txt" नामक एक फाइल है:

hello $FOO world

मैं फिर दौड़ता हूं

export FOO=42
echo $(cat something.txt)

यह रिटर्न:

   hello $FOO world

चर सेट होने के बावजूद इसने $ FOO का विस्तार नहीं किया। मैं फ़ाइल को विकसित या स्रोत नहीं कर सकता - जैसा कि यह कोशिश करेगा और इसे निष्पादित करेगा (यह निष्पादन योग्य नहीं है क्योंकि यह है - मैं बस चाहता हूं कि स्ट्रिंग को चर के साथ प्रक्षेपित किया जाए)।

कोई विचार?



क्या आप टिप्पणी का मतलब नीचे दिए गए उत्तर के लिए होना चाहते हैं?
माइकल निएले

मेरा मतलब आपके लिए टिप्पणी से है। एक से अधिक जवाब का उपयोग करने का प्रस्ताव है evalऔर निहितार्थ के बारे में पता होना महत्वपूर्ण है।
अगली सूचना तक रोक दिया गया।

@ डेनिसविलियम्सन गेटा - मैं थोड़ी देर से जवाब दे रहा हूं लेकिन अच्छी टिप। और निश्चित रूप से हस्तक्षेप के वर्षों में हमें "शेल शॉक" जैसी चीजें मिली हैं, इसलिए आपकी टिप्पणी समय की कसौटी पर खरी उतरी है! टिप्स हैट
माइकल नेले

क्या इससे आपके सवाल का जवाब मिलता है? बैश एक वेरिएबल में वेरिएबल का विस्तार करता है
लोगनमज

जवाबों:


111

मैं जो सोचता हूं, उस पर अड़ गया कि इस प्रश्न का उत्तर है: envsubstआज्ञा।

envsubst < something.txt

यदि यह पहले से ही आपके डिस्ट्रो में उपलब्ध नहीं है, तो यह अंदर है

GNU पैकेज gettext

@Rockallite - मैंने '\ $' समस्या का ध्यान रखने के लिए थोड़ा रैपर स्क्रिप्ट लिखी।

(BTW, envsubst का एक "फीचर" है, जो https://unix.stackexchange.com/a/294400/7088 पर समझाया गया है, इनपुट में केवल कुछ चर का विस्तार करने के लिए, लेकिन मैं मानता हूं कि अपवादों से बचना बहुत अधिक है सुविधाजनक।)

यहाँ मेरी स्क्रिप्ट है:

#! /bin/bash
      ## -*-Shell-Script-*-
CmdName=${0##*/}
Usage="usage: $CmdName runs envsubst, but allows '\$' to  keep variables from
    being expanded.
  With option   -sl   '\$' keeps the back-slash.
  Default is to replace  '\$' with '$'
"

if [[ $1 = -h ]]  ;then echo -e >&2  "$Usage" ; exit 1 ;fi
if [[ $1 = -sl ]] ;then  sl='\'  ; shift ;fi

sed 's/\\\$/\${EnVsUbDolR}/g' |  EnVsUbDolR=$sl\$  envsubst  "$@"

5
+1। यह एकमात्र उत्तर है जो कमांड प्रतिस्थापन, उद्धरण हटाने, तर्क प्रसंस्करण आदि नहीं करने की गारंटी देता है
जोश केली

6
यह सिर्फ पूरी तरह से निर्यात किए गए शेल वर्जन के लिए काम करता है, हालांकि (बैश में)। उदा S = '$ a $ b'; एक = सेट; निर्यात b = निर्यात; इको $ एस | एन्वसबस्ट; पैदावार: "निर्यात"
gauithe

1
धन्यवाद - मुझे लगता है कि इन सभी वर्षों के बाद, मुझे इस उत्तर को स्वीकार करना चाहिए।
माइकल निएल

1
हालांकि, यह डॉलर के चिह्न ( $) से बचने के लिए संभाल नहीं करता है , उदाहरण के echo '\$USER' | envsubstलिए वर्तमान उपयोगकर्ता नाम को पूर्ववर्ती बैकिंग स्लैश के साथ आउटपुट करेगा, न कि $USER
रॉकलाईट

3
brew link --force gettext
होमबॉव की

25

काम के उपयोग evalऔर echoतरह - तरह के जवाबों में से कई , लेकिन विभिन्न चीजों पर ब्रेक, जैसे कि कई लाइनें, शेल मेटा-पात्रों से बचने का प्रयास करना, टेम्पलेट के अंदर भागना, जिसका उद्देश्य बैश द्वारा विस्तार नहीं किया जाना है।

मेरे पास एक ही मुद्दा था, और इस शेल फ़ंक्शन को लिखा, जो कि जहां तक ​​मैं बता सकता हूं, सब कुछ सही ढंग से संभालता है। यह अभी भी केवल बैश के कमांड प्रतिस्थापन नियमों के कारण, टेम्पलेट से नई लाइनों को पीछे छोड़ देगा, लेकिन मैंने कभी नहीं पाया है कि जब तक बाकी सब कुछ बरकरार रहता है, तब तक यह मुद्दा नहीं होगा।

apply_shell_expansion() {
    declare file="$1"
    declare data=$(< "$file")
    declare delimiter="__apply_shell_expansion_delimiter__"
    declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter"
    eval "$command"
}

उदाहरण के लिए, आप इसे इस तरह से उपयोग कर सकते हैं parameters.cfgजो वास्तव में एक शेल स्क्रिप्ट है जो बस चर सेट करता है, और template.txtजो एक टेम्पलेट है जो उन चर का उपयोग करता है:

. parameters.cfg
printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt

व्यवहार में, मैं इसे हल्के टेम्पलेट सिस्टम की तरह उपयोग करता हूं।


4
यह अब तक मुझे मिली सबसे अच्छी ट्रिक में से एक है :)। आपके उत्तर के आधार पर यह एक फ़ंक्शन का निर्माण करना बहुत आसान है जो एक चर का विस्तार करता है जिसमें अन्य चर के संदर्भ होते हैं - बस $ 1 के साथ $ डेटा को अपने फ़ंक्शन में बदलें और यहां हम जाते हैं! मेरा मानना ​​है कि यह मूल प्रश्न, बीटीडब्ल्यू का सबसे अच्छा जवाब है।
आकाशगंगा

यहां तक ​​कि इसमें सामान्य evalनुकसान भी होते हैं। ; $(eval date)इनपुट फ़ाइल के अंदर किसी भी लाइन के अंत में जोड़ने का प्रयास करें और यह dateकमांड चलाएगा ।
शुभ

1
@anubhava मुझे यकीन नहीं है कि आपका क्या मतलब है; यह वास्तव में इस मामले में वांछित विस्तार व्यवहार है। दूसरे शब्दों में, हां, आपको इसके साथ मनमाने ढंग से शेल कोड निष्पादित करने में सक्षम होना चाहिए।
18

22

तुम कोशिश कर सकते हो

echo $(eval echo $(cat something.txt))

9
echo -e "$(eval "echo -e \"`<something.txt`\"")"यदि आपको नई लाइनों की आवश्यकता हो तो उपयोग करें ।
पाविक

एक आकर्षण की तरह काम करता है
kyb

1
लेकिन केवल अगर आपका template.txtपाठ है। यह ऑपरेशन खतरनाक है क्योंकि अगर वे होते हैं तो यह (इम्प्ल्यूट) कमांड निष्पादित करता है।
kyb

2
लेकिन इस दोहरे उद्धरण को हटा दिया
थॉमस Decaux

इसका ठीकरा उपस्तिथ पर फोड़ो।
21:39 पर

8

आप प्रत्येक पंक्ति को प्रिंट नहीं करना चाहते हैं, आप इसका मूल्यांकन करना चाहते हैं ताकि बैश परिवर्तनीय प्रतिस्थापन कर सकें।

FOO=42
while read; do
    eval echo "$REPLY"
done < something.txt

help evalअधिक जानकारी के लिए बैश मैनुअल देखें या।


2
evalखतरनाक है ; आप न केवल $varnameविस्तारित होते हैं (जैसा कि आप के साथ होगा envsubst), लेकिन $(rm -rf ~)साथ ही साथ।
चार्ल्स डफी

4

एक और दृष्टिकोण (जो icky लगता है, लेकिन मैं इसे यहाँ वैसे भी डाल रहा हूँ):

एक temp फ़ाइल में something.txt की सामग्री लिखें, इसके चारों ओर एक गूंज कथन के साथ:

something=$(cat something.txt)

echo "echo \"" > temp.out
echo "$something" >> temp.out
echo "\"" >> temp.out

फिर इसे एक चर में वापस स्रोत:

RESULT=$(source temp.out)

और $ RESULT में इसका विस्तार होगा। लेकिन यह कितना गलत लगता है!


1
icky लेकिन यह सबसे सीधा-सरल, सरल और काम करता है।
kyb

3

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

contents="$(cat something.txt)"
echo $(eval echo \"$contents\")

(लगभग $ सामग्री से बच गए उद्धरण यहाँ कुंजी है)


मैंने यह एक कोशिश की है, हालांकि $ () मेरे मामले में लाइन के अंत को हटा देता है जो परिणामी स्ट्रिंग को तोड़ता है
moz

मैंने eval लाइन को बदल दिया eval "echo \"$$contents\""और इसने
व्हॉट्सएप को

1

निम्नलिखित समाधान:

  • चर की जगह की अनुमति देता है जो परिभाषित किए जाते हैं

  • अपरिवर्तित वैरिएबल प्लेसहोल्डर्स को छोड़ देता है जो परिभाषित नहीं हैं। यह विशेष रूप से स्वचालित तैनाती के दौरान उपयोगी है।

  • निम्नलिखित स्वरूपों में चर के प्रतिस्थापन का समर्थन करता है:

    ${var_NAME}

    $var_NAME

  • ऐसी रिपोर्टें जो चर पर्यावरण में परिभाषित नहीं हैं और ऐसे मामलों के लिए त्रुटि कोड देता है



    TARGET_FILE=someFile.txt;
    ERR_CNT=0;

    for VARNAME in $(grep -P -o -e '\$[\{]?(\w+)*[\}]?' ${TARGET_FILE} | sort -u); do     
      VAR_VALUE=${!VARNAME};
      VARNAME2=$(echo $VARNAME| sed -e 's|^\${||g' -e 's|}$||g' -e 's|^\$||g' );
      VAR_VALUE2=${!VARNAME2};

      if [ "xxx" = "xxx$VAR_VALUE2" ]; then
         echo "$VARNAME is undefined ";
         ERR_CNT=$((ERR_CNT+1));
      else
         echo "replacing $VARNAME with $VAR_VALUE2" ;
         sed -i "s|$VARNAME|$VAR_VALUE2|g" ${TARGET_FILE}; 
      fi      
    done

    if [ ${ERR_CNT} -gt 0 ]; then
        echo "Found $ERR_CNT undefined environment variables";
        exit 1 
    fi

1
  1. अगर कुछ। में केवल एक ही लाइन है, तो प्रक्रिया और कमांड प्रतिस्थापन का उपयोग करते हुए एक bashविधि, ( माइकल नेले के "icky" उत्तर का एक छोटा संस्करण ) :

    FOO=42 . <(echo -e echo $(<something.txt))
    

    आउटपुट:

    hello 42 world
    

    ध्यान दें कि exportजरूरत नहीं है।

  2. अगर कुछ चीजों में एक या एक से अधिक लाइनें हैं, तो एक GNU वाल्ट विधि:sed e

    FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
    

1
मैंने sedअपने उद्देश्य को सबसे उपयुक्त बनाने के लिए मूल्यांकन पाया । मुझे टेम्प्लेट से स्क्रिप्ट का उत्पादन करने की आवश्यकता थी, जिसमें अन्य कॉन्फ़िगरेशन फ़ाइल में निर्यात किए गए चर से भरे मूल्य होंगे। यह आउटपुट में \$(date)प्राप्त करने के लिए टेम्पलेट में डाले गए कमांड विस्तार के लिए ठीक से भागना संभालता है $(date)। धन्यवाद!
गादमीक

0
$ eval echo $(cat something.txt)
hello 42 world
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.

0
foo=45
file=something.txt       # in a file is written: Hello $foo world!
eval echo $(cat $file)

1
इस कोड स्निपेट के लिए धन्यवाद, जो कुछ सीमित, तत्काल सहायता प्रदान कर सकता है। एक उचित व्याख्या यह दर्शाती है कि यह समस्या का एक अच्छा समाधान क्यों है, यह दिखाते हुए इसके दीर्घकालिक मूल्य में बहुत सुधार करेगा और यह भविष्य के पाठकों को अन्य, समान प्रश्नों के साथ और अधिक उपयोगी बना देगा। कृपया कुछ स्पष्टीकरण जोड़ने के लिए अपने उत्तर को संपादित करें, जिसमें आपके द्वारा की गई धारणाएँ शामिल हैं।
मकर

-1

envsubst एक बढ़िया समाधान है (लेन का उत्तर देखें) यदि आप जिस सामग्री को प्रतिस्थापित कर रहे हैं वह "उचित" लंबाई की है।

मेरे मामले में, मुझे चर नाम बदलने के लिए फ़ाइल की सामग्री में स्थानापन्न करने की आवश्यकता थी। envsubstआवश्यकता है कि सामग्री को पर्यावरण चर के रूप में निर्यात किया जाए और जब एक मेगाबाइट से अधिक हो तो पर्यावरण चर का निर्यात करते समय एक समस्या हो।

awk solution

एक अलग सवाल से cuonglm के समाधान का उपयोग करना :

needle="doc1_base64" # The "variable name" in the file. (A $ is not needed.)
needle_file="doc1_base64.txt" # Will be substituted for the needle 
haystack=$requestfile1 # File containing the needle
out=$requestfile2
awk "BEGIN{getline l < \"${needle_file}\"}/${needle}/{gsub(\"${needle}\",l)}1" $haystack > $out

यह समाधान बड़ी फ़ाइलों के लिए भी काम करता है।


-3

निम्नलिखित कार्य: bash -c "echo \"$(cat something.txt)"\"


यह सिर्फ अक्षम नहीं है, बल्कि बेहद खतरनाक भी है; यह सिर्फ सुरक्षित विस्तार की तरह नहीं चलता है $foo, लेकिन जैसे असुरक्षित हैं $(rm -rf ~)
चार्ल्स डफी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.