बैश टेंपलेटिंग: बैश के साथ टेम्प्लेट से कॉन्फ़िगरेशन फ़ाइलों का निर्माण कैसे करें?


134

मैं अपने वेबसर्वर के लिए Apache और PHP के लिए विन्यास फाइल बनाने के लिए एक स्क्रिप्ट लिख रहा हूँ। मैं किसी भी GUI जैसे CPanel या ISPConfig का उपयोग नहीं करना चाहता।

मेरे पास अपाचे और पीएचपी कॉन्फ़िगरेशन फ़ाइलों के कुछ टेम्पलेट हैं। बैश स्क्रिप्ट को टेम्प्लेट पढ़ने की जरूरत है, कुछ फ़ोल्डर में वैरिएबल प्रतिस्थापन और आउटपुट पार्स किए गए टेम्प्लेट बनाएं। उसे करने का सबसे अच्छा तरीका कौन सा है? मैं कई तरीकों के बारे में सोच सकता हूं। कौन सा सबसे अच्छा है या ऐसा करने के लिए कुछ बेहतर तरीके हो सकते हैं? मैं इसे शुद्ध बैश में करना चाहता हूं (उदाहरण के लिए PHP में यह आसान है)

1) टेक्स्ट फ़ाइल में $ {} प्लेसहोल्डर्स को कैसे बदलें?

template.txt:

the number is ${i}
the word is ${word}

script.sh:

#!/bin/sh

#set variables
i=1
word="dog"
#read in template one line at the time, and replace variables
#(more natural (and efficient) way, thanks to Jonathan Leffler)
while read line
do
    eval echo "$line"
done < "./template.txt"

BTW, मैं यहाँ बाहरी फ़ाइल पर आउटपुट को कैसे पुनः निर्देशित करूँ? यदि चर, बोल, उद्धरण सहित मुझे कुछ बचने की आवश्यकता है?

2) प्रत्येक चर को उसके मान से बदलने के लिए बिल्ली और पाल का उपयोग करना:

टेम्पलेट दिया गया।

The number is ${i}
The word is ${word}

कमान:

cat template.txt | sed -e "s/\${i}/1/" | sed -e "s/\${word}/dog/"

कई अलग-अलग प्रतीकों से बचने की आवश्यकता के कारण मेरे लिए बुरा लगता है और कई चर के साथ लाइन बहुत लंबी हो जाएगी।

क्या आप किसी और सुरुचिपूर्ण और सुरक्षित समाधान के बारे में सोच सकते हैं?


जवाबों:


61

आप इसका उपयोग कर सकते हैं:

perl -p -i -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : $&/eg' < template.txt

सभी ${...}स्ट्रिंग्स को संबंधित एनवायरमेंट वैरिएबल से बदलना (इस स्क्रिप्ट को चलाने से पहले उन्हें निर्यात करना न भूलें)।

शुद्ध बैश के लिए यह काम करना चाहिए (यह मानते हुए कि चर में $ {...} स्ट्रिंग्स नहीं हैं):

