IT

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

lottoking 2020. 7. 21. 07:40
반응형

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


내 웹 서버의 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/"

많은 다른 것을 피할 필요가 있기 때문에 많은 변수가 있기 때문에 선이 길어질 것입니다.

우아하고 안전한 다른 솔루션을 생각할 수 있습니까?


사용할 수 있습니다 :

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
줄 = "$ (고양이; 에코 -na)"
end_offset = $ {# line}
[[ "$ {line : 0 : $ end_offset}"= ~ (. *) (\ $ \ {([a-zA-Z _] [a-zA-Z_0-9] *) \}) (. * )]]; 하다
    PRE = "$ {BASH_REMATCH [1]}"
    POST = "$ {BASH_REMATCH [4]} $ {line : $ end_offset : $ {# line}}"
    VARNAME = "$ {BASH_REMATCH [3]}"
    평가 'VARVAL = "$'$ VARNAME '"'
    줄 = "$ PRE $ VARVAL $ POST"
    end_offset = $ {# PRE}
끝난
echo -n "$ {line : 0 : -1}"

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

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

시험 envsubst

FOO=foo
BAR=bar
export FOO BAR

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

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

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

나는 평가가 정말 잘 작동합니다. 줄 바꿈, 공백 및 모든 종류의 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의 솔루션을 추가했습니다!


mogsie와 같은 bash 솔루션이 사용하면 여기에 큰 따옴표를 피할 수 있습니다.

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

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에 대한 모든 .


허용되는 답변의 길지만 강력한 버전 :

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

나는이 방법을 사용했을 것입니다. 아마 덜 덜이지만 읽기 / 유지 관리하기 어렵습니다.

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

Jinja2 템플릿 을 사용하는 경우 j2cli 프로젝트를 참조하십시오 .

다음을 지원합니다.

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

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

순수한 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

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

또 다른 순수한 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>

또 다른 해결책은 다음과 가변합니다. 템플릿 파일의 모든 변수와 내용으로 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
    

이 페이지는 awk를 사용한 답변을 설명합니다.

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

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

다음을 실행하십시오.

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

결과는 다음과 같습니다.

the number is 1
the word is dog

즐기세요.


# 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 함수로, 프로덕션에 사용되며 입력을 중단해서는 안됩니다. 깨지면 알려주세요.


bashible 을 사용할 수도 있습니다 (위 / 아래에 설명 된 평가 접근 방식을 내부적으로 사용함).

여러 부분에서 HTML을 생성하는 방법에 대한 예가 있습니다.

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


다음은 공백을 유지하는 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}"
}

다음 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 및 밑줄 문자 만 지원합니다 (첫 번째 위치의 숫자 제외).

여기에서 간단한 변수 대체 파이썬 스크립트를보십시오 : https://github.com/jeckep/vsubst

사용이 매우 간단합니다.

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

참고 URL : https://stackoverflow.com/questions/2914220/bash-templating-how-to-build-configuration-files-from-templates-with-bash

반응형