Я хочу передавати висновок файлу "шаблон" у MySQL, при цьому файл має такі змінні, як ${dbName}
перемежовані. Яка утиліта командного рядка замінює ці екземпляри та скидає вихід на стандартний вихід?
Я хочу передавати висновок файлу "шаблон" у MySQL, при цьому файл має такі змінні, як ${dbName}
перемежовані. Яка утиліта командного рядка замінює ці екземпляри та скидає вихід на стандартний вихід?
Відповіді:
Сед !
Дано template.txt:
Кількість - $ {i} Слово - $ {word}
ми просто повинні сказати:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
Дякую Джонатану Леффлеру за пораду передавати кілька -e
аргументів на одне sed
виклик.
cat
. Все, що вам потрібно - це sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.text
.
sed
буде очікувати уникнутий текст, який є клопотом.
Ось рішення від йоттаца з подібного питання, яке замінює лише такі змінні, як $ VAR або $ {VAR}, і є коротким однолінійним
i=32 word=foo envsubst < template.txt
Звичайно, якщо я і слово є у вашому оточенні, то це просто
envsubst < template.txt
На моєму Mac це виглядає так, що він був встановлений як частина gettext та з MacGPG2
Ось вдосконалення рішення Mogsie за аналогічним запитанням, моє рішення не вимагає від вас збільшувати подвійні цитати, як це робить mogsie, але його - це один лайнер!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
Сила цих двох рішень полягає в тому, що ви отримуєте лише кілька типів розширень оболонок, які не трапляються зазвичай $ ((...)), `...` і $ (...), хоча зворотний нахил - це тут втеча персонаж, але вам не доведеться турбуватися про те, що при розборі є помилка, і це робить кілька рядків відмінно.
envsubst
не спрацьовують, якщо ваші товариші не експортуються.
envsubst
, як випливає з назви, розпізнає лише змінні середовища , а не змінні оболонки . Варто також зазначити, що envsubst
це утиліта GNU , а тому не встановлена або доступна на всіх платформах.
Використовуйте /bin/sh
. Створіть невеликий сценарій оболонки, який встановлює змінні, а потім проаналізуйте шаблон, використовуючи саму оболонку. Так (редагуйте, щоб правильно обробляти нові рядки):
the number is ${i}
the word is ${word}
#!/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
bash
команда на вході буде виконана. Якщо шаблон такий: "слова є; rm -rf $ HOME", ви втратите файли.
read
команда, як написано, обрізає провідні та кінцеві пробіли з кожного рядка та "їсть" \
символи. (C) використовуйте це лише якщо ви повністю довіряти або керувати входом, тому що заміни команд ( `…`
або $(…)
), вбудовані у вхід, дозволяють виконувати довільні команди завдяки використанню eval
. Нарешті, є невеликий шанс echo
помилитися на початку рядка для одного з його параметрів командного рядка.
Я знову замислювався над цим, враховуючи недавній інтерес, і думаю, що інструментом, про який я спочатку думав, був m4
макропроцесор для автоінструментів. Тож замість змінної, яку я спочатку вказав, ви використовуєте:
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
envsubst
для цієї простої заміни / використання шаблонів змінну, як зазначено в інших відповідях. m4
це чудовий інструмент, але це повноцінний препроцесор з набагато більшою кількістю функцій і, таким чином, складність, яка може не знадобитися, якщо ви просто хочете замінити деякі змінні.
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
`…`
або $(…)
), вбудовані у вхід, дозволяють виконувати довільні команди завдяки використанню eval
та прямому виконанню коду оболонки через використання source
. Крім того, подвійні лапки у введенні спокійно відкидаються, і вони echo
можуть помилково почати рядок для одного з його параметрів командного рядка.
Ось надійна функція Bash, яка, незважаючи на використання, eval
повинна бути безпечною у використанні.
Всі ${varName}
посилання змінних у вхідному тексті розширюються на основі змінних оболонки виклику.
Ніщо інше не розширюється: ні змінні посилання, імена яких не укладені {...}
(наприклад $varName
), ні підстановки команд ( $(...)
і застарілий синтаксис `...`
), ні арифметичні підстановки ( $((...))
і застарілий синтаксис $[...]
).
Трактувати a $
як буквальне, \
-швидти його; наприклад:\${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
керуючі символи присутні на вході, тому що ці символи. використовуються внутрішньо - оскільки функція обробляє текст , це має бути безпечним припущенням.
eval
його досить безпечно у використанні.
"
належним чином!)
${FOO:-bar}
або виводити щось лише якщо воно встановлено - ${HOME+Home is ${HOME}}
. Я підозрюю, що з невеликим розширенням він також може повернути вихідні коди для відсутніх змінних, ${FOO?Foo is missing}
але наразі tldp.org/LDP/abs/html/parameter-substitution.html не має їх списку, якщо це допомагає
Створити 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
$(rm -rf ~)
, ви запускаєте його як код.
eval "echo \"$(cat $1)\""
Чудово працює!
ось моє рішення perl, засноване на колишній відповіді, замінює змінні середовища:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
Якщо ви відкриті для використання Perl , це було б моєю пропозицією. Хоча, мабуть, є деякі фахівці з сед та / або 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 < yourfile | 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
є вказані змінні середовища.'$(rm -rf ~)'$(rm -rf ~)
буквальні цитати у файлі шаблону будуть відповідати тим, які ви додали до його розширення.
'$(echo a)'$(echo a)
. Це виробляє 'a'a
. Головне, що відбувається, це те, що перше echo a
всередині '
оцінюється, що може бути не тим, чого ви очікуєте, оскільки воно є '
, але це така ж поведінка, як і включення '
в "
цитований рядок.
"
Справа в розширенні чого-небудь, котируемого рядка (включаючи $(...)
).
${varname}
не про інші розширення з підвищеною небезпекою.
echo "
, за яким слідує рядок з подвійним цитуванням з буквальними контекстами template.txt
, а потім інший буквальний рядок "
, об'єднаний в один аргумент, переданий до sh -c
. Ви маєте рацію, що це '
неможливо зіставити (оскільки його споживає зовнішня оболонка, а не передається внутрішній), але, "
безумовно, може, тому шаблон, що містить, Gotcha"; rm -rf ~; echo "
може бути виконаний.
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
\$(date)
while IFS= read -r line; do
як read
команду, інакше ви знімете провідні та кінцеві пробіли з кожного рядка введення. Крім того, echo
може помилитися початок рядка для одного з його параметрів командного рядка, тому краще використовувати printf '%s\n'
. Нарешті, безпечніше подвійне цитування ${1}
.
Я б запропонував використовувати щось на зразок Sigil : https://github.com/gliderlabs/sigil
Він зібраний в єдиний двійковий файл, тому його вкрай просто встановити в системах.
Тоді ви можете зробити простий однокласинник, як описано нижче:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
Це набагато безпечніше, ніж eval
і простіше, ніж використовувати регулярний вираз абоsed
cat
та використовувати <my-file.conf.template
замість цього, щоб ви дали sigil
справжню обробку файлів замість FIFO.
Я знайшов цю тему, цікавившись те саме. Це мене надихнуло на це (обережно із задніми руками)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
$(cat file)
це$(< file)
eval echo "\"$(cat FILE)\""
але це все ще може бути невдалим у тому, що подвійні лапки у введенні будуть відкинуті.
`…`
або $(…)
), вбудовані у вхід, дозволяють виконувати довільні команди за рахунок використання eval
.
Тут багато варіантів, але я подумав, що я кину міну на купу. Він заснований на perl, націлює лише змінні форми $ {...}, приймає файл для обробки як аргумент і виводить перетворений файл на stdout:
use Env;
Env::import();
while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }
print "$text";
Звичайно, я насправді не перламурова людина, тому легко може статися фатальний недолік (хоча для мене це працює).
Env::import();
рядок - імпорт має на увазі use
. Крім того, я пропоную не збирати спочатку весь вихід у пам'яті: просто використовуйте print;
замість $text .= $_;
всередині циклу та відкиньте команду пост-цикл print
.
Це можна зробити в самому файлі bash, якщо ви контролюєте формат файлу конфігурації. Вам просто потрібно створити (".") Файл конфігурації, а не підзавантажити його. Це гарантує, що змінні створюються в контексті поточної оболонки (і продовжують існувати), а не підпакеті (де змінна зникає при виході нижньої оболонки).
$ 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 '^$'
| 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
У вашому конкретному випадку ви можете використовувати щось на кшталт:
$ 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 і voila, сподіваємось, ви не знищите свою базу даних :-).
go.bash
), у вас неправильний кінець палички - вони не є частиною рішення, вони просто спосіб показати, що змінні є встановити правильно.
Ви хочете чогось більш надійного, ніж нинішні пропозиції, оскільки, хоча вони працюють для вашого (поки що) обмеженого випадку використання, їх не вистачить для складніших ситуацій.
Вам потрібен кращий рендер. Вам потрібен найкращий рендер. Вам потрібен The Renderest!
Дано template.txt:
Привіт, {{person}}!
Виконати:
$ person = Bob ./render template.txt
І ви побачите вихід
Привіт, Бобе!
Запишіть його у файл, перенаправивши stdout у файл:
$ person = Bob ./render template.txt> rended.txt
І якщо у вас трапляється сценарій із змінними $ {}, які ви не хочете інтерполювати, Renderest вас охоплює, не роблячи нічого іншого!
Продовжуйте діставати свою копію за адресою https://github.com/relaxdiego/renderest