Как заменить заполнители ${} в текстовом файле?
-
03-07-2019 - |
Вопрос
Я хочу передать вывод файла «шаблона» в MySQL, файл имеет такие переменные, как ${dbName}
вкрапления.Какая утилита командной строки позволяет заменить эти экземпляры и выгрузить вывод в стандартный вывод?
Решение
Sed !
Данный template.txt:
The number is ${i} The word is ${word}
мы просто должны сказать:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
Спасибо Джонатану Леффлеру за подсказку о передаче нескольких аргументов -e
в один и тот же вызов sed
.
Другие советы
Update
Вот решение от yottatsa по аналогичному вопросу, который выполняет замену только таких переменных, как $ VAR или $ {VAR } и краткий однострочный
i=32 word=foo envsubst < template.txt
Конечно, если i и word в вашей среде, то это просто
envsubst < template.txt
На моем Mac похоже, что он был установлен как часть gettext и из MacGPG2
Старый ответ
Ниже приведено усовершенствование решения из mogsie по аналогичному вопросу. Мое решение не требует от вас повторения дважды цитаты, Mogsie's, но его один вкладыш!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
Сила этих двух решений заключается в том, что вы можете получить только несколько типов расширений оболочки, которые обычно не выполняются $ ((...)), `...` и $ (...), хотя обратная косая черта здесь является escape-символом, но вам не нужно беспокоиться о том, что при синтаксическом анализе есть ошибка, и она отлично справляется с несколькими строками.
Используйте / 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
Я снова думал об этом, учитывая недавний интерес, и думаю, что инструмент, о котором я изначально думал, был m4
, макропроцессор для автоинструментов. Таким образом, вместо переменной, которую я первоначально указал, вы должны использовать:
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
шаблон.txt
Variable 1 value: ${var1}
Variable 2 value: ${var2}
данные.sh
#!/usr/bin/env bash
declare var1="value 1"
declare var2="value 2"
парсер.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 шаблон.txt анализируемый_файл.txt
анализируемый_файл.txt
Variable 1 value: value 1
Variable 2 value: value 2
вот мое решение с Perl, основанное на предыдущем ответе, заменяет переменные среды:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
Вот надежная функция Bash это - несмотря на использование eval
- должно быть безопасно в использовании.
Все ${varName}
ссылки на переменные во входном тексте расширяются на основе переменных вызывающей оболочки.
Ничего больше расширен:ни одна переменная не ссылается на имена, нет заключенный в {...}
(такой как $varName
), ни замены команд ($(...)
и устаревший синтаксис `...`
), ни арифметические замены ($((...))
и устаревший синтаксис $[...]
).
Чтобы лечить $
как буквальное, \
-убежать от него;например.:\${HOME}
Обратите внимание, что ввод принимается только через стандартный ввод.
Пример:
$ 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
во входных данных присутствуют управляющие символы, поскольку эти символы.используются внутри — поскольку функция обрабатывает текст, это должно быть безопасным предположением.
Если вы открыты для использования 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/\$\{ Если вы открыты для использования Perl , это мое предложение. Хотя, возможно, есть несколько sed и / или AWK эксперты, которые, вероятно, знают, как это сделать гораздо проще. Если у вас есть более сложное сопоставление с заменой dbName для замен, вы можете довольно легко расширить это, но вы можете с таким же успехом поместить его в стандартный сценарий Perl.
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
Короткий Perl-скрипт для выполнения чего-то немного более сложного (обработка нескольких ключей):
replace-script < yourfile | mysql
Если вы назвали вышеуказанный скрипт как replace-script, его можно будет использовать следующим образом:
<*>\}/$replace{ Если вы открыты для использования Perl , это мое предложение. Хотя, возможно, есть несколько sed и / или AWK эксперты, которые, вероятно, знают, как это сделать гораздо проще. Если у вас есть более сложное сопоставление с заменой dbName для замен, вы можете довольно легко расширить это, но вы можете с таким же успехом поместить его в стандартный сценарий Perl.
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
Короткий Perl-скрипт для выполнения чего-то немного более сложного (обработка нескольких ключей):
<*>
Если вы назвали вышеуказанный скрипт как replace-script, его можно будет использовать следующим образом:
<*>}/g for keys %replace;
print $buf;
Если вы назвали вышеуказанный скрипт как replace-script, его можно будет использовать следующим образом:
<*> Создать 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
файл.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.
скрипт.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
Я бы предложил использовать что-то вроде Sigil : https://github.com/gliderlabs/sigil
Он скомпилирован в один двоичный файл, поэтому его очень легко установить в системах.
Затем вы можете сделать простой однострочный текст, как показано ниже:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
Это намного безопаснее, чем eval
, и проще, чем использование регулярных выражений или sed
Я нашел эту ветку, удивляясь тому же. Это вдохновило меня на это (осторожно с галочкой)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
Здесь много вариантов, но я решил бросить свой в кучу. Это основано на Perl, только целевые переменные вида $ {...}, принимает файл для обработки в качестве аргумента и выводит преобразованный файл на стандартный вывод:
use Env;
Env::import();
while(<>) { Здесь много вариантов, но я решил бросить свой в кучу. Это основано на Perl, только целевые переменные вида $ {...}, принимает файл для обработки в качестве аргумента и выводит преобразованный файл на стандартный вывод:
<*>
Конечно, я на самом деле не Perl человек, поэтому легко может быть фатальный недостаток (работает для меня, хотя).
=~ s/(\${\w+})/$1/eeg; $text .= Здесь много вариантов, но я решил бросить свой в кучу. Это основано на Perl, только целевые переменные вида $ {...}, принимает файл для обработки в качестве аргумента и выводит преобразованный файл на стандартный вывод:
<*>
Конечно, я на самом деле не Perl человек, поэтому легко может быть фатальный недостаток (работает для меня, хотя).
; }
print "$text";
Конечно, я на самом деле не Perl человек, поэтому легко может быть фатальный недостаток (работает для меня, хотя).
Это можно сделать в самом bash, если у вас есть контроль над форматом файла конфигурации. Вам просто нужно получить исходный текст (". & Quot;) файла конфигурации, а не его оболочку. Это гарантирует, что переменные создаются в контексте текущей оболочки (и продолжают существовать), а не подоболочки (где переменная исчезает при выходе из подоболочки).
$ cat config.data
export parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA
export parm_user=pax
export parm_pwd=never_you_mind
$ cat go.bash
. config.data
echo "JDBC string is " $parm_jdbc
echo "Username is " $parm_user
echo "Password is " $parm_pwd
$ bash go.bash
JDBC string is jdbc:db2://box7.co.uk:5000/INSTA
Username is pax
Password is never_you_mind
Если ваш конфигурационный файл не может быть сценарием оболочки, вы можете просто «скомпилировать» его перед выполнением (сборка зависит от вашего формата ввода).
$ cat config.data
parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA # JDBC URL
parm_user=pax # user name
parm_pwd=never_you_mind # password
$ cat go.bash
cat config.data
| sed 's/#.*$//'
| sed 's/[ \t]*$//'
| sed 's/^[ \t]*//'
| grep -v '^
В вашем конкретном случае вы можете использовать что-то вроде:
$ cat config.data
export p_p1=val1
export p_p2=val2
$ cat go.bash
. ./config.data
echo "select * from dbtable where p1 = '$p_p1' and p2 like '$p_p2%' order by p1"
$ bash go.bash
select * from dbtable where p1 = 'val1' and p2 like 'val2%' order by p1
Затем перенаправьте вывод go.bash в MySQL и вуаля, надеюсь, вы не уничтожите свою базу данных: -).
| sed 's/^/export '
>config.data-compiled
. config.data-compiled
echo "JDBC string is " $parm_jdbc
echo "Username is " $parm_user
echo "Password is " $parm_pwd
$ bash go.bash
JDBC string is jdbc:db2://box7.co.uk:5000/INSTA
Username is pax
Password is never_you_mind
В вашем конкретном случае вы можете использовать что-то вроде:
<*>Затем перенаправьте вывод go.bash в MySQL и вуаля, надеюсь, вы не уничтожите свою базу данных: -).
Вот способ заставить оболочку выполнить замену за вас, как если бы содержимое файла было введено в двойных кавычках.
На примере файла template.txt с содержимым:
The number is ${i}
The word is ${word}
Следующая строка заставит оболочку интерполировать содержимое файла template.txt и записать результат в стандартный вывод.
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
Объяснение:
i
иword
передаются как переменные среды, ограниченные выполнениемsh
.sh
выполняет содержимое переданной строки.- Строки, написанные рядом друг с другом, становятся одной строкой, эта строка:
- '
echo "
' + "$(cat template.txt)
" + '"
'
- '
- Поскольку замена происходит между
"
, "$(cat template.txt)
" становится результатомcat template.txt
. - Таким образом, команда, выполняемая
sh -c
становится:echo "The number is ${i}\nThe word is ${word}"
,- где
i
иword
являются указанными переменными среды.