Как заменить $ {} заполнителей в текстовом файле? - PullRequest
125 голосов
/ 06 января 2009

Я хочу направить вывод файла шаблона в MySQL, в котором переменные типа ${dbName} разбросаны. Что такое утилита командной строки для замены этих экземпляров и вывода вывода на стандартный вывод?

Ответы [ 15 ]

154 голосов
/ 06 января 2009

Сед !

Данный шаблон.txt:

The number is ${i}
The word is ${word}

мы просто должны сказать:

sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt

Спасибо Джонатану Леффлеру за подсказку о передаче нескольких -e аргументов в один и тот же sed вызов.

134 голосов
/ 10 июня 2013

Обновление

Вот решение из yottatsa по аналогичному вопросу, которое выполняет замену только таких переменных, как $ VAR или $ {VAR}, и является кратким однострочным

i=32 word=foo envsubst < template.txt

Конечно, если i и word находятся в вашем окружении, то это просто

envsubst < template.txt

На моем Mac похоже, что он был установлен как часть gettext и MacGPG2

Старый ответ

Вот улучшение по сравнению с mogsie по аналогичному вопросу. Мое решение не требует от вас эскалирования двойных кавычек, это делает mogsie, но у него одна строка!

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

Сила этих двух решений заключается в том, что вы можете получить только несколько типов расширений оболочки, которые обычно не происходят $ ((...)), `...` и $ (...), хотя обратная косая черта здесь является экранирующим символом, но вам не нужно беспокоиться о том, что при синтаксическом анализе есть ошибка, и он отлично справляется с несколькими строками.

42 голосов
/ 06 января 2009

Используйте /bin/sh. Создайте небольшой скрипт оболочки, который устанавливает переменные, а затем проанализируйте шаблон с помощью самой оболочки. Вот так (отредактируйте, чтобы правильно обрабатывать новые строки):

Файл 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"

Выход:

#sh script.sh
the number is 1
the word is dog
19 голосов
/ 19 мая 2011

Я снова думал об этом, учитывая недавний интерес, и думаю, что инструмент, о котором я изначально думал, был m4, макропроцессор для автоинструментов. Таким образом, вместо переменной, которую я первоначально указал, вы должны использовать:

$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
12 голосов
/ 21 августа 2009

template.txt

Variable 1 value: ${var1}
Variable 2 value: ${var2}

data.sh

#!/usr/bin/env bash
declare var1="value 1"
declare var2="value 2"

parser.sh

#!/usr/bin/env bash

# args
declare file_data=$1
declare file_input=$2
declare file_output=$3

source $file_data
eval "echo \"$(< $file_input)\"" > $file_output

. / Parser.sh data.sh template.txt parsed_file.txt

parsed_file.txt

Variable 1 value: value 1
Variable 2 value: value 2
9 голосов
/ 06 июня 2013

вот мое решение с Perl, основанное на предыдущем ответе, заменяет переменные окружения:

perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
8 голосов
/ 21 октября 2016

Вот надежная * Bash функция 1002 *, которую, несмотря на использование eval, следует использовать безопасно.

Все ${varName} ссылки на переменные во входном тексте расширяются на основе переменных вызывающей оболочки.

Ничего другого не расширен: ни ссылки на переменные, имена которых не заключены в {...} (например, $varName), ни замены команд ($(...) и устаревший синтаксис `...`), ни арифметические замены ($((...)) и устаревший синтаксис $[...]).

Чтобы трактовать $ как литерал, \ - уберечь его; e.g.:\${HOME}

Обратите внимание, что ввод принимается только через stdin .

Пример:

$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)

Исходный код функции:

expandVarsStrict(){
  local line lineEscaped
  while IFS= read -r line || [[ -n $line ]]; do  # the `||` clause ensures that the last line is read even if it doesn't end with \n
    # Escape ALL chars. that could trigger an expansion..
    IFS= read -r -d '' lineEscaped < <(printf %s "$line" | tr '`([$' '\1\2\3\4')
    # ... then selectively reenable ${ references
    lineEscaped=${lineEscaped//$'\4'{/\${}
    # Finally, escape embedded double quotes to preserve them.
    lineEscaped=${lineEscaped//\"/\\\"}
    eval "printf '%s\n' \"$lineEscaped\"" | tr '\1\2\3\4' '`([$'
  done
}

Функция предполагает, что на входе отсутствуют управляющие символы 0x1, 0x2, 0x3 и 0x4, поскольку эти символы. используются внутренне - поскольку функция обрабатывает text , это должно быть безопасным допущением.

6 голосов
/ 06 января 2009

Если вы открыты для использования Perl , это было бы моим предложением. Хотя, вероятно, есть эксперты sed и / или AWK , которые, вероятно, знают, как сделать это намного проще. Если у вас есть более сложное отображение с использованием не только dbName для ваших замен, вы можете довольно легко расширить это, но вы можете с таким же успехом поместить его в стандартный скрипт Perl.

perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql

Короткий Perl-скрипт для выполнения чего-то немного более сложного (обработка нескольких ключей):

#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;

Если вы назвали вышеупомянутый скрипт как replace-script, его можно было бы использовать следующим образом:

replace-script < yourfile | mysql
5 голосов
/ 22 февраля 2017

Создать rendertemplate.sh:

#!/usr/bin/env bash

eval "echo \"$(cat $1)\""

А template.tmpl:

Hello, ${WORLD}
Goodbye, ${CHEESE}

Визуализация шаблона:

$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl 
Hello, Foo
Goodbye, Bar
4 голосов
/ 03 октября 2011

file.tpl:

The following bash function should only replace ${var1} syntax and ignore 
other shell special chars such as `backticks` or $var2 or "double quotes". 
If I have missed anything - let me know.

script.sh:

template(){
    # usage: template file.tpl
    while read -r line ; do
            line=${line//\"/\\\"}
            line=${line//\`/\\\`}
            line=${line//\$/\\\$}
            line=${line//\\\${/\${}
            eval "echo \"$line\""; 
    done < ${1}
}

var1="*replaced*"
var2="*not replaced*"

template file.tpl > result.txt
...