Як замінити змінні оболонки у складні текстові файли


84

У мене є кілька текстових файлів, в які я ввів змінні оболонки (наприклад, $ VAR1 або $ VAR2).

Я хотів би взяти ці файли (по одному) і зберегти їх у нових файлах, де всі змінні були б замінені.

Для цього я використав такий сценарій оболонки (знайдений на StackOverflow):

while read line
do
    eval echo "$line" >> destination.txt
done < "source.txt"

Це дуже добре працює на дуже простих файлах.

Але для більш складних файлів команда "eval" робить занадто багато:

  • Рядки, що починаються з "#", пропускаються

  • Розбір файлів XML призводить до маси помилок

Чи є кращий спосіб це зробити? (у сценарії оболонки ... я знаю, що це легко зробити, наприклад, з Ant)

З повагою

Відповіді:


196

Дивлячись, виявляється, в моїй системі є envsubstкоманда, яка є частиною пакету gettext-base.

Отже, це полегшує:

envsubst < "source.txt" > "destination.txt"

Зверніть увагу , якщо ви хочете використовувати один і той же файл для обох, вам доведеться використовувати що - щось на зразок moreutil - х sponge, як запропонував Джонні Utahh: envsubst < "source.txt" | sponge "source.txt". (Оскільки перенаправлення оболонки спорожнить файл перед його читанням.)


4
Чудово! Але це працює лише для змінних середовища. Як я можу змусити це працювати зі змінними, які оголошені в моєму .sh-сценарії?
Бен

10
@Ben: використовуйте 'export' (перед викликом envsubst) для кожної змінної, яку ви хочете використовувати в envsubst
tlo

7
envsubstє частиною gettext GNU
Енді,

3
@user_mda, куди надходить вихід.
дероберт

4
Попередження: якщо source.txtмістять будь-які $символи за межами розширення змінної, envsubstвони також замінять їх, і немає можливості врятуватись від $. Загальне (потворне) обхідне рішення export DOLLAR="$".
Кос,

60

Посилаючись на відповідь 2, обговорюючи envsubst, ви запитали:

Як я можу змусити це працювати зі змінними, які оголошені в моєму .sh-сценарії?

Відповідь полягає в тому, що вам просто потрібно експортувати свої змінні перед викликом envsubst.

Ви також можете обмежити рядки змінних, які ви хочете замінити у введенні, використовуючи envsubst SHELL_FORMATаргумент (уникаючи ненавмисної заміни рядка у вході загальним значенням змінної оболонки - наприклад $HOME).

Наприклад:

export VAR1='somevalue' VAR2='someothervalue'
MYVARS='$VAR1:$VAR2'

envsubst "$MYVARS" <source.txt >destination.txt

Замінить усі екземпляри $VAR1та $VAR2(і лише VAR1та VAR2) в source.txtна 'somevalue'та 'someothervalue'відповідно.


14
'MYVARS
Поодинокі

Зверніть увагу, що export, envsubstабо будь-яка команда з чергуванням може не вдатися, коли текст для заміни великий. Довідково.
єпископ

@ram, оскільки відсутність SHELL_FORMATаргументу (тобто "$MYVARS") спричиняє заміну всіх експортованих змінних. Якщо це те, що вам потрібно, то не хвилюйтеся.
Тьяго Фігейру

12

Я знаю, що ця тема стара, але у мене є більш просте робоче рішення без експорту змінних. Може бути однолінійним, але я віддаю перевагу розділенню, використовуючи \онлайновий кінець.

var1='myVar1'\
var2=2\
var3=${var1}\
envsubst '$var1,$var3' < "source.txt" > "destination.txt"

#         ^^^^^^^^^^^    ^^^^^^^^^^     ^^^^^^^^^^^^^^^
# define which to replace   input            output

Змінні повинні бути визначені в тому ж рядку, що і envsubst і для того, щоб розглядати їх як змінні середовища.

'$var1,$var3'Чи не є обов'язковим тільки замінити зазначені застереження. Уявіть собі вхідний файл, що містить, ${VARIABLE_USED_BY_JENKINS}який не слід замінювати.


8
  1. Визначте свою змінну ENV