#!/bin/bash
while read -r line ; do
    while [[ "$line" =~ (\$\{[a-zA-Z_][a-zA-Z_0-9]*\}) ]] ; do
        LHS=${BASH_REMATCH[1]}
        RHS="$(eval echo "\"$LHS\"")"
        line=${line//$LHS/$RHS}
    done
    echo "$line"
done

। यदि RHS कुछ चर का संदर्भ देता है जो समाधान स्वयं को संदर्भित नहीं करता है:

#!/bin/bash
line="$(cat; echo -n a)"
end_offset=${#line}
while [[ "${line:0:$end_offset}" =~ (.*)(\$\{([a-zA-Z_][a-zA-Z_0-9]*)\})(.*) ]] ; do
    PRE="${BASH_REMATCH[1]}"
    POST="${BASH_REMATCH[4]}${line:$end_offset:${#line}}"
    VARNAME="${BASH_REMATCH[3]}"
    eval 'VARVAL="$'$VARNAME'"'
    line="$PRE$VARVAL$POST"
    end_offset=${#PRE}
done
echo -n "${line:0:-1}"

चेतावनी : मुझे सही तरीके से इनपुट को एनएएलएस के साथ बैश में संभालना या नई अनुगामी राशि को संरक्षित करने का तरीका नहीं पता है। अंतिम संस्करण इसलिए प्रस्तुत किया गया है क्योंकि यह गोले "प्यार" बाइनरी इनपुट है:

  1. read बैकस्लैश की व्याख्या करेगा।
  2. read -r बैकस्लैश की व्याख्या नहीं करेगा, लेकिन फिर भी अंतिम पंक्ति छोड़ देगा यदि यह एक नई रेखा के साथ समाप्त नहीं होता है।
  3. "$(…)"उतने ही नए अनुगामी के रूप में स्ट्रिप करेंगे, इसलिए मैं समाप्त होता हूं ; echo -n aऔर उपयोग करता हूं echo -n "${line:0:-1}": यह अंतिम चरित्र (जो है a) को छोड़ देता है और कई अनुगामी न्यूलाइन्स को संरक्षित करता है क्योंकि इनपुट में (नहीं सहित) था।

3
मैं बदल जाएगा [^}]करने के लिए [A-Za-Z_][A-Za-z0-9_](अगर यह प्रक्रिया करने की कोशिश की जैसे सख्त प्रतिस्थापन से परे जा रहा से रोकने के लिए खोल bash संस्करण में ${some_unused_var-$(rm -rf $HOME)})।
क्रिस जॉन्सन

2
@FractalizeR आप $&पर्ल समाधान में बदलना चाहते हैं "": पहले ${...}अछूता छोड़ देता है अगर यह स्थानापन्न करने में विफल रहता है, तो दूसरा इसे खाली स्ट्रिंग के साथ बदल देता है।
ZyX

5
नोट: जाहिरा तौर पर बैश 3.1 से 3.2 (और ऊपर) से एक परिवर्तन हुआ था जिसमें रेगेक्स के चारों ओर एकल उद्धरण - रेगेक्स की सामग्री को एक स्ट्रिंग शाब्दिक के रूप में मानते हैं। तो ऊपर रेगेक्स होना चाहिए ... (\ $ $ {[a-zA-Z _] [a-zA-Z_0-9] * \}) stackoverflow.com/questions/304864/…
ब्लू

2
बनाने के whileपाश भले ही यह एक नई पंक्ति, उपयोग के द्वारा समाप्त नहीं कर रहा है अंतिम पंक्ति को पढ़ने के while read -r line || [[ -n $line ]]; do। इसके अतिरिक्त, आपका readकमांड प्रत्येक पंक्ति से व्हाट्सएप को लीड और ट्रेल करता है; while IFS= read -r line || [[ -n $line ]]; do
उससे

2
बस एक व्यापक समाधान की तलाश करने वालों के लिए एक बाधा को नोट करने के लिए: ये अन्यथा आसान समाधान आपको विस्तार से चर संदर्भों को सुरक्षित रखने की अनुमति नहीं देते हैं (जैसे कि \ उन्हें बचाकर)।
mklement0

138

प्रयत्न envsubst

FOO=foo
BAR=bar
export FOO BAR

envsubst <<EOF
FOO is $FOO
BAR is $BAR
EOF

11
बस संदर्भ के लिए, envsubstजब हेरेडॉक का उपयोग करना आवश्यक नहीं है , क्योंकि बैश हेडरॉक को शाब्दिक दोहरे-उद्धृत स्ट्रिंग के रूप में मानता है और इसमें पहले से ही चर को प्रक्षेपित करता है। जब आप किसी अन्य फ़ाइल से टेम्पलेट को पढ़ना चाहते हैं तो यह एक बढ़िया विकल्प है। बहुत अधिक बोझिल के लिए एक अच्छा प्रतिस्थापन m4
बीएचपी

2
इस कमांड के बारे में जानकर मुझे बहुत सुखद आश्चर्य हुआ। मैं शून्य सफलता के साथ मैन्युअल रूप से एन्वसबस्ट की कार्यक्षमता को कोब करने की कोशिश कर रहा था। धन्यवाद yottatsa!
टिम स्टीवर्ट

4
नोट: envsubstएक GNU गेटटेक्स्ट उपयोगिता है, और वास्तव में यह सब मजबूत नहीं है (क्योंकि गेटटेक्स्ट मानव संदेशों को स्थानीय बनाने के लिए है)। सबसे महत्वपूर्ण बात, यह बैकस्लैश-बच गए $ {VAR} प्रतिस्थापनों की पहचान नहीं करता है (इसलिए आपके पास एक टेम्पलेट नहीं हो सकता है जो रन स्क्रिप्ट के समय $ VAR प्रतिस्थापन का उपयोग करता है, जैसे शेल स्क्रिप्ट या Nginx conf फ़ाइल)। एक समाधान के लिए मेरा जवाब देखें जो बैकस्लैश से बचता है।
स्टुअर्ट पी। बेंटले

4
@beporter इस मामले में, यदि आप किसी कारण से इस टेम्प्लेट को पास करना चाहते हैं, तो आप उपयोग करना चाहते हैं <<"EOF", जो कि चर (प्रक्षेपित टर्मिनेटर heredocs के एकल-कोट्स की तरह) को प्रक्षेपित नहीं करता है
स्टुअर्ट पी। बेंटले

2
मैंने इसे इस तरह इस्तेमाल किया: cat template.txt | envsubst
truthadjustr

47

envsubst मेरे लिए नया था। बहुत खुबस।

रिकॉर्ड के लिए, एक हेरेडोक का उपयोग करना एक फ़ाइल को टेम्पलेट करने का एक शानदार तरीका है।

STATUS_URI="/hows-it-goin";  MONITOR_IP="10.10.2.15";

cat >/etc/apache2/conf.d/mod_status.conf <<EOF
<Location ${STATUS_URI}>
    SetHandler server-status
    Order deny,allow
    Deny from all
    Allow from ${MONITOR_IP}
</Location>
EOF

1
मैं इस envsubstcoz से बेहतर पसंद करते हैं यह मुझे apt-get install gettext-baseमेरे Dockerfile में अतिरिक्त से बचाया
truthadjustr

बिना किसी बाहरी लाइब्रेरी इंस्टॉलेशन के बिना टेम्प्लेट जैसी स्क्रिप्ट के रूप में शेल और न ही ट्रिकी एक्सप्रेशंस का मुकाबला करने से तनाव।

35

मैं sed का उपयोग करने से सहमत हूं: यह खोज / प्रतिस्थापन के लिए सबसे अच्छा उपकरण है। यहाँ मेरा दृष्टिकोण है:

$ cat template.txt
the number is ${i}
the dog's name is ${name}

$ cat replace.sed
s/${i}/5/
s/${name}/Fido/

$ sed -f replace.sed template.txt > out.txt

$ cat out.txt
the number is 5
the dog's name is Fido

1
यह प्रतिस्थापन स्ट्रिंग के लिए अस्थायी फ़ाइल की आवश्यकता है, है ना? वहाँ एक तरीका है कि अस्थायी फ़ाइलों के बिना है?
व्लादिस्लाव रास्ट्रुसनी

@FractalizeR: के कुछ संस्करणों sed एक है -iविकल्प (जगह में संपादित फ़ाइलें) के समान है पर्ल विकल्प। अपने sed के लिए मैनपेज की जाँच करें ।
क्रिस जॉन्सन

@FractalizeR हाँ, sed -i इनलाइन को बदल देगा। यदि आप Tcl (अन्य स्क्रिप्टिंग भाषा) के साथ सहज हैं, तो इस धागे को देखें: stackoverflow.com/questions/2818130/…
Hai Vu

मैंने प्रतिस्थापित किया था। एक प्रॉपर्टी से लिया गया। निम्नलिखित सेड कमांड को सफेद करने के लिए: sed -e's / ^ / s \ / $ {/ g '-e' s / = /} \ / g '-e' s / $ / \ // जी 'the.properties> प्रतिस्थापित.sed
जाप डी

@hai vu का कोड एक sed प्रोग्राम बनाता है और sed के -f फ्लैग का उपयोग करके उस प्रोग्राम को पास करता है। यदि आप चाहते हैं, तो आप -e झंडे का उपयोग करके sed कार्यक्रम की प्रत्येक पंक्ति को sed में पास कर सकते हैं। एफडब्ल्यूआईडब्ल्यू मुझे टेम्प्लेटिंग के लिए सेड का उपयोग करने का विचार पसंद है।
एंड्रयू एलब्राइट

23

मुझे लगता है कि eval वास्तव में अच्छी तरह से काम करती है। यह लाइनब्रीक, व्हॉट्सएप और सभी प्रकार के बैश सामान के साथ टेम्पलेट्स को संभालता है। यदि आप स्वयं पाठ्यक्रम पर पूर्ण नियंत्रण रखते हैं:

$ cat template.txt
variable1 = ${variable1}
variable2 = $variable2
my-ip = \"$(curl -s ifconfig.me)\"

$ echo $variable1
AAA
$ echo $variable2
BBB
$ eval "echo \"$(<template.txt)\"" 2> /dev/null
variable1 = AAA
variable2 = BBB
my-ip = "11.22.33.44"

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

तुम भी यदि आप चाहें तो यहां दस्तावेजों का उपयोग कर सकते catकरने के लिएecho

$ eval "cat <<< \"$(<template.txt)\"" 2> /dev/null

@plockc ने एक समाधान को उकसाया जो कि बैश बोली से बचने के मुद्दे से बचता है:

$ eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null

संपादित करें: इसे sudo का उपयोग करके रूट के रूप में चलाने के बारे में निकाला गया ...

संपादित करें: उद्धरणों से बचने की आवश्यकता के बारे में जोड़ा गया टिप्पणी, मिश्रण में प्लॉक के समाधान को जोड़ा गया!


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

IMHO बैश-आधारित टेम्पलेट पागलपन हैं, क्योंकि आपको यह समझने के लिए बैश प्रोग्रामर होने की आवश्यकता है कि आपका टेम्पलेट क्या कर रहा है! लेकिन टिप्पणी के लिए धन्यवाद!
मोगसी

@ एलेक्स: यह दृष्टिकोण सिंगल कोट्स के बीच स्थानापन्न होगा , क्योंकि वे स्ट्रिंग डेलिमिटर के बजाय एन्कोडिंग डबल-कोटेड स्ट्रिंग के अंदर केवल शाब्दिक वर्ण हैं जब evalएड echo / catकमांड उन्हें प्रोसेस करते हैं; कोशिश करो eval "echo \"'\$HOME'\""
mklement0

21

मेरे पास मोगी जैसा बैश सॉल्यूशन है लेकिन हेरेडोक के साथ हेरडेज के बजाय आपको दोहरे उद्धरणों से बचने की अनुमति देने के लिए

eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null

4
यह समाधान टेम्पलेट में बैश पैरामीटर विस्तार का समर्थन करता है । मेरे पसंदीदा रहे हैं पैरामीटर आवश्यक साथ ${param:?}नहीं मिल पाता पाठ के आसपास वैकल्पिक पैरामीटर। उदाहरण: ${DELAY:+<delay>$DELAY</delay>}DELAY अपरिभाषित होने पर कुछ भी नहीं फैलता है और <देरी> 17 </ देरी> जब DELAY = 17 होता है।
एरिक बोलिंजर

2
ओह! और EOF सीमांकक पीआईडी ​​की तरह एक गतिशील स्ट्रिंग का उपयोग कर सकता है _EOF_$$
एरिक बोलिंजर

1
@ mklement0 नई कड़ियों को पीछे हटाने के लिए एक समाधान यह है कि कुछ विस्तार का उपयोग करें जैसे कि एक खाली चर $trailing_newline, या उपयोग करें $NL5और सुनिश्चित करें कि इसका विस्तार 5 नईलाइन्स के रूप में हो।
xebeche

@xebeche: हाँ, जो आप सुझाव देते हैं, उसके अंदर केtemplate.txt अंत में नई सुर्खियों को बनाए रखने के लिए काम करेगा।
mklement0

1
एक सुरुचिपूर्ण समाधान, लेकिन ध्यान दें कि कमांड प्रतिस्थापन इनपुट फ़ाइल से किसी भी अनुगामी newlines को छीन लेगा, हालांकि यह आमतौर पर एक समस्या नहीं होगी। एक और धार मामला: के उपयोग के कारण eval, यदि इसकी स्वयं की एक पंक्ति में template.txtशामिल है , तो EOFयह समय से पहले ही यहां-डॉक्टर को समाप्त कर देगा और इस प्रकार कमांड को तोड़ देगा। (@Xebeche को टोपी की टिप)।
mklement0

18

6 जनवरी, 2017 को संपादित करें

मुझे अपनी विन्यास फाइल में दोहरे उद्धरण चिह्नों को रखने की आवश्यकता थी ताकि डबल सेड के साथ दोहरे उद्धरण बच सकें:

render_template() {
  eval "echo \"$(sed 's/\"/\\\\"/g' $1)\""
}

मैं नई लाइनों को पीछे रखने के बारे में नहीं सोच सकता, लेकिन बीच में खाली लाइनें रखी जाती हैं।


हालाँकि यह एक पुराना विषय है, IMO मुझे यहाँ और अधिक सुरुचिपूर्ण समाधान मिला: http://pempek.net/articles/2013/07/08/bash-sh-as-template-engine/

#!/bin/sh

# render a template configuration file
# expand variables + preserve formatting
render_template() {
  eval "echo \"$(cat $1)\""
}

user="Gregory"
render_template /path/to/template.txt > path/to/configuration_file

सभी का श्रेय ग्रेगोरी पकोस को दिया जाता है


यह इनपुट से दोहरे उद्धरण चिह्नों को हटाता है और यदि इनपुट फ़ाइल में कई अनुगामी newlines हैं, तो उन्हें एकल के साथ बदल देता है।
mklement0

2
मुझे यह काम करने के लिए दो कम बैकस्लैम की आवश्यकता थी, अर्थात, eval "echo \"$(sed 's/\"/\\"/g' $1)\""
डेविड बाऊ

दुर्भाग्य से, यह दृष्टिकोण आपको php फ़ाइलों को टेम्पलेट करने की अनुमति नहीं देता है (वे शामिल हैं $variables)।
IStranger

10

पहिया को सुदृढ़ करने के बजाय एन्वसबस्ट के साथ जाना जाता है, लगभग किसी भी परिदृश्य में उपयोग किया जा सकता है, उदाहरण के लिए डॉक कंटेनर में पर्यावरण चर से कॉन्फ़िगरेशन फ़ाइलों का निर्माण।

अगर मैक पर सुनिश्चित करें कि आपके पास होमब्रे है तो इसे गेटटेक्स्ट से लिंक करें:

brew install gettext
brew link --force gettext

./template.cfg

# We put env variables into placeholders here
this_variable_1 = ${SOME_VARIABLE_1}
this_variable_2 = ${SOME_VARIABLE_2}

./.env:

SOME_VARIABLE_1=value_1
SOME_VARIABLE_2=value_2

./configure.sh

#!/bin/bash
cat template.cfg | envsubst > whatever.cfg

अब बस इसका उपयोग करें:

# make script executable
chmod +x ./configure.sh
# source your variables
. .env
# export your variables
# In practice you may not have to manually export variables 
# if your solution depends on tools that utilise .env file 
# automatically like pipenv etc. 
export SOME_VARIABLE_1 SOME_VARIABLE_2
# Create your config file
./configure.sh

envsubstवास्तव में काम करने का यह आह्वान क्रम है ।
एलेक्स

किसी और को देखने के लिए, envsubstMacOS पर काम नहीं करता है, आपको होमब्रे का उपयोग करके इसे स्थापित करने की आवश्यकता होगी brew install gettext:।
मैट

9

स्वीकृत उत्तर का एक लंबा लेकिन अधिक मजबूत संस्करण:

perl -pe 's;(\\*)(\$([a-zA-Z_][a-zA-Z_0-9]*)|\$\{([a-zA-Z_][a-zA-Z_0-9]*)\})?;substr($1,0,int(length($1)/2)).($2&&length($1)%2?$2:$ENV{$3||$4});eg' template.txt

यह $VAR या ${VAR} उनके पर्यावरण मूल्यों के सभी उदाहरणों का विस्तार करता है (या, यदि वे अपरिभाषित हैं, तो रिक्त स्ट्रिंग)।

यह ठीक से बैकस्लैश से बच जाता है, और प्रतिस्थापन को रोकने के लिए एक बैकस्लैश-बच गए $ को स्वीकार करता है (एनवसुबस्ट के विपरीत, जो यह निकलता है, ऐसा नहीं करता है )।

तो, अगर आपका पर्यावरण है:

FOO=bar
BAZ=kenny
TARGET=backslashes
NOPE=engi

और आपका खाका है:

Two ${TARGET} walk into a \\$FOO. \\\\
\\\$FOO says, "Delete C:\\Windows\\System32, it's a virus."
$BAZ replies, "\${NOPE}s."

परिणाम होगा:

Two backslashes walk into a \bar. \\
\$FOO says, "Delete C:\Windows\System32, it's a virus."
kenny replies, "${NOPE}s."

यदि आप केवल $ से पहले बैकस्लैश से बचना चाहते हैं (आप किसी टेम्पलेट में "C: \ Windows \ System32" को अपरिवर्तित लिख सकते हैं), इस थोड़े से संशोधित संस्करण का उपयोग करें:

perl -pe 's;(\\*)(\$([a-zA-Z_][a-zA-Z_0-9]*)|\$\{([a-zA-Z_][a-zA-Z_0-9]*)\});substr($1,0,int(length($1)/2)).(length($1)%2?$2:$ENV{$3||$4});eg' template.txt

8

मैंने इसे इस तरह से किया है, शायद कम कुशल, लेकिन पढ़ने / बनाए रखने में आसान है।

TEMPLATE='/path/to/template.file'
OUTPUT='/path/to/output.file'

while read LINE; do
  echo $LINE |
  sed 's/VARONE/NEWVALA/g' |
  sed 's/VARTWO/NEWVALB/g' |
  sed 's/VARTHR/NEWVALC/g' >> $OUTPUT
done < $TEMPLATE

10
आप लाइन-दर-लाइन पढ़े बिना और केवल एक सेड इनवोकेशन के साथ ऐसा कर सकते हैं:sed -e 's/VARONE/NEWVALA/g' -e 's/VARTWO/NEWVALB/g' -e 's/VARTHR/NEWVALC/g' < $TEMPLATE > $OUTPUT
ब्रैंडन ब्लूम

8

यदि आप Jinja2 टेम्प्लेट का उपयोग करना चाहते हैं , तो इस प्रोजेक्ट को देखें: j2cli

यह समर्थन करता है:

  • JSON, INI, YAML फ़ाइलों और इनपुट स्ट्रीम से टेम्पलेट
  • पर्यावरण चर से अस्थायी

5

ZyX से शुद्ध बैश का उपयोग करके उत्तर लेना लेकिन नई शैली regex मिलान और अप्रत्यक्ष पैरामीटर प्रतिस्थापन के साथ यह हो जाता है:

#!/bin/bash
regex='\$\{([a-zA-Z_][a-zA-Z_0-9]*)\}'
while read line; do
    while [[ "$line" =~ $regex ]]; do
        param="${BASH_REMATCH[1]}"
        line=${line//${BASH_REMATCH[0]}/${!param}}
    done
    echo $line
done

5

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

इस उत्तर का उद्देश्य केवल बैश समाधान प्रदान करना है - जिसका उपयोग करने के बावजूद - उपयोग करने के लिए सुरक्षितeval होना चाहिए ।

लक्ष्यों हैं:

  • दोनों ${name}और $nameचर संदर्भों का समर्थन विस्तार ।
  • अन्य सभी विस्तार को रोकें:
    • कमांड प्रतिस्थापन ( $(...)और विरासत सिंटैक्स `...`)
    • अंकगणित प्रतिस्थापन ( $((...))और विरासत सिंटैक्स $[...])।
  • \( \${name}) के साथ उपसर्ग करके चर विस्तार के चयनात्मक दमन की अनुमति दें ।
  • विशेष वर्णों का संरक्षण करें। इनपुट में, विशेष रूप से "और \उदाहरणों में।
  • या तो तर्कों के माध्यम से या स्टड के माध्यम से इनपुट की अनुमति दें।

समारोहexpandVars() :

expandVars() {
  local txtToEval=$* txtToEvalEscaped
  # If no arguments were passed, process stdin input.
  (( $# == 0 )) && IFS= read -r -d '' txtToEval
  # Disable command substitutions and arithmetic expansions to prevent execution
  # of arbitrary commands.
  # Note that selectively allowing $((...)) or $[...] to enable arithmetic
  # expressions is NOT safe, because command substitutions could be embedded in them.
  # If you fully trust or control the input, you can remove the `tr` calls below
  IFS= read -r -d '' txtToEvalEscaped < <(printf %s "$txtToEval" | tr '`([' '\1\2\3')
  # Pass the string to `eval`, escaping embedded double quotes first.
  # `printf %s` ensures that the string is printed without interpretation
  # (after processing by by bash).
  # The `tr` command reconverts the previously escaped chars. back to their
  # literal original.
  eval printf %s "\"${txtToEvalEscaped//\"/\\\"}\"" | tr '\1\2\3' '`(['
}

उदाहरण:

$ expandVars '\$HOME="$HOME"; `date` and $(ls)'
$HOME="/home/jdoe"; `date` and $(ls)  # only $HOME was expanded

$ printf '\$SHELL=${SHELL}, but "$(( 1 \ 2 ))" will not expand' | expandVars
$SHELL=/bin/bash, but "$(( 1 \ 2 ))" will not expand # only ${SHELL} was expanded
  • प्रदर्शन कारणों से, फ़ंक्शन स्टड इनपुट को मेमोरी में एक बार पढ़ता है , लेकिन फ़ंक्शन को लाइन-बाय-लाइन दृष्टिकोण के लिए अनुकूलित करना आसान है।
  • इसके अलावा गैर-बुनियादी चर विस्तार का समर्थन करता है ${HOME:0:10}, जब तक कि उनमें कोई एम्बेडेड कमांड या अंकगणितीय प्रतिस्थापन न हों, जैसे कि${HOME:0:$(echo 10)}
    • इस तरह के एम्बेडेड प्रतिस्थापन वास्तव में फ़ंक्शन को रोकते हैं (क्योंकि सभी $(और `उदाहरण नेत्रहीन रूप से बच गए हैं)।
    • इसी तरह, विकृत चर संदर्भ जैसे ${HOME(गुम समापन }) फ़ंक्शन को ब्रेक करते हैं।
  • बैश के दोहरे उद्धरण वाले तारों की हैंडलिंग के कारण, बैकस्लैश को निम्नानुसार संभाला जाता है:
    • \$name विस्तार को रोकता है।
    • एक के \बाद एक के $रूप में संरक्षित नहीं है।
    • यदि आप कई आसन्न \ उदाहरणों का प्रतिनिधित्व करना चाहते हैं , तो आपको उन्हें दोगुना करना होगा ; उदाहरण के लिए:
      • \\-> \- बस के रूप में ही\
      • \\\\ -> \\
    • इनपुट निम्नलिखित शामिल नहीं करना चाहिए (शायद ही कभी इस्तेमाल) वर्ण, जो आंतरिक उद्देश्यों के लिए उपयोग किया जाता है: 0x1, 0x2, 0x3
  • एक बड़े पैमाने पर काल्पनिक चिंता है कि अगर बैश को नए विस्तार वाक्यविन्यास को पेश करना चाहिए, तो यह फ़ंक्शन ऐसे विस्तार को नहीं रोक सकता है - एक समाधान के लिए नीचे देखें जो उपयोग नहीं करता है eval

यदि आप एक अधिक प्रतिबंधात्मक समाधान की${name} तलाश कर रहे हैं जो केवल विस्तार का समर्थन करता है - अर्थात, अनिवार्य घुंघराले ब्रेसिज़ के साथ, $nameसंदर्भों को अनदेखा करते हुए - मेरा यह उत्तर देखें ।


यहाँ bash-onlyeval का एक उन्नत संस्करण है, जो स्वीकृत उत्तर से मुक्त समाधान है :

सुधार हैं:

  • दोनों ${name}और $nameचर संदर्भों के विस्तार के लिए समर्थन ।
  • \चर संदर्भों के लिए समर्थन का विस्तार नहीं किया जाना चाहिए।
  • evalऊपर दिए गए समाधान के विपरीत ,
    • गैर-बुनियादी विस्तार पर ध्यान नहीं दिया जाता है
    • विकृत चर संदर्भों को अनदेखा किया जाता है (वे स्क्रिप्ट को नहीं तोड़ते हैं)
 IFS= read -d '' -r lines # read all input from stdin at once
 end_offset=${#lines}
 while [[ "${lines:0:end_offset}" =~ (.*)\$(\{([a-zA-Z_][a-zA-Z_0-9]*)\}|([a-zA-Z_][a-zA-Z_0-9]*))(.*) ]] ; do
      pre=${BASH_REMATCH[1]} # everything before the var. reference
      post=${BASH_REMATCH[5]}${lines:end_offset} # everything after
      # extract the var. name; it's in the 3rd capture group, if the name is enclosed in {...}, and the 4th otherwise
      [[ -n ${BASH_REMATCH[3]} ]] && varName=${BASH_REMATCH[3]} || varName=${BASH_REMATCH[4]}
      # Is the var ref. escaped, i.e., prefixed with an odd number of backslashes?
      if [[ $pre =~ \\+$ ]] && (( ${#BASH_REMATCH} % 2 )); then
           : # no change to $lines, leave escaped var. ref. untouched
      else # replace the variable reference with the variable's value using indirect expansion
           lines=${pre}${!varName}${post}
      fi
      end_offset=${#pre}
 done
 printf %s "$lines"

5

यहाँ एक और शुद्ध बैश समाधान है:

  • यह हेरेडोक का उपयोग कर रहा है, इसलिए:
    • अतिरिक्त आवश्यक सिंटैक्स के कारण जटिलता नहीं बढ़ती है
    • टेम्पलेट बैश कोड शामिल कर सकते हैं
      • यह भी आपको सामान ठीक से इंडेंट करने की अनुमति देता है। निचे देखो।
  • यह eval का उपयोग नहीं करता है, इसलिए:
    • खाली लाइनों के अनुशीलन के प्रतिपादन में कोई समस्या नहीं है
    • टेम्पलेट में उद्धरण के साथ कोई समस्या नहीं है

$ cat code

#!/bin/bash
LISTING=$( ls )

cat_template() {
  echo "cat << EOT"
  cat "$1"
  echo EOT
}

cat_template template | LISTING="$LISTING" bash

$ cat template (अनुगामी newlines और दोहरे उद्धरण के साथ)

<html>
  <head>
  </head>
  <body> 
    <p>"directory listing"
      <pre>
$( echo "$LISTING" | sed 's/^/        /' )
      <pre>
    </p>
  </body>
</html>

उत्पादन

<html>
  <head>
  </head>
  <body> 
    <p>"directory listing"
      <pre>
        code
        template
      <pre>
    </p>
  </body>
</html>

4

यहां एक और समाधान है: सभी चर और टेम्पलेट फ़ाइल की सामग्री के साथ एक बैश स्क्रिप्ट उत्पन्न करें, यह स्क्रिप्ट इस तरह दिखाई देगी:

word=dog           
i=1                
cat << EOF         
the number is ${i} 
the word is ${word}

EOF                

यदि हम इस स्क्रिप्ट को बैश में खिलाते हैं तो यह वांछित आउटपुट का उत्पादन करेगा:

the number is 1
the word is dog

यहाँ उस स्क्रिप्ट को जेनरेट करने और उस स्क्रिप्ट को बैश में फीड करने का तरीका दिया गया है:

(
    # Variables
    echo word=dog
    echo i=1

    # add the template
    echo "cat << EOF"
    cat template.txt
    echo EOF
) | bash

विचार-विमर्श

  • कोष्ठक एक उप खोल को खोलता है, इसका उद्देश्य उत्पन्न सभी उत्पादन को एक साथ समूहित करना है
  • उप शेल के भीतर, हम सभी चर घोषणाओं को उत्पन्न करते हैं
  • उप शेल में भी, हम catHEREDOC के साथ कमांड उत्पन्न करते हैं
  • अंत में, हम उप-खोल आउटपुट को काटने और वांछित आउटपुट का उत्पादन करने के लिए फ़ीड करते हैं
  • यदि आप इस आउटपुट को किसी फ़ाइल में पुनर्निर्देशित करना चाहते हैं, तो अंतिम पंक्ति को इसके साथ बदलें:

    ) | bash > output.txt


3

Shtpl के लिए बिल्कुल सही मामला । (मेरा प्रोजेक्ट, इसलिए यह व्यापक रूप से उपयोग में नहीं है और दस्तावेज़ीकरण में कमी है। लेकिन यहां समाधान है जो किसी भी तरह प्रदान करता है। मई आप इसे परीक्षण करना चाहते हैं।)

बस निष्पादित करें:

$ i=1 word=dog sh -c "$( shtpl template.txt )"

परिणाम है:

the number is 1
the word is dog

मज़े करो।


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

मैं जोड़ना चाहता हूं, कि मैं वास्तव में कल usecases के लिए खोज की है, जहां shtpl एक सही समाधान होगा। हाँ, मैं ऊब गया था ...
zstegi

3
# Usage: template your_file.conf.template > your_file.conf
template() {
        local IFS line
        while IFS=$'\n\r' read -r line ; do
                line=${line//\\/\\\\}         # escape backslashes
                line=${line//\"/\\\"}         # escape "
                line=${line//\`/\\\`}         # escape `
                line=${line//\$/\\\$}         # escape $
                line=${line//\\\${/\${}       # de-escape ${         - allows variable substitution: ${var} ${var:-default_value} etc
                # to allow arithmetic expansion or command substitution uncomment one of following lines:
#               line=${line//\\\$\(/\$\(}     # de-escape $( and $(( - allows $(( 1 + 2 )) or $( command ) - UNSECURE
#               line=${line//\\\$\(\(/\$\(\(} # de-escape $((        - allows $(( 1 + 2 ))
                eval "echo \"${line}\"";
        done < "$1"
}

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


1

आप बशीबल का उपयोग भी कर सकते हैं (जो आंतरिक रूप से ऊपर / नीचे वर्णित मूल्यांकन दृष्टिकोण का उपयोग करता है)।

एक उदाहरण है, HTML को कई भागों से कैसे उत्पन्न किया जाए:

https://github.com/mig1984/bashible/tree/master/examples/templates


0

यहाँ एक बश फ़ंक्शन है जो व्हॉट्सएप को संरक्षित करता है:

# Render a file in bash, i.e. expand environment variables. Preserves whitespace.
function render_file () {
    while IFS='' read line; do
        eval echo \""${line}"\"
    done < "${1}"
}

0

यहां perlकुछ अन्य उत्तरों के आधार पर एक संशोधित स्क्रिप्ट दी गई है:

perl -pe 's/([^\\]|^)\$\{([a-zA-Z_][a-zA-Z_0-9]*)\}/$1.$ENV{$2}/eg' -i template

सुविधाएँ (मेरी आवश्यकताओं के आधार पर, लेकिन इसे संशोधित करना आसान होना चाहिए):

  • स्केप्स पैरामीटर विस्तार (जैसे \ $ {VAR}) से बच गए।
  • $ {VAR} फॉर्म के पैरामीटर विस्तार का समर्थन करता है, लेकिन $ VAR का नहीं।
  • यदि कोई VAR envar नहीं है, तो रिक्त स्ट्रिंग के साथ $ {VAR} को प्रतिस्थापित करता है।
  • केवल नाम में az, AZ, 0-9 और अंडरस्कोर वर्णों का समर्थन करता है (पहली स्थिति में अंकों को छोड़कर)।

0

सरल चर प्रतिस्थापन अजगर लिपि को यहां देखें: https://github.com/jeckep/vsubst

इसे इस्तेमाल करना बहुत आसान है:

python subst.py --props secure.properties --src_path ./templates --dst_path ./dist

0

इस अद्भुत प्रश्न के लिए मेरा विनम्र योगदान।

tpl() {
    local file=$(cat - | \
                 sed -e 's/\"/\\"/g' \
                     -e "s/'/\\'/g")
    local vars=$(echo ${@} | tr ' ' ';')
    echo "$(sh -c "${vars};echo \"$file\"")"
}

cat tpl.txt | tpl "one=fish" "two=fish"

यह अनिवार्य रूप से एनवार प्रतिस्थापन करने के लिए उपधारा का उपयोग करके काम करता है, सिवाय इसके कि यह eval का उपयोग नहीं करता है और यह स्पष्ट रूप से सिंगल और डबल कोट्स से बच जाता है। यह var भावों को एक ही लाइन में समेटता है, जिसमें कोई स्पेस नहीं है कि भ्रमित न हों shऔर फिर टेम्पलेट को पास करते हैं echo, जिससे वेरिएशन को shहैंडल किया जा सके। यह newlines, टिप्पणियों आदि को संरक्षित करता है ... और \${like_this}यदि आप चाहते हैं कि var की व्याख्या न की जाए तो आप बच सकते हैं । ${missing_var}बस खाली मूल्य के साथ बदल दिया जाएगा।

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

का आनंद लें!


0

इस पेज पर plockc के उत्तर का पालन ​​करने के लिए , यहां डैश-उपयुक्त संस्करण है, जो आप में से बशियों से बचने के लिए देख रहे हैं।

eval "cat <<EOF >outputfile
$( cat template.in )
EOF
" 2> /dev/null

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