파일의 환경 변수를 실제 값으로 바꾸시겠습니까?


41

파일에서 환경 변수를 대체 / 평가하는 쉬운 방법이 있습니까? 다음과 같은 파일 config.xml이 있다고 가정 해 보겠습니다 .

<property>
    <name>instanceId</name>
    <value>$INSTANCE_ID</value>
</property>
<property>
    <name>rootPath</name>
    <value>/services/$SERVICE_NAME</value>
</property>

...기타. $INSTANCE_ID파일에서 INSTANCE_ID환경 변수 $SERVICE_NAME값과 SERVICE_NAMEenv var 값 으로 바꾸고 싶습니다 . 어떤 환경 변수가 필요한지 미리 알지 못합니다 (또는 누군가가 환경 파일에 새 환경 변수를 추가하는 경우 스크립트를 업데이트하고 싶지는 않습니다). 감사!


1
언제 파일 (cat, echo, source 등)로 무언가를 할 것인가? 변수는 그 값으로 대체 될 것이다
Costas

이 xml 파일의 내용이 귀하에게 있습니까? 그렇다면 매개 변수가있는 xslt는 값을 주입하는 또 다른 방법을 제공하며 (envsubst 및 ilk와 달리) 결과적으로 잘 구성된 xml을 보장합니다.
kojiro

답변:


69

당신은 envsubst(의 일부 gnu gettext)를 사용할 수 있습니다 :

envsubst < infile

파일의 환경 변수를 해당 값으로 바꿉니다. 변수 이름은 영숫자 또는 밑줄 ASCII 문자로만 구성되어야하며 숫자로 시작하지 않아야하며 비어 있지 않아야합니다. 그렇지 않으면 이러한 변수 참조는 무시됩니다.


특정 환경 변수 만 바꾸려면 이 질문을 참조하십시오.


1
... 내 Docker 이미지에 기본적으로 설치되지 않은 것을 제외하고 : '-(
Robert Fraser

4
잘 됐네요 Docker 이미지는 가볍고 맞춤형이어야합니다. 물론, 항상 envsubst를 추가 할 수 있습니다.
kojiro

또는 컨테이너를 가득 채우고 컨테이너 자체에 envsubst를 넣으십시오. Atomic Host, CoreOS 또는 RancherOS와 같은 OS를 사용하는 경우 일반적인 패턴과 생활 방식입니다. 원자는 특히 파일 시스템이나 컨테이너를 사용해야하는 것으로 루트 엉망이되지 않습니다.
Kuberchaun

1
^[[:alpha:]_][[:alnum:]_]*$POSIX 로케일에서 이름이 일치하는 변수 만 "모든"환경 변수를 대체하지 않습니다 .
Stéphane Chazelas

간결한 것으로 보이지만 모든 대체 값으로 반드시 정확하지는 않습니다. XML 특수 문자를 존중하지 않는 것 같습니다.
EFraim

16

이것은 좋지는 않지만 작동합니다.

( echo "cat <<EOF" ; cat config.xml ; echo EOF ) | sh

쉘 스크립트에 있다면 다음과 같습니다.

#! /bin/sh
cat <<EOF
<property>
    <name>instanceId</name>
    <value>$INSTANCE_ID</value>
</property>
EOF

두 번째 제안 수정 :

eval "echo \"$(cat config.xml)\""

질문과 관련이 없지만 파일에서 변수를 읽는 경우 편집하십시오.

(. .env && eval "echo \"$(cat config.xml)\"")

이 문제는 파일에가있는 줄이 포함 된 EOF경우 나머지 줄은 셸에서 명령으로 실행된다는 것입니다. 구분자를 더 길거나 더 복잡한 것으로 바꿀 수는 있지만 여전히 충돌 가능성이 있습니다. 그리고 누군가는 명령을 실행하기 위해 구분 기호로 파일을 의도적으로 만들 수 있습니다.
ilkkachu

좋아, 이것을 시도하십시오 : eval "echo \"$ (cat config.xml) \ ""
hschou

3
"; ls ;"파일 안에 무언가를 넣고 eval다시 시도 하십시오 :) 이것은 SQL 주입 공격과 거의 같은 문제입니다. 당신이 아니라면 당신은 코드와 데이터를 혼합 할 때 정말주의 (및 명령 쉘 그 무엇의이다)해야 정말 , 정말 있는지 아무도 당신의 일까지 혼란에 아무것도하려고하지 않습니다.
ilkkachu

아니요. "; ls;" 해를 끼치 지 않을 것입니다.
hschou

3
@hschou 나는 ilkkachu가 의미하는 것으로 생각한다 `"; ls ;"`– 주석 형식은 백틱을 먹었다. 그러나 실제로 그 shoule은 `ls`여기에 있습니다. 요점은 파일의 내용이 임의의 코드 실행으로 이어지고 당신이 할 수있는 일은 없다는 것입니다.
Gilles 'SO- 악한 중지'

8

Perl (gettext 및 envsubst제외)이있는 경우 짧은 스크립트로 간단한 교체를 수행 할 수 있습니다.

$ export INSTANCE_ID=foo; export SERVICE_NAME=bar;
$ perl -pe 's/\$([_A-Z]+)/$ENV{$1}/g'  < config.xml
<property>
    <name>instanceId</name>
    <value>foo</value>
</property>
<property>
    <name>rootPath</name>
    <value>/services/bar</value>
</property>

변수 이름에는 대문자와 밑줄 만 있다고 가정했지만 첫 번째 패턴은 필요에 따라 쉽게 변경할 수 있어야합니다. $ENV{...}Perl이 보는 환경을 참조하십시오.

${...}구문 을 지원 하거나 설정되지 않은 변수에 대한 오류를 발생 시키 려면 추가 작업이 필요합니다. 의 근접 동등한 gettexts '이 envsubst될 것이다 :

perl -pe 's/\$(\{)?([a-zA-Z_]\w*)(?(1)\})/$ENV{$2}/g'

프로세스 환경을 통해 변수를 제공하는 것이 일반적으로 다소 어려워 보인다고 생각하지만 파일에 임의의 변수를 사용할 수 없으며 (특별한 의미가있을 수 있기 때문에) 일부 값은 적어도 반값을 가질 수 있습니다 민감한 데이터.


도커 컨테이너이기 때문에 Perl을 사용하지는 않지만 최선의 솔루션처럼 보입니다.
Robert Fraser

2
perl -pe 's{\$(\{)?(\w+)(?(1)\})}{$ENV{$2} // $&}ge'정의 된 변수 만 대체하는 것도 참조하십시오 .
Stéphane Chazelas

1

이것에 대한 내 스크립트를 제안해도 될까요?

https://github.com/rydnr/set-square/blob/master/.templates/common-files/process-file.sh

#!/bin/bash /usr/local/bin/dry-wit
# Copyright 2016-today Automated Computing Machinery S.L.
# Distributed under the terms of the GNU General Public License v3

function usage() {
cat <<EOF
$SCRIPT_NAME -o|--output output input
$SCRIPT_NAME [-h|--help]
(c) 2016-today Automated Computing Machinery S.L.
    Distributed under the terms of the GNU General Public License v3

Processes a file, replacing any placeholders with the contents of the
environment variables, and stores the result in the specified output file.

Where:
    * input: the input file.
    * output: the output file.
Common flags:
    * -h | --help: Display this message.
    * -v: Increase the verbosity.
    * -vv: Increase the verbosity further.
    * -q | --quiet: Be silent.
EOF
}

# Requirements
function checkRequirements() {
  checkReq envsubst ENVSUBST_NOT_INSTALLED;
}

# Error messages
function defineErrors() {
  export INVALID_OPTION="Unrecognized option";
  export ENVSUBST_NOT_INSTALLED="envsubst is not installed";
  export NO_INPUT_FILE_SPECIFIED="The input file is mandatory";
  export NO_OUTPUT_FILE_SPECIFIED="The output file is mandatory";

  ERROR_MESSAGES=(\
    INVALID_OPTION \
    ENVSUBST_NOT_INSTALLED \
    NO_INPUT_FILE_SPECIFIED \
    NO_OUTPUT_FILE_SPECIFIED \
  );

  export ERROR_MESSAGES;
}

## Parses the input
## dry-wit hook
function parseInput() {

  local _flags=$(extractFlags $@);
  local _flagCount;
  local _currentCount;

  # Flags
  for _flag in ${_flags}; do
    _flagCount=$((_flagCount+1));
    case ${_flag} in
      -h | --help | -v | -vv | -q)
         shift;
         ;;
      -o | --output)
         shift;
         OUTPUT_FILE="${1}";
         shift;
         ;;
    esac
  done

  # Parameters
  if [[ -z ${INPUT_FILE} ]]; then
    INPUT_FILE="$1";
    shift;
  fi
}

## Checking input
## dry-wit hook
function checkInput() {

  local _flags=$(extractFlags $@);
  local _flagCount;
  local _currentCount;
  logDebug -n "Checking input";

  # Flags
  for _flag in ${_flags}; do
    _flagCount=$((_flagCount+1));
    case ${_flag} in
      -h | --help | -v | -vv | -q | --quiet)
         ;;
      -o | --output)
         ;;
      *) logDebugResult FAILURE "fail";
         exitWithErrorCode INVALID_OPTION ${_flag};
         ;;
    esac
  done

  if [[ -z ${INPUT_FILE} ]]; then
    logDebugResult FAILURE "fail";
    exitWithErrorCode NO_INPUT_FILE_SPECIFIED;
  fi

  if [[ -z ${OUTPUT_FILE} ]]; then
      logDebugResult FAILURE "fail";
      exitWithErrorCode NO_OUTPUT_FILE_SPECIFIED;
  fi
}

## Replaces any placeholders in given file.
## -> 1: The file to process.
## -> 2: The output file.
## <- 0 if the file is processed, 1 otherwise.
## <- RESULT: the path of the processed file.
function replace_placeholders() {
  local _file="${1}";
  local _output="${2}";
  local _rescode;
  local _env="$(IFS=" \t" env | awk -F'=' '{printf("%s=\"%s\" ", $1, $2);}')";
  local _envsubstDecl=$(echo -n "'"; IFS=" \t" env | cut -d'=' -f 1 | awk '{printf("${%s} ", $0);}'; echo -n "'";);

  echo "${_env} envsubst ${_envsubstDecl} < ${_file} > ${_output}" | sh;
  _rescode=$?;
  export RESULT="${_output}";
  return ${_rescode};
}

## Main logic
## dry-wit hook
function main() {
  replace_placeholders "${INPUT_FILE}" "${OUTPUT_FILE}";
}
# vim: syntax=sh ts=2 sw=2 sts=4 sr noet

0

Perl 답변과 마찬가지로 환경 변수 대체는 PHP CLI에 위임 될 수 있습니다. 사용중인 기술 스택에 따라 PHP에 대한 종속성이 허용되거나 허용되지 않을 수 있습니다.

php -r 'echo preg_replace_callback("/\\$([a-z0-9_]+)/i", function ($matches) { return getenv($matches[1]); }, fread(STDIN, 8192));' < input.file > output.file

다음과 같이 재사용 가능한 스크립트에 추가 할 수 있습니다 envsubst.

#!/usr/bin/env php
<?php

echo preg_replace_callback(
    '/\$(?<name>[a-z0-9_]+)/i',
    function ($matches) {
        return getenv($matches['name']);
    },
    file_get_contents('php://stdin')
);

사용법은 다음과 같습니다.

envsubst < input.file > output.file
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.