$ export MY_ENV_VAR=congratulation
  1. Створіть файл шаблону ( in.txt ) із наступним вмістом
$MY_ENV_VAR

Ви також можете використовувати всі інші змінні ENV, визначені вашою системою, наприклад (у Linux) $ TERM, $ SHELL, $ HOME ...

  1. Запустіть цю команду, щоб замінити всі env-змінні у вашому файлі in.txt і записати результат у out.txt
$ envsubst "`printf '${%s} ' $(sh -c "env|cut -d'=' -f1")`" < in.txt > out.txt
  1. Перевірте вміст файлу out.txt
$ cat out.txt

і ви повинні побачити "привітання".


0

Якщо ви дійсно хочете використовувати лише bash (і sed), то я б переглянув кожну з ваших змінних середовища (як повертається setв режимі posix) і побудував купу -e 'regex'for для sed з цього, завершеного-e 's/\$[a-zA-Z_][a-zA-Z0-9_]*//g' , а потім передав все це в sed.

Perl зробить приємнішу роботу, однак у вас є доступ до середовища, яке змінюється як масив, і ви можете виконувати заміну виконуваних файлів, щоб ви лише один раз зіставили будь-яку змінну середовища.


чому хтось би підтримав таку відповідь як ??? -perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' "$main_tf_file"
Йордан Георгієв

0

Насправді вам потрібно змінити свій readнаread -r який змусить його проігнорувати зворотні косу риску.

Крім того, вам слід уникати цитат і зворотних скісних рисок. Тому

while read -r line; do
  line="${line//\\/\\\\}"
  line="${line//\"/\\\"}"
  line="${line//\`/\\\`}"
  eval echo "\"$line\""
done > destination.txt < source.txt

Все ще жахливий спосіб зробити розширення, хоча.


Дійсно, це ризик при виконанні "eval" для файлу, який міг містити поганий код
Бен,

0

Експортуйте всі необхідні змінні, а потім використовуйте perl onliner

TEXT=$(echo "$TEXT"|perl -wpne 's#\${?(\w+)}?# $ENV{$1} // $& #ge;')

Це замінить усі змінні ENV, присутні в TEXT, на фактичні значення. Цитати також зберігаються :)


0

Якщо ви хочете, щоб змінні env були замінені у вихідних файлах, зберігаючи всі змінні не env такими, якими вони є, ви можете використовувати таку команду:

envsubst "$(printf '${%s} ' $(env | sed 's/=.*//'))" < source.txt > destination.txt

Тут пояснено синтаксис заміни лише конкретних змінних . Наведена вище команда використовує допоміжну оболонку для переліку всіх визначених змінних, а потім передає її вenvsubst

Отже, якщо існує визначена змінна env $NAME, і ваш source.txtфайл виглядає так:

Hello $NAME
Your balance is 123 ($USD)

destination.txtбуде:

Hello Arik
Your balance is 123 ($USD)

Зверніть увагу на те, що $NAMEзамінено та $USDзалишено незайманим


0

Викличте двійковий файл perl у режимі пошуку та заміни на кожен рядок (the -pi), запустивши код perl (the -e) в одинарних лапках, який перебирається над клавішами спеціального %ENVхешу, що містять експортовані імена змінних як ключі та експортовані значення змінних як значення ключів і для кожної ітерації просто замінюйте рядок, що містить a, $<<key>>на його <<value>>.

 perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' file

Застереження: додаткова обробка логіки потрібна для випадків, коли два або більше варіантів починаються з одного і того ж рядка ...


-1

envsubstздається точно як щось, що я хотів використати, але -vваріант мене трохи здивував.

Поки envsubst < template.txtпрацював нормально, те саме з опцією -vне працювало:

$ cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.1 (Maipo)
$ envsubst -V
envsubst (GNU gettext-runtime) 0.18.2
Copyright (C) 2003-2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Bruno Haible.

Як я писав, це не спрацювало:

$ envsubst -v < template.txt
envsubst: missing arguments
$ cat template.txt | envsubst -v
envsubst: missing arguments

Мені довелося зробити це, щоб це запрацювало:

TEXT=`cat template.txt`; envsubst -v "$TEXT"

Можливо, це комусь допомагає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.