Substituir variáveis de ambiente em um arquivo com seus valores reais?

25

Existe uma maneira fácil de substituir / avaliar variáveis de ambiente em um arquivo? Tipo, digamos que eu tenha um arquivo config.xml que contém:

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

... etc. Desejo substituir $INSTANCE_ID no arquivo pelo valor da variável de ambiente INSTANCE_ID , $SERVICE_NAME com o valor da SERVICE_NAME env var. Eu não saberei a priori que vars de ambiente são necessários (ou melhor, eu não quero ter que atualizar o script se alguém adicionar uma nova variável de ambiente ao arquivo de configuração). Obrigado!

    
por Robert Fraser 09.07.2016 / 16:08

4 respostas

48

Você pode usar envsubst (parte de gnu gettext ) :

envsubst < infile

irá substituir as variáveis de ambiente no seu arquivo pelo seu valor correspondente. Os nomes das variáveis devem consistir apenas em caracteres ASCII alfanuméricos ou sublinhados, não começar com um dígito e ser não-vazio; caso contrário, tal referência variável é ignorada.

Para substituir apenas algumas variáveis de ambiente, veja esta pergunta.

    
por 09.07.2016 / 16:38
9

Isso não é muito legal, mas funciona

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

Se estivesse em um script de shell, seria parecido com:

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

Editar, segunda proposta:

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

Editar, não estritamente relacionado à pergunta, mas no caso de variáveis lidas do arquivo:

(. .env && eval "echo \"$(cat config.xml)\"")
    
por 09.07.2016 / 16:25
7

Se você tiver Perl (mas não gettext e envsubst ), poderá fazer a substituição simples com um script curto:

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

Assumi que os nomes das variáveis terão apenas letras maiúsculas e sublinhados, mas o primeiro padrão deve ser fácil de alterar conforme necessário. $ENV{...} referencia o ambiente que Perl vê.

Se você quiser oferecer suporte à sintaxe ${...} ou lançar um erro em variáveis não definidas, precisará de mais algum trabalho. Um equivalente próximo de gettext ' envsubst seria:

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

Embora eu sinta que alimentar variáveis como essa através do ambiente de processo parece um pouco duvidoso em geral: você não pode usar variáveis arbitrárias nos arquivos (já que elas podem ter significados especiais), e alguns dos valores poderiam ter menos dados semi-sensíveis neles.

    
por 09.07.2016 / 16:24
0

Posso sugerir meu próprio roteiro para isso?

link

#!/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
    
por 02.04.2017 / 08:55