Bash Templating : Bash를 사용하여 템플릿에서 구성 파일을 작성하는 방법은 무엇입니까?


134

내 웹 서버의 Apache 및 PHP 구성 파일 작성을 자동화하는 스크립트를 작성 중입니다. CPanel 또는 ISPConfig와 같은 GUI를 사용하고 싶지 않습니다.

Apache 및 PHP 구성 파일의 템플릿이 있습니다. Bash 스크립트는 템플릿을 읽고 변수를 대체하며 구문 분석 된 템플릿을 일부 폴더에 출력해야합니다. 가장 좋은 방법은 무엇입니까? 몇 가지 방법을 생각할 수 있습니다. 어느 것이 가장 좋거나 더 좋은 방법이 있습니까? 순수한 Bash에서 그렇게하고 싶습니다 (예를 들어 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) cat & sed를 사용하여 각 변수를 값으로 대체하십시오.

주어진 template.txt :

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

모든 ${...}문자열을 해당 환경 변수로 바꿉니다 (이 스크립트를 실행하기 전에 반드시 내보내는 것을 잊지 마십시오).

순수 bash의 경우 변수가 $ {...} 문자열을 포함하지 않는다고 가정하면 다음과 같이 작동합니다.

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

경고 : bash에서 NUL로 입력을 올바르게 처리하거나 후행 줄 바꿈 양을 보존하는 방법을 모르겠습니다. 쉘은 바이너리 입력을 "사랑"하기 때문에 마지막 변형이 그대로 제공됩니다.

  1. read 백 슬래시를 해석합니다.
  2. read -r 백 슬래시를 해석하지 않지만 줄 바꿈으로 끝나지 않으면 마지막 줄을 삭제합니다.
  3. "$(…)"본 있기 때문에 I 종료되도록 다수의 후미로 바꿈 벗길 으로 ; echo -n a사용 echo -n "${line:0:-1}"이 지난 (캐릭터 상품이다 : a(더 포함한다)에 입력 없었다만큼 후미로 바꿈) 및 보존한다.

3
내가 바꿀 것 [^}][A-Za-Z_][A-Za-z0-9_](이 과정을 시도하는 경우 예를 들어, 엄격한 대체 넘어에서 쉘을 방지하기 위해 배쉬 버전 ${some_unused_var-$(rm -rf $HOME)}).
Chris Johnsen

2
@FractalizeR $&펄 솔루션에서 ""다음 과 같이 변경하고 싶을 수도 있습니다 ${...}.
ZyX

5
참고 : 분명히 bash 3.1에서 3.2로 변경되어 정규 표현식 주위의 작은 따옴표가 정규 표현식의 내용을 문자열 리터럴로 취급합니다. 따라서 위의 정규식은 ... (\ $ \ {[a-zA-Z _] [a-zA-Z_0-9] * \}) stackoverflow.com/questions/304864/…
Blue Waters

2
while줄 바꿈으로 끝나지 않더라도 루프가 마지막 줄을 읽게 하려면을 사용하십시오 while read -r line || [[ -n $line ]]; do. 또한 read명령은 각 행에서 앞뒤 공백을 제거합니다. 이를 피하기 위해 다음을 사용하십시오while IFS= read -r line || [[ -n $line ]]; do
mklement0

2
포괄적 인 솔루션을 찾는 사람들에게 제약 조건을 참고하십시오. 이처럼 편리한 솔루션을 사용하면 변수 참조를 확장 (예 : \ 이스케이프 처리) 으로부터 선택적으로 보호 할 수 없습니다 .
mklement0

138

시험 envsubst

FOO=foo
BAR=bar
export FOO BAR

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

12
envsubstbash는 heredoc을 리터럴 큰 따옴표로 묶은 문자열로 취급하고 이미 변수를 보간하기 때문에 참조 를 위해 heredoc을 사용할 때 필요하지 않습니다. 그래도 다른 파일에서 템플릿을 읽으려는 경우 훌륭한 선택입니다. 훨씬 더 번거로운 대체품 m4.
beporter

