ファイル内の環境変数を実際の値に置き換えますか?


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
とき、あなたは(...、猫、エコー、ソース)ファイルで何かをする変数は、その値によってsubtituteう
コスタス

このxmlファイルの内容はあなた次第ですか?その場合、パラメータ化されたxsltは値を注入する別の方法を提供し、(envsubstやそのilkとは異なり)結果として整形式のxmlを保証します。
小次郎

回答:


69

envsubst(の一部gnu gettext)を使用できます:

envsubst < infile

ファイル内の環境変数を対応する値に置き換えます。変数名は、英数字またはアンダースコアASCII文字のみで構成する必要があり、数字で始まって空ではありません。それ以外の場合、そのような変数参照は無視されます。


特定の環境変数のみを置き換えるには、この質問を参照してください。


1
...それは私のドッカー画像にデフォルトでインストールされていないことを除いて: '-(
ロバート・フレイザー

4
それは良い。Dockerイメージは軽量で、テーラーメイドでなければなりません。もちろん、常にそれにenvsubstを追加することもできます。
小次郎

または、完全なコンテナに移動して、envsubstを単独でコンテナに入れます。Atomic Host、CoreOS、RancherOSなどのOSを使用する場合、これは一般的なパターンであり、生活様式です。アトミックは、ファイルシステムやインストールされているものをルート混乱させたり、コンテナを使用する必要はありません。
Kuberchaun

1
^[[:alpha:]_][[:alnum:]_]*$POSIXロケールで名前が一致する環境変数のみを「すべて」の環境変数に置き換えます。
ステファンシャゼラス

非常に簡潔に思えますが、必ずしもすべての置換値で正しいとは限りません。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

編集、2番目の提案:

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

質問に厳密に関連するものではなく、ファイルから読み取られる変数の場合、編集します。

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

これに伴う問題は、ファイルにの行が含まれている場合EOF、残りの行がシェルによってコマンドとして実行されることです。セパレータをもっと長く、またはより複雑なものに変更することもできますが、理論的には衝突する可能性があります。そして、誰かが意図的にコマンドを実行するために区切り記号付きのファイルを作成することができます。
-ilkkachu

OK、これを試してください:eval "echo \" $(cat config.xml)\ ""
hschou

3
"; ls ;"ファイルの中に何かを入れてもう一度evalコマンドを実行してみてください:)これは、SQLインジェクション攻撃とほぼ同じ問題です。あなたがない限り、あなたはコードとデータとを混合したときは本当に注意する必要があります(それは命令をしているシェルものだ)本当に本当に誰もあなたの日まで台無しに何かをしようとしていないことを確認。
-ilkkachu

いいえ。「; ls;」害はありません。
hschou

3
@hschou ilkkachuが意味したと思う`"; ls ;"`-コメントのフォーマットはバックティックを食べた。しかし、実際にはその肩が`ls`ここにあるだけです。ポイントは、ファイルのコンテンツが任意のコードの実行につながり、あなたがそれについてできることは何もないということです。
ジル「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が見る環境を参照します。

${...}構文をサポートしたり、未設定の変数でエラーをスローしたい場合は、さらに作業が必要になります。に相当するものは次gettextenvsubstとおりです。

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

プロセス環境を介してそのような変数を供給することは、一般的に少し曖昧に思えます:ファイルで任意の変数を使用することはできません(特別な意味を持つ可能性があるため)それらの機密データ。


Perlはdockerコンテナになるはずなので、むしろ使用しませんが、それが最適なソリューションのように見えます。
ロバートフレイザー

2
perl -pe 's{\$(\{)?(\w+)(?(1)\})}{$ENV{$2} // $&}ge'定義されている変数のみを置き換えることも参照してください。
ステファンシャゼラス

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.