Bash Templating: Bash ile şablonlardan yapılandırma dosyaları nasıl oluşturulur?


134

Kendi web sunucum için Apache ve PHP için yapılandırma dosyaları oluşturmayı otomatikleştirmek için bir komut dosyası yazıyorum. CPanel veya ISPConfig gibi herhangi bir GUI kullanmak istemiyorum.

Apache ve PHP yapılandırma dosyalarının bazı şablonlarına sahibim. Bash komut dosyasının şablonları okuması, değişken ikamesi yapması ve ayrıştırılmış şablonları bazı klasörlere çıkarması gerekir. Bunu yapmanın en iyi yolu nedir? Birkaç yol düşünebilirim. Hangisi en iyisi veya bunu yapmanın daha iyi yolları var mı? Bunu saf Bash ile yapmak istiyorum (örneğin PHP'de kolaydır)

1) Bir metin dosyasındaki $ {} yer tutucuları nasıl değiştirilir?

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, çıktıyı burada harici bir dosyaya nasıl yönlendirebilirim? Değişkenler, örneğin tırnak işaretleri içeriyorsa bir şeyden kaçmam gerekir mi?

2) Her değişkeni değeriyle değiştirmek için cat & sed'i kullanma:

Template.txt verildiğinde:

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

Komut:

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

Birçok farklı sembolden kaçma ihtiyacından dolayı bana kötü görünüyor ve birçok değişkenle birlikte satır çok uzun olacak.

Başka zarif ve güvenli bir çözüm düşünebilir misiniz?


Yanıtlar:


62

Bunu kullanabilirsiniz:

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

tüm ${...}dizeleri karşılık gelen ortam değişkenleriyle değiştirmek için (bu betiği çalıştırmadan önce bunları dışa aktarmayı unutmayın).

Saf bash için bu çalışmalıdır (değişkenlerin $ {...} dizeleri içermediğini varsayarak):

#!/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 kendine referans veren bazı değişkenlere başvurursa takılmayan çözüm:

#!/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}"

UYARI : bash'ta NUL'larla girdileri doğru bir şekilde ele almanın veya sondaki yeni satırların miktarını korumanın bir yolunu bilmiyorum. Son varyant olduğu gibi sunulmuştur çünkü kabuklar ikili girişi "sever":

  1. read ters eğik çizgileri yorumlayacaktır.
  2. read -r ters eğik çizgileri yorumlamaz, ancak bir satırsonu ile bitmezse son satırı bırakacaktır.
  3. "$(…)"Mevcut vardır gibi son nedenle, çok sayıda arka yeni satır olarak şerit olacak olan ; echo -n ave kullanımı echo -n "${line:0:-1}"bu son (bir karakter düşer: a(bir de dahil olmak üzere), giriş orada olduğu gibi birçok arka yeni satır gibi) ve korur.

3
Ben değiştirecek [^}]kadar [A-Za-Z_][A-Za-z0-9_](bu süreç çalışırsa örn sıkı ikamesi ötesine gitmekten kabuk önlemek için bash versiyonunda ${some_unused_var-$(rm -rf $HOME)}).
Chris Johnsen

2
@FractalizeR $&perl çözümünü şu şekilde değiştirmek isteyebilirsiniz "": ilk önce ${...}ikame edilemiyorsa dokunulmadan bırakır , ikincisi boş dizgiyle değiştirir .
ZyX

5
NOT: Görünüşe göre, normal ifadenin etrafındaki tek tırnakların - normal ifadenin içeriğini bir dize değişmezi olarak ele aldığı bash 3.1'den 3.2'ye (ve üstü) bir değişiklik olmuştur. Dolayısıyla yukarıdaki normal ifade ... (\ $ \ {[a-zA-Z _] [a-zA-Z_0-9] * \}) stackoverflow.com/questions/304864/…
Blue Waters

2
whileDöngünün bir satırsonu ile sonlandırılmamış olsa bile son satırı okumasını sağlamak için kullanın while read -r line || [[ -n $line ]]; do. Ek olarak, readkomutunuz her satırın başındaki ve sonundaki boşlukları çıkarır; bundan kaçınmak için kullanınwhile IFS= read -r line || [[ -n $line ]]; do
mklement0

2
Kapsamlı bir çözüm arayanlar için bir kısıtlamaya dikkat etmek gerekirse: Aksi takdirde kullanışlı olan bu çözümler, değişken referansları seçmeli olarak genişletmeden korumanıza izin vermez (örneğin \ bunları -escaping ile).
mklement0