2
나는이 명령에 대해 매우 즐겁게 놀랐습니다. 나는 envsubst의 기능을 수동으로 조롱하려고했지만 성공하지 못했습니다. 고마워요
팀 스튜어트

4
참고 : envsubstGNU gettext 유틸리티이며 gettext가 사람 메시지를 지역화하기위한 것이기 때문에 실제로 강력하지는 않습니다. 가장 중요한 것은 백 슬래시로 이스케이프 된 $ {VAR} 대체를 인식하지 못하기 때문에 런타임에 쉘 스크립트 나 Nginx conf 파일과 같이 $ VAR 대체를 사용하는 템플릿을 가질 수 없다는 것입니다. 보다백 슬래시 이스케이프를 처리하는 솔루션에 대한 내 대답 을 .
스튜어트 P. 벤틀리

4
@beporter이 경우 어떤 이유로이 템플릿을 envsubst에 전달하려면 사용하고 싶을 것입니다 <<"EOF" 한다, 하지 않는다 (인용 터미네이터가 heredocs의 작은 따옴표처럼) 보간 변수를.
스튜어트 P. 벤틀리

2
나는 그것을 다음과 같이 사용했다 : cat template.txt | envsubst
truthadjustr

47

envsubst는 나에게 새로운 것이 었습니다. 환상적인.

레코드를 위해 heredoc을 사용하는 것이 conf 파일을 템플릿으로 만드는 좋은 방법입니다.

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
나는 이것이 Dockerfile envsubst의 추가 파일 apt-get install gettext-base에서 저를 구하기 위해 coz 보다 이것을 더 좋아합니다
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
대체 문자열을위한 임시 파일이 필요합니까? 임시 파일없이 그렇게 할 수있는 방법이 있습니까?
Vladislav Rastrusny

@FractalizeR : 일부 버전의 sed 에는 perl 옵션 -i과 유사한 옵션 (파일 편집)이 있습니다. sed 의 맨 페이지를 확인하십시오 .
Chris Johnsen 7:27에

@FractalizeR 예, sed -i는 인라인을 대체합니다. Tcl (다른 스크립트 언어)에 익숙한 경우이 스레드를 확인하십시오. stackoverflow.com/questions/2818130/…
Hai Vu

다음 sed 명령을 사용하여 속성 파일에서 replace.sed를 만들었습니다. sed -e 's / ^ / s \ / $ {/ g'-e 's / = /} \ // g'-e 's / $ / \ // g 'the.properties> replace.sed
Jaap D

@hai vu의 코드는 sed 프로그램을 작성하고 sed의 -f 플래그를 사용하여 해당 프로그램을 전달합니다. 원하는 경우, -e 플래그를 사용하여 sed 프로그램의 각 행을 sed로 전달할 수 있습니다. FWIW 템포 팅에 sed를 사용한다는 아이디어가 마음에 듭니다.
Andrew Allbright

23

나는 평가가 정말 잘 작동한다고 생각합니다. 줄 바꿈, 공백 및 모든 종류의 bash 항목이있는 템플릿을 처리합니다. 물론 템플릿 자체를 완전히 제어 할 수있는 경우 :

$ 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.

당신이 원하는 경우도 여기에 문서를 사용할 수 있습니다 catecho

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

@plockc는 bash quote escaping 문제를 피하는 솔루션을 제안했습니다.

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

편집 : sudo를 사용하여 루트로 이것을 실행하는 것에 대한 부분을 제거했습니다 ...

편집 : 따옴표를 이스케이프 처리하는 방법에 대한 의견을 추가하고 믹스에 plockc의 솔루션을 추가했습니다!


템플릿에 포함 된 따옴표를 제거하고 작은 따옴표를 대체하지 않으므로 템플릿 형식에 따라 미묘한 버그가 발생할 수 있습니다. 이것은 아마도 모든 Bash 기반 템플릿 방법에 적용될 수 있습니다.
Alex B

IMHO Bash 기반 템플릿은 열광적입니다. 템플릿 작업을 이해하려면 bash 프로그래머 여야합니다! 그러나 의견을 주셔서 감사합니다!
mogsie

@AlexB :이 접근 방식 작은 따옴표 대체합니다. 왜냐하면 evaled echo / cat명령이 처리 할 때 문자열 구분 기호가 아니라 묶는 큰 따옴표로 묶은 문자열 안에있는 문자입니다 . 시도하십시오 eval "echo \"'\$HOME'\"".
mklement0

21

mogsie와 같은 bash 솔루션이 있지만 herestring 대신 heredoc을 사용하면 큰 따옴표를 피할 수 있습니다.

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

4
이 솔루션은 템플릿에서 Bash 매개 변수 확장 을 지원합니다 . 내 즐겨 찾기는 필수 매개 변수 이며 선택 매개 변수 주위에${param:?} 텍스트가 중첩 되어 있습니다. 예 : ${DELAY:+<delay>$DELAY</delay>}DELAY가 정의되어 있지 않으면 아무 것도 확장하지 않고 DELAY = 17이면 <delay> 17 </ delay>입니다.
Eric Bolinger

2
오! EOF 구분 기호는 PID와 같은 동적 문자열을 사용할 수 있습니다 _EOF_$$.
에릭 Bolinger

1
@ mklement0 줄 바꿈 후행 해결 방법은 빈 변수와 같은 확장을 사용 $trailing_newline하거나 $NL55 개 줄 바꿈으로 확장하는 것을 사용하는 것입니다.
xebeche

@xebeche : 예, 당신이 바로 그 끝에서 제안 배치 template.txt 뒤에 줄 바꿈을 유지하기 위해 작동합니다.
mklement0

1
훌륭한 해결책이지만 명령 대체는 입력 파일에서 후행 줄 바꿈을 제거하지만 일반적으로 문제가되지는 않습니다. 또 다른 가장자리 경우 :의 사용으로 인해 자체 줄에 포함되어 eval있으면 here-doc을 조기에 종료하여 명령을 중단합니다. (@xebeche에게 모자 팁). template.txtEOF
mklement0

18

2017 년 1 월 6 일 편집

구성 파일에 큰 따옴표를 유지해야했기 때문에 sed help로 큰 따옴표를 이스케이프 처리하면 다음과 같습니다.

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

Grégory Pakosz에 대한 모든 크레딧 .


입력에서 큰 따옴표를 제거하고 입력 파일에 여러 개의 후행 줄 바꿈이있는 경우 단일 따옴표로 바꿉니다.
mklement0

2
작동하려면 백 슬래시가 2 개 더 적게 필요했습니다. eval "echo \"$(sed 's/\"/\\"/g' $1)\""
David Bau

불행히도,이 방법은 PHP 파일을 템플릿으로 만들 수 없습니다 (포함되어 있음 $variables).
IStranger

10

envsubst로 바퀴를 재발 명하는 대신 거의 모든 시나리오에서 사용할 수 있습니다 (예 : 도커 컨테이너의 환경 변수에서 구성 파일 작성).

Mac에서 homebrew 가 있는지 확인 하고 gettext에서 연결하십시오.

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실제로 작동합니다.
Alex

다른 사람이 envsubstMacOS에서 작동하지 않으면 homebrew를 사용하여 설치해야합니다 brew install gettext.
Matt

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} 해당 환경 값 (또는 정의되지 않은 경우 빈 문자열)으로 확장됩니다.

백 슬래시를 올바르게 이스케이프하고 백 슬래시 이스케이프 된 $를 수락하여 대체를 금지합니다 (envsubst와 달리이 작업을 수행하지 않음) )를.

따라서 환경이 다음과 같은 경우

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 호출로이를 수행 할 수 있습니다.sed -e 's/VARONE/NEWVALA/g' -e 's/VARTWO/NEWVALB/g' -e 's/VARTHR/NEWVALC/g' < $TEMPLATE > $OUTPUT
Brandon Bloom

8

Jinja2 템플릿 을 사용하려면 이 프로젝트를 참조하십시오 : j2cli .

다음을 지원합니다.

  • JSON, INI, YAML 파일 및 입력 스트림의 템플릿
  • 환경 변수에서 템플릿

5

순수한 bash를 사용하여 ZyX에서 답을 얻었지만 새로운 스타일의 정규 표현식 일치 및 간접 매개 변수 대체를 사용하면 다음과 같습니다.

#!/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을 사용 하는 것이 옵션이고 모든 변수 와 달리 환경 변수만을 기반으로 확장하는 내용이라면 Stuart P. Bentley의 강력한 답변을 고려하십시오 . .

이 답변의 목표는 제공하는 bash는 전용 솔루션 의 사용에도 불구하고 - 그렇게 eval해야 - 사용하는 것이 안전합니다 .

목표는 다음과 같습니다

  • 가변 참조 ${name}$name가변 참조의 확장을 지원합니다 .
  • 다른 모든 확장을 방지하십시오.
    • 명령 대체 ( $(...)및 레거시 구문 `...`)
    • 산술 대체 ( $((...))및 레거시 구문 $[...]).
  • 접두사 \( \${name}) 로 변수 확장을 선택적으로 억제 할 수 있습니다.
  • 특수 문자를 유지하십시오. 입력에서 현저 "하고 \인스턴스.
  • 인수 또는 stdin을 통한 입력을 허용하십시오.

기능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
  • 성능상의 이유로이 함수는 stdin 입력 을 한 번 에 메모리로 읽지 만 함수를 한 줄씩 접근하는 것은 쉽습니다.
  • 또한 내장 명령이나 산술 대체가 포함되어 있지 않은 경우 와 같은 비 기본 변수 확장 도 지원합니다.${HOME:0:10}${HOME:0:$(echo 10)}
    • 이러한 내장 대체는 실제로 기능을 중단합니다 (모두 $(`인스턴스가 맹목적으로 이스케이프 되기 때문에 ).
    • 마찬가지로 ${HOME(닫기 누락 }) BREAK 함수 와 같은 잘못된 변수 참조
  • bash가 큰 따옴표로 묶은 문자열을 처리하기 때문에 백 슬래시는 다음과 같이 처리됩니다.
    • \$name 확장을 방지합니다.
    • \뒤에 하나도 없는$ 는 그대로 유지됩니다.
    • 인접한 여러 \ 인스턴스 를 나타내 려면 두 배로 늘려야합니다 . 예 :
      • \\-> \-그대로\
      • \\\\ -> \\
    • 내부 목적으로 사용되는 다음을 포함 할 수 없습니다 입력 (거의 사용하지 않음) 문자 : 0x1, 0x2, 0x3.
  • bash가 새로운 확장 구문을 도입해야한다면이 함수가 그러한 확장을 막지 못할 수도 있다는 가설이 있습니다 eval. 사용하지 않는 솔루션에 대해서는 아래를 참조하십시오 .

확장 지원 하는 보다 제한적인 솔루션을${name} 찾고있는 경우 ( 예 : 필수 중괄호 사용) 무시$name 참조 이 답변 을 참조하십시오 .


여기는 허용 된 답변 의 bash 전용 eval무료 솔루션 향상된 버전은 .

개선 사항은 다음과 같습니다.

  • ${name}및 모두 확장 지원$name가변 참조 가변 참조의 .
  • 을지 지하다 \확장해서는 안되는 -escaping 변수 참조를 .
  • 달리 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

또 다른 순수한 bash 솔루션이 있습니다.

  • heredoc을 사용하고 있으므로 다음을 수행하십시오.
    • 추가로 필요한 구문으로 인해 복잡성이 증가하지 않습니다.
    • bash 코드를 포함 할 수있는 템플릿
      • 또한 물건을 제대로 들여 쓸 수 있습니다. 아래를 참조하십시오.
  • eval을 사용하지 않으므로 다음을 수행하십시오.
    • 뒤에 빈 줄을 렌더링하는 데 아무런 문제가 없습니다.
    • 템플릿에서 따옴표에 문제가 없습니다.

$ cat code

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

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

cat_template template | LISTING="$LISTING" bash

$ cat template (뒤에 줄 바꿈과 큰 따옴표 포함)

<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

또 다른 해결책은 다음과 같습니다. 템플릿 파일의 모든 변수와 내용으로 bash 스크립트를 생성하십시오. 해당 스크립트는 다음과 같습니다.

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

EOF                

이 스크립트를 bash에 공급하면 원하는 결과를 얻을 수 있습니다.

the number is 1
the word is dog

해당 스크립트를 생성하고 해당 스크립트를 bash에 공급하는 방법은 다음과 같습니다.

(
    # Variables
    echo word=dog
    echo i=1

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

토론

  • 괄호는 하위 쉘을 열고 그 목적은 생성 된 모든 출력을 그룹화하는 것입니다.
  • 하위 셸 내에서 모든 변수 선언을 생성합니다
  • 또한 하위 쉘에서 catHEREDOC을 사용하여 명령을 생성합니다.
  • 마지막으로 하위 쉘 출력을 bash에 공급하고 원하는 출력을 생성합니다.
  • 이 출력을 파일로 경로 재 지정하려면 마지막 행을 다음으로 바꾸십시오.

    ) | bash > output.txt

3

이 페이지는 awk답변을 설명합니다

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

이렇게하면 모든 따옴표가 그대로 유지됩니다. 큰!
Pepster 2016 년

3

shtpl을 위한 완벽한 케이스 . (내 프로젝트이므로 널리 사용되지 않고 문서화가 부족합니다. 그러나 여기에 어쨌든 제공하는 솔루션이 있습니다. 테스트하고 싶을 수도 있습니다.)

그냥 실행하십시오 :

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

결과는 다음과 같습니다

the number is 1
the word is dog

즐기세요


1
쓰레기라면 어쨌든 하향 조정됩니다. 그리고 난 괜찮습니다. 그러나 좋아, 요점은 명확하게 보이지 않으며 실제로 내 프로젝트라는 점입니다. 앞으로 더 잘 보이게하려고합니다. 귀하의 의견과 시간에 감사드립니다.
zstegi

나는 어제 유스 케이스를 검색했는데 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"
}

이것은 원하는대로 조정 가능하고 프로덕션 환경에서 사용되는 순수한 bash 기능이며 입력시 깨지지 않아야합니다. 고장난 경우 알려주세요.



0

공백을 유지하는 bash 함수는 다음과 같습니다.

# 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

이 페이지 에서 plockc의 답변 을 추적하기 위해 여기 에 bashism 을 피하려는 사람들에게 적합한 대시 버전이 있습니다.

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


-1

이 훌륭한 질문에 저의 겸손한 기여.

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"

이것은 기본적으로 서브 쉘을 사용하여 evar을 대체하여 eval을 사용하지 않고 작은 따옴표와 큰 따옴표를 이스케이프 처리한다는 점을 제외하고 작동합니다. var 표현식을 공백없이 한 줄로 연결하여 혼동하지 말고 sh템플릿을에 전달 하여 var 대체를 처리 echo할 수 sh있도록합니다. 줄 바꿈, 주석 등을 유지 \${like_this}하며 var를 해석하지 않으려면 탈출 할 수 있습니다 . ${missing_var}빈 값으로 대체됩니다.

여기에있는 다른 많은 대답은 매우 훌륭하지만 매우 간단한 것을 원했으며 현재 가지고있는 템플릿 케이스에 대해 가능한 모든 경우를 처리 할 필요는 없습니다.

즐겨!


심각한 인용 버그가 있지만 수정하기 쉬워야합니다 ( 쉘 변수를 인용 부호로 묶는 시기 참조 ). 그러나 나는 여기에 더 근본적인 문제가 있다고 본다. 복잡성 중 일부는 부서지기 쉽고 완전히 불필요합니다.
tripleee
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.