Bir dosyadaki ortam değişkenlerini gerçek değerleri ile değiştir.


41

Bir dosyadaki ortam değişkenlerini değiştirmek / değerlendirmek için kolay bir yol var mı? Diyelim ki içinde bir dosya config.xmlvar:

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

...vb. $INSTANCE_IDDosyada, INSTANCE_IDortam değişkeninin $SERVICE_NAMEdeğeri ile, SERVICE_NAMEenv var değerinin yerine geçmek istiyorum . Hangi ortam değişkenine ihtiyaç duyulduğunu önceden bilmiyorum (daha doğrusu, biri yapılandırma dosyasına yeni bir ortam değişkeni eklerse, betiği güncellemek istemiyorum). Teşekkürler!


1
Ne zaman dosya ile bir şey yapacaksın (cat, echo, source,…) değişken değerine göre altyazı yapacak
Costas

Bu xml dosyasının içeriği size kalmış mı? Eğer öyleyse, parametreleştirilmiş xslt, değerleri enjekte etmenin başka bir yolunu sunar ve (envsubst ve ilkinin aksine) sonuç olarak iyi biçimlendirilmiş xml'yi garanti eder.
kojiro

Yanıtlar:


69

Kullanabilirsiniz envsubst(bir parçası gnu gettext):

envsubst < infile

dosyanızdaki ortam değişkenlerini karşılık gelen değerle değiştirir. Değişken isimleri, yalnızca bir rakam ile başlayıp boş olmayan, alfanümerik veya alt çizgi ASCII karakterlerinden oluşmalıdır; Aksi takdirde, böyle bir değişken referansı dikkate alınmaz.


Yalnızca belirli ortam değişkenlerini değiştirmek için bu soruya bakın.


1
... liman işçisi resmime varsayılan olarak yüklenmemiş olması dışında: '- (
Robert Fraser

4
Bu iyi. Docker görüntüleri hafif ve özel olarak üretilmiş olmalıdır. Tabii ki, her zaman envsubst da ekleyebilirsiniz.
kojiro

Ya da üzerine tam konteyner gidin ve envsubst'ı kendi başına bir kaba koyun. Atomic Host, CoreOS veya RancherOS gibi bir işletim sistemi kullanıyorsanız, bu yaygın bir kalıp ve yaşam biçimidir. Atomic, özellikle dosya sistemiyle ya da yüklü olanlarla kök bulaşmasına izin vermez.
Kuberchaun

1
"All" ortam değişkenlerinin yerini almayacağını, yalnızca adı ^[[:alpha:]_][[:alnum:]_]*$POSIX yerel ayarında eşleşenleri unutmayın.
Stéphane Chazelas

Çok özlü görünmekle birlikte, tüm ikame değerleri ile mutlaka doğru olmak zorunda değildir. XML özel karakterlerine saygı göstermiyor gibi görünüyor.
EFraim

16

Bu çok hoş değil ama işe yarıyor

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

Bir kabuk betiğinde olsaydı şöyle olurdu:

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

Düzenleyin, ikinci teklif:

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

Düzenli, kesinlikle soru ile ilgili değil, dosyadan okunan değişkenler durumunda:

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

Bununla ilgili sorun, eğer dosya bir satır içeriyorsa EOF, kalan satırların kabuk tarafından komut olarak çalıştırılmasıdır. Ayırıcıyı daha uzun veya daha karmaşık bir şeye değiştirebiliriz, ancak yine de teorik bir çarpışma olasılığı var. Ve birisi komutları yürütmek için kasıtlı olarak ayırıcıyla bir dosya hazırlayabilir.
ilkkachu

Tamam, şunu deneyin: eval "echo \" $ (cat config.xml) \ ""
hschou

3
"; ls ;"Dosyanın içine benzer bir şey koymayı deneyin ve bu evalkomutu tekrar yapın :) Bu, SQL enjeksiyon saldırılarıyla hemen hemen aynı sorun. Verileri kodla karıştırırken çok dikkatli olmalısınız (ve kabuk komutları budur), gerçekten , gerçekten kimsenin gününüzle uğraşmak için bir şey yapmaya çalışmadığından emin değilseniz.
ilkkachu

No. "; ls;" zarar vermez
hschou

3
@hschou Ben ilkkachu bence demek `"; ls ;"`- yorum biçimlendirme backticks yedik. Ama aslında bu shoule sadece `ls`burada olmak. Mesele şu ki, dosyanın içeriği rasgele kod çalıştırma gerektiriyor ve bu konuda yapabileceğiniz hiçbir şey yok.
Gilles 'SO- kötülük olmayı'

8

Perl'e sahipseniz (ancak gettext ve değil envsubst), basit bir değiştirme işlemini kısa bir komut dosyasıyla yapabilirsiniz:

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

Değişken isimlerinin sadece büyük harflere ve alt çizgilere sahip olacağını varsaydım, ancak ilk kalıbın gerektiği gibi değiştirilmesi kolay olmalı. $ENV{...}Perl'in gördüğü çevreye gönderme yapar.

${...}Sözdizimini desteklemek veya ayarlanmamış değişkenlere bir hata atmak istiyorsanız, biraz daha çalışmaya ihtiyacınız olacaktır. gettext'Nin yakın bir eşdeğeri envsubst:

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

Bu gibi değişkenlerin işlem ortamından beslenmesinin genel olarak biraz kabaca göründüğünü düşünmeme rağmen: dosyalarda rasgele değişkenler kullanamazsınız (çünkü özel anlamları olabilir) ve değerlerin bazıları en azından yarı yarıya olabilir. bunlarda hassas veriler.


Liman işçisi olması gerektiğinden Perl'i kullanmamayı tercih ederim, ancak bu en iyi çözüme benziyor.
Robert Fraser,

2
Ayrıca perl -pe 's{\$(\{)?(\w+)(?(1)\})}{$ENV{$2} // $&}ge'sadece tanımlanmış değişkenleri değiştirmek için bakınız .
Stéphane Chazelas

1

Bunun için kendi senaryomu önerebilir miyim?

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 cevabına benzer şekilde, ortam değişkeninin değişimi PHP CLI'ye devredilebilir. PHP'ye bağımlılık, kullanılan teknoloji yığına bağlı olarak kabul edilebilir veya olmayabilir.

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

Daha ileri gidebilir ve yeniden kullanılabilir bir komut dosyasına koyabilirsiniz, örneğin 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')
);

Kullanım olacaktır:

envsubst < input.file > output.file
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.