139

Deneyin envsubst

FOO=foo
BAR=bar
export FOO BAR

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

12
Sadece referans olması için, envsubstheredoc kullanırken gerekli değildir , çünkü bash heredok'u gerçek çift tırnaklı bir dizge olarak ele alır ve zaten içindeki değişkenleri enterpolasyonlar. Şablonu başka bir dosyadan okumak istediğinizde harika bir seçimdir. Çok daha hantal olanlar için iyi bir yedek m4.
beporter

2
Bu komutu öğrenince çok şaşırdım. Envsubst'un işlevselliğini manuel olarak sıfır başarı ile kaldırmaya çalışıyordum. Teşekkürler yottatsa!
Tim Stewart

4
Not: envsubstbir GNU gettext aracıdır ve aslında o kadar sağlam değildir (çünkü gettext insan mesajlarını yerelleştirmek içindir). En önemlisi, ters eğik çizgiden kaçan $ {VAR} ikamelerini tanımıyor (bu nedenle, bir kabuk komut dosyası veya Nginx conf dosyası gibi çalışma zamanında $ VAR ikamelerini kullanan bir şablonunuz olamaz). Bkz Cevabımı kolları kaçar kullanılan karakterler bir çözüm.
Stuart P. Bentley

4
@beporter Bu durumda, herhangi bir nedenle bu şablonu envsubst'a geçirmek istiyorsanız <<"EOF", değişkenleri interpolasyon yapmayan (alıntı sonlandırıcılar yorumlu metinlerin tek tırnak işaretleri gibidir) kullanmak istersiniz.
Stuart P. Bentley

2
Bunu şöyle kullandım: cat template.txt | envsubst
truthadjustr

47

envsubst benim için yeniydi. Fantastik.

Kayıt için yorumlu metin kullanmak, bir conf dosyasını şablonlamak için harika bir yoldur.

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
envsubstBunu apt-get install gettext-baseDockerfile dosyamdaki eklerden kurtardığı coz'dan daha iyi tercih ederim
truthadjustr

Kabuk, Şablon benzeri bir betik olarak, ancak herhangi bir harici kitaplık kurulumu veya zor ifadelerle başa çıkma stresi olmadan.
千 木 郷

36

Sed kullanmaya katılıyorum: arama / değiştirme için en iyi araçtır. İşte benim yaklaşımım:

$ 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
Bu, ikame dizesi için geçici dosya gerektirir, değil mi? Bunu geçici dosyalar olmadan yapmanın bir yolu var mı?
Vladislav Rastrusny

@FractalizeR: Bazı sed sürümlerinde perl seçeneğine -ibenzer bir seçenek (dosyaları yerinde düzenleme) bulunur . Senin için manpage edin sed .
Chris Johnsen

@FractalizeR Evet, sed -i satır içi yerine geçecek. Tcl (başka bir betik dili) konusunda
Hai Vu

Replace.sed'i aşağıdaki sed komutuyla bir özellik dosyasından oluşturdum: sed -e 's / ^ / s \ / $ {/ g' -e 's / = /} \ // g' -e 's / $ / \ // g 'the.properties> replace.sed
Jaap D

@hai vu'nun kodu bir sed programı oluşturur ve bu programı sed'in -f bayrağını kullanarak geçirir. İsterseniz, sed programının her satırını -e bayraklarını kullanarak sed'e geçirebilirsiniz. FWIW Şablon oluşturma için sed kullanma fikrini seviyorum.
Andrew Allbright

23

Bence eval gerçekten iyi çalışıyor. Satır kesmeleri, boşluklar ve her tür bash öğesiyle şablonları yönetir. Elbette şablonlar üzerinde tam kontrole sahipseniz:

$ 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 keyfi kod çalıştırabildiğinden, bu yöntem elbette dikkatli kullanılmalıdır. Bunu root olarak çalıştırmak söz konusu olamaz. Şablondaki alıntılardan kaçınılması gerekir, aksi takdirde yenileceklerdir eval.

Dilerseniz buradaki belgeleri de kullanabilirsiniz cat.echo

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

@plockc, bash alıntı sorununu ortadan kaldıran bir çözüm geliştirdi:

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

Düzenleme: Bunu sudo kullanarak root olarak çalıştırmayla ilgili bölüm kaldırıldı ...

Düzenleme: Alıntılardan nasıl kaçılacağına dair yorum eklendi, karışıma plockc'un çözümü eklendi!


Bu, şablonunuzda sahip olduğunuz alıntıları çıkarır ve tek tırnak işaretlerinin yerine geçmez, bu nedenle şablon biçiminize bağlı olarak küçük hatalara yol açabilir. Yine de, bu muhtemelen herhangi bir Bash tabanlı şablonlama yöntemi için geçerlidir.
Alex B

IMHO Bash tabanlı şablonlar çılgınlıktır, çünkü şablonunuzun ne yaptığını anlamak için bir bash programcısı olmanız gerekir! Ama yorum için teşekkürler!
mogsie

@AlexB: Bu yaklaşım , ed / komutları onları işlerken dize sınırlayıcıları yerine, çevreleyen çift tırnaklı dizenin içindeki harf karakterleri oldukları için tek tırnakların yerini alacaktır ; deneyin . evalechocateval "echo \"'\$HOME'\""
mklement0

21

Mogsie gibi bir bash çözümüm var, ancak çift tırnaktan kaçmaktan kaçınmanıza izin vermek için herestring yerine heredoc ile

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

4
Bu çözüm , şablonda Bash parametresi genişletmeyi destekler . Favorilerim, isteğe bağlı parametrelerin etrafına yerleştirilen ve metin içeren gerekli parametrelerdir. Örnek: GECİKME tanımlanmadığında hiçbir şeye genişler ve GECİKME = 17 olduğunda <delay> 17 </delay> genişler. ${param:?}${DELAY:+<delay>$DELAY</delay>}
Eric Bolinger

2
Ah! Ve EOF sınırlayıcı, PID gibi dinamik bir dizi kullanabilir _EOF_$$.
Eric Bolinger

1
@ mklement0 Son satırları takip etmek için bir çözüm, örneğin boş bir değişken gibi bazı genişletmeler $trailing_newlinekullanmak veya $NL55 satırsonu olarak genişletildiğinden emin olmaktır.
xebeche

@xebeche: Evet, son satırları korumak için önerdiğiniz şeyi sonuna yerleştirmektemplate.txt işe yarayacaktır.
mklement0

1
Zarif bir çözüm, ancak komut ikamesinin giriş dosyasından sonraki satırsonlarını çıkaracağını unutmayın, ancak bu genellikle bir sorun teşkil etmez. Başka kenarı durumda: sebebiyle kullanımına evaleğer template.txtiçeren EOFkendine ait bir çizgi üzerinde, bu zamanından önce burada-doc sonlandırmak ve böylece komutu kırarım. (Şapkanın ucu @xebeche).
mklement0

18

Düzenle 6 Oca 2017

Yapılandırma dosyamda çift tırnak tutmam gerekiyordu, bu nedenle sed ile çift tırnaktan kaçış yapmak yardımcı oluyor:

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

Yeni satırları takip etmeyi düşünemiyorum ama arada boş satırlar tutuluyor.


Eski bir konu olmasına rağmen, IMO burada daha zarif bir çözüm buldum: 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

Tüm krediler Grégory Pakosz'a .


Bu, girişten çift tırnak işaretlerini kaldırır ve giriş dosyasında birden fazla satırsonu varsa, bunları tek bir satır ile değiştirir.
mklement0

2
Çalışması için iki tane daha az ters eğik çizgiye ihtiyacım vardı, yani, eval "echo \"$(sed 's/\"/\\"/g' $1)\""
David Bau

Ne yazık ki, bu yaklaşım php dosyalarını (içerirler $variables) şablonlamanıza izin vermez .
IStranger

10

Tekerleği yeniden icat etmek yerine envsubst ile gidin Hemen hemen her senaryoda kullanılabilir, örneğin docker konteynerlerindeki ortam değişkenlerinden yapılandırma dosyaları oluşturmak gibi.

Mac kullanıyorsanız, homebrew'e sahip olduğunuzdan emin olun, ardından gettext'ten bağlayın :

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

Şimdi sadece onu kullanın:

# 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

bu çağrı dizisi envsubstgerçekten işe yarıyor.
Alex

envsubstMacOS'ta çalışmayan başka biri için, onu homebrew: kullanarak yüklemeniz gerekir brew install gettext.
Matt

9

Kabul edilen cevabın daha uzun ama daha sağlam bir versiyonu:

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

Bu, tüm örneklerini $VAR veya ${VAR} ortam değerlerine (veya tanımlanmamışsa, boş dizeye) genişletir .

Düzgün bir şekilde ters eğik çizgilerden kaçar ve ikameyi engellemek için ters eğik çizgiden kaçan bir $ kabul eder (envsubst'un aksine, bunu yapmaz ).

Öyleyse, çevreniz:

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

ve şablonunuz:

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

sonuç şöyle olacaktır:

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

Yalnızca $ 'dan önce ters eğik çizgilerden kaçınmak istiyorsanız (değişmeden bir şablona "C: \ Windows \ System32" yazabilirsiniz), bu biraz değiştirilmiş sürümü kullanın:

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

Bu şekilde yapardım, muhtemelen daha az verimli, ancak okuması / bakımı daha kolay.

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
Bunu satır satır okumadan ve yalnızca bir sedan çağırma ile yapabilirsiniz:sed -e 's/VARONE/NEWVALA/g' -e 's/VARTWO/NEWVALB/g' -e 's/VARTHR/NEWVALC/g' < $TEMPLATE > $OUTPUT
Brandon Bloom

8

Jinja2 şablonlarını kullanmak istiyorsanız , bu projeye bakın: j2cli .

Destekler:

  • JSON, INI, YAML dosyalarından ve giriş akışlarından şablonlar
  • Ortam değişkenlerinden şablon oluşturma

5

Yanıtı saf bash kullanarak ZyX'ten almak, ancak yeni stil normal ifade eşleşmesi ve dolaylı parametre ikamesi ile şu hale gelir:

#!/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

Perl kullanmak bir seçenekse ve genişletmeleri yalnızca ortam değişkenlerine dayandırmaktan memnunsanız (tüm kabuk değişkenlerinin aksine ), Stuart P. Bentley'in sağlam cevabını düşünün .

Bu cevap, kullanımına rağmen - kullanımı güvenli olması gereken tek bash çözümü sağlamayı amaçlamaktadır .eval

Hedefler şunlardır:

  • Hem Destek genişlemesi ${name}ve $namedeğişken referanslar.
  • Diğer tüm genişletmeleri önleyin:
    • komut ikameleri ( $(...)ve eski sözdizimi `...`)
    • aritmetik ikameler ( $((...))ve eski sözdizimi $[...]).
  • Önek \( \${name}) ile değişken genişletmenin seçici olarak bastırılmasına izin verin .
  • Özel karakterleri koruyun. girişte, özellikle "ve \örneklerde.
  • Bağımsız değişkenler veya stdin aracılığıyla girişe izin verin.

İşlevexpandVars() :

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' '`(['
}

Örnekler:

$ 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
  • Performans nedenleriyle, işlev stdin girişini tek seferde belleğe okur , ancak işlevi satır satır yaklaşıma uyarlamak kolaydır.
  • Ayrıca gömülü komut veya aritmetik ikameler içermedikleri sürece temel olmayan değişken genişletmeleri de destekler ${HOME:0:10}.${HOME:0:$(echo 10)}
    • Bu tür gömülü ikameler aslında işlevi KIRIR (çünkü tüm örnekler $(ve `örnekler körü körüne kaçmıştır).
    • Benzer şekilde, ${HOME(eksik kapanış }) gibi hatalı biçimlendirilmiş değişken referansları işlevi BREAK.
  • Bash'in çift tırnaklı dizeleri işlemesi nedeniyle, ters eğik çizgiler şu şekilde işlenir:
    • \$name genişlemeyi engeller.
    • \Takip edilmeyen bir tek $olduğu gibi korunur.
    • Birden çok bitişik \ örneği temsil etmek istiyorsanız , bunları ikiye katlamanız gerekir ; Örneğin:
      • \\-> \- aynısı\
      • \\\\ -> \\
    • İç amaçlı kullanılan aşağıdaki içermemelidir girişi (nadiren kullanılır) karakterler,: 0x1, 0x2, 0x3.
  • Bash'in yeni genişletme sözdizimi sunması durumunda, bu işlevin bu tür genişletmeleri engellemeyeceğine dair büyük ölçüde varsayımsal bir endişe var - kullanmayan bir çözüm için aşağıya bakın eval.

Yalnızca genişletmeleri destekleyen daha kısıtlayıcı bir çözüm${name} arıyorsanız - yani zorunlu kaşlı ayraçlarla, $namereferansları göz ardı ederek - bu cevaba bakın .


Kabul edilen yanıttan yalnızca bash- evaliçermeyen çözümün geliştirilmiş bir versiyonu :

İyileştirmeler:

  • Her iki genişletilmesi için destekleyin ${name}ve $namedeğişken referanslar.
  • \Genişletilmemesi gereken -escaping değişken referansları için destek .
  • evalYukarıdaki tabanlı çözümün aksine ,
    • temel olmayan genişletmeler göz ardı edilir
    • hatalı biçimlendirilmiş değişken referansları göz ardı edilir (betiği bozmazlar)
 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

İşte başka bir saf bash çözümü:

  • heredoc kullanıyor, bu yüzden:
    • Ek gerekli sözdizimi nedeniyle karmaşıklık artmaz
    • şablon bash kodunu içerebilir
      • bu aynı zamanda öğelerin girintisini doğru şekilde yapmanıza da olanak tanır. Aşağıya bakınız.
  • eval kullanmaz, bu nedenle:
    • takip eden boş satırların oluşturulmasında sorun yok
    • şablondaki alıntılarla ilgili sorun yok

$ cat code

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

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

cat_template template | LISTING="$LISTING" bash

$ cat template (son satırlar ve çift tırnak işaretleri ile)

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

çıktı

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

4

İşte başka bir çözüm: şablon dosyasının tüm değişkenleri ve içerikleriyle bir bash betiği oluşturun, bu komut dosyası şöyle görünecektir:

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

EOF                

Bu betiği bash'a beslersek, istenen çıktıyı üretir:

the number is 1
the word is dog

İşte bu komut dosyasını nasıl oluşturacağınız ve bu komut dosyasını bash'a nasıl besleyeceğiniz aşağıda açıklanmıştır:

(
    # Variables
    echo word=dog
    echo i=1

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

Tartışma

  • Parantezler bir alt kabuğu açar, amacı üretilen tüm çıktıları gruplamaktır.
  • Alt kabuk içinde, tüm değişken bildirimlerini üretiyoruz
  • Ayrıca alt kabukta catHEREDOC ile komutu üretiyoruz.
  • Son olarak, alt kabuk çıktısını bash'a besliyoruz ve istenen çıktıyı üretiyoruz
  • Bu çıktıyı bir dosyaya yeniden yönlendirmek istiyorsanız, son satırı şu şekilde değiştirin:

    ) | bash > output.txt

3

Bu sayfada awk ile bir cevap açıklanmaktadır

awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < input.txt > output.txt

Bu, tüm alıntıları sağlam tutar. Harika!
Pepster

3

Shtpl için mükemmel bir durum . (Benim projem, bu yüzden yaygın olarak kullanılmıyor ve dokümantasyondan yoksun. Ama işte yine de sunduğu çözüm. Test etmek isteyebilirsin.)

Sadece yürütün:

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

Sonuç:

the number is 1
the word is dog

İyi eğlenceler.


1
Eğer saçmalıksa, yine de reddedilir. Ve bunda sorun yok. Ama tamam, açıkça görülemeyen nokta, aslında benim projem. Gelecekte daha görünür hale getirecek. Yorumunuz ve zaman ayırdığınız için yine de teşekkür ederim.
zstegi

Shtpl'nin mükemmel bir çözüm olacağı dün gerçekten kullanım durumlarını aradığımı eklemek istiyorum. Evet, sıkıldım ...
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"
}

Bu, sizin beğeninize göre ayarlanabilen, üretimde kullanılan ve herhangi bir girdide kırılmaması gereken saf bash işlevidir. Kırılırsa - bana bildirin.



0

İşte beyaz boşluğu koruyan bir bash işlevi:

# 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

İşte perldiğer yanıtlardan birkaçına dayalı değiştirilmiş bir komut dosyası:

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

Özellikler (ihtiyaçlarıma bağlıdır, ancak değiştirilmesi kolay olmalıdır):

  • Çıkış karakterli parametre genişletmelerini atlar (örneğin \ $ {VAR}).
  • $ {VAR} biçimindeki parametre genişletmelerini destekler, ancak $ VAR'ı desteklemez.
  • VAR listesi yoksa $ {VAR} 'ı boş bir dizeyle değiştirir.
  • Adda yalnızca az, AZ, 0-9 ve alt çizgi karakterlerini destekler (ilk konumdaki rakamlar hariç).


0

Bu sayfadaki plockc cevabını takip etmek için , işte bashizmlerden kaçınmak isteyenler için kısa çizgi için uygun bir versiyon.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.