При запуске, например:
bash -c "echo $ BASHPID; exec sleep 10"
или
echo $ ( echo $ BASHPID; exec sleep 10)
ваша текущая оболочка интерполирует переменную $ BASHPID
до того, как ее увидит второй bash (или подоболочка). Решение состоит в том, чтобы предотвратить расширение этих переменных текущей оболочкой:
bash -c 'echo $BASHPID ; exec sleep 10'
Вот уродливое решение для awk; у кого-то может быть более умный способ сделать это:
awk -v pi=1.2.3.4 -v pint=eth0 -v pip=8.8.8 -vaips="8.8.10.1 8.8.10.2 8.8.10.3" \
'BEGIN{
split(aips, array)
}
{
gsub(/@PUBLIC_INTERFACE@/, pint);
gsub(/@PUBLIC_IP@/, pi);
if (/@ADMIN_IPS@/) {
copy=$0
for (i in array) {
gsub(/@ADMIN_IPS@/, array[i], copy)
print copy
copy=$0
}
} else {
print;
}
}' input
Это передает переменные в awk (используя разделение пробелами для IP-адресов администратора); перед чтением любого ввода awk разделит этот переданный «массив» на настоящий awk-массив с именем array
. Для каждой строки ввода awk будет затем глобально искать и заменять различные одноэлементные переменные, а затем, если в строке присутствует @ ADMIN_IPS @, проходит цикл по IP-адресам администратора и распечатывает копии строки ввода с соответствующими IP-адресами администратора. заменены.
Вот решение perl
, использующее Text :: Template
. Сценарий считывает шаблон из строковой переменной ( $ tstr
), выполняет все замены (включая некоторый встроенный код Perl для цикла по массиву @ADMIN_IPS
, а затем распечатывает результат :
(в системе Debian для этого требуется установить пакет libtext-template-perl
)
#! /usr/bin/perl
use strict;
use Text::Template;
# The template, in a string ($tstr):
my $tstr='-A INPUT -i {$PUBLIC_INTERFACE} -p tcp -m tcp --dport 80 -d {$PUBLIC_IP} --syn -j ACCEPT
{
foreach $a (@ADMIN_IPS) {
$OUT .= "-A INPUT -i $PUBLIC_INTERFACE -p tcp -m tcp --dport 22 -d $PUBLIC_IP -s $a --syn -j ACCEPT\n";
}
}
-A INPUT -i {$PUBLIC_INTERFACE} -p tcp -m tcp --dport 443 -d {$PUBLIC_IP} --syn -j ACCEPT
';
# create the Text::Template object ($tt)
my $tt = Text::Template->new(TYPE => 'STRING', SOURCE => $tstr);
# define a hash reference to hold all the replacements:
my $vars = { PUBLIC_INTERFACE => 'eth0',
PUBLIC_IP => '8.8.8.8',
ADMIN_IPS => [ '8.8.10.1', '8.8.10.2', '8.8.10.3' ],
};
# fill in the template
my $text = $tt->fill_in(HASH => $vars);
print $text;
Вывод:
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -d 8.8.8.8 --syn -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -d 8.8.8.8 -s 8.8.10.1 --syn -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -d 8.8.8.8 -s 8.8.10.2 --syn -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -d 8.8.8.8 -s 8.8.10.3 --syn -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 443 -d 8.8.8.8 --syn -j ACCEPT
Для большинства легких шаблонов, подобных этому, скалярный (однозначный) переменные и случайный список (он же массив) или хэш (он же ассоциативный массив) - это все, что вам нужно.
Приведенный выше сценарий можно бесконечно адаптировать для всех видов аналогичных заданий - просто измените шаблон и $ vars
. И совсем не сложно, например, загрузить шаблон из одного файла, а переменные (скаляры и массивы) из другого, так что у вас может быть крошечный повторно используемый скрипт, который принимает два аргумента файла (шаблон и vars).
Помимо самого шаблона и настройки $ vars
, там всего около 5 строк кода. Может быть, 6 или 7 , в зависимости от того, как вы подсчитываете цикл для
в шаблоне.
Шаблоны могут быть прочитаны из имени файла, массива строк, уже открытого дескриптора файла (включая stdin) или строки, как в этом примере.
Вы можете выполнять вычисления, просматривать таблицы, запросы к базе данных (например, с помощью модуля DBI
), извлекать данные из файла CSV, извлекать и обрабатывать выходные данные подпрограмм и внешних программ и многое другое в пределах одного шаблон. Все, что вы можете сделать с perl
.
Для простых скалярных переменных все, что вам нужно сделать, это вставить имя переменной в шаблон в фигурные скобки (например, для переменной $ foo
используйте {$ foo}
). Для массивов, хэшей, вычислений и т. Д. Вам нужно встроить в шаблон некоторый код perl
.
Вот версия, которая считывается в имени файла шаблона и имени файла переменной конфигурации из первых двух аргументов в командной строке:
(в системе Debian для этого требуется libconfig-simple-perl
и libtext-template-perl
пакеты для установки)
#! /usr/bin/perl
use strict;
use Text::Template;
use Config::Simple;
# create the Text::Template object ($tt)
my $tt = Text::Template->new(TYPE => 'FILE', SOURCE => $ARGV[0]);
# read the config file into the `%vars` hash.
my $cfg = new Config::Simple();
$cfg->read($ARGV[1]);
my %vars = $cfg->vars();
# strip "default." from key names.
%vars = map { s/^default\.//r => $vars{$_} } keys(%vars);
# fill in the template
my $text = $tt->fill_in(HASH => \%vars);
print $text;
ПРИМЕЧАНИЕ: сценарий должен удалить по умолчанию.
от начала имени каждого хеш-ключа, потому что файл конфигурации очень похож на файл .INI
и может иметь [разделы]
, как и они. Предполагается, что любые переменные конфигурации, не входящие в раздел, находятся в разделе по умолчанию
. Написание шаблона с такими переменными, как {$ default.PUBLIC_INTERFACE}
было бы утомительным, поэтому решение состоит в том, чтобы исправить ключи хэша % vars
.
Кстати, эти .INI-подобные [разделы]
не обязательно являются проблемой.Их можно эффективно использовать в шаблоне. Но по умолчанию . Префикс
бесполезен и раздражает при использовании с Text :: Template
.
В любом случае, с этим файлом шаблона:
$ cat iptables.tpl
-A INPUT -i {$PUBLIC_INTERFACE} -p tcp -m tcp --dport 80 -d {$PUBLIC_IP} --syn -j ACCEPT
{
my @o=();
foreach $a (@ADMIN_IPS) {
push @o, "-A INPUT -i $PUBLIC_INTERFACE -p tcp -m tcp --dport 22 -d $PUBLIC_IP -s $a --syn -j ACCEPT";
$OUT .= join("\n",@o);
}
}
-A INPUT -i {$PUBLIC_INTERFACE} -p tcp -m tcp --dport 443 -d {$PUBLIC_IP} --syn -j ACCEPT
ПРИМЕЧАНИЕ: этот шаблон немного улучшен, поскольку он использует массив ( @o
) и join ()
, чтобы избежать добавления ненужных дополнительных новые строки - вы заметили, как я «обманул» в $ tstr
в первой версии, добавив еще одну новую строку, чтобы строки вывода массива были в отдельном абзаце?
и этот файл, содержащий переменные:
$ cat iptables.var
PUBLIC_INTERFACE=eth0
PUBLIC_IP=8.8.8.8
ADMIN_IPS=8.8.10.1, 8.8.10.2, 8.8.10.3
Этот файл работал бы точно так же, если бы в первой строке было вставлено [по умолчанию]
.
Кроме того, в отличие от большинства форм файлов .INI, в этом есть очень простой способ определения переменных массива: просто разделите их запятой с необязательными дополнительными пробелами, что именно то, что вы просили в своем вопросе.
Кстати, пробелы игнорируются в определениях переменных, если вы не заключили их в кавычки. См. man Config :: Simple
для получения более подробной информации о конфигурационных файлах.
Запустите его так:
$ ./alexis.pl iptables.tpl iptables.var
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -d 8.8.8.8 --syn -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -d 8.8.8.8 -s 8.8.10.1 --syn -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -d 8.8.8.8 -s 8.8.10.2 --syn -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -d 8.8.8.8 -s 8.8.10.3 --syn -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 443 -d 8.8.8.8 --syn -j ACCEPT
Он намеренно минималистичен (то есть очень быстрый и грязный, например, даже не пытается проверить существование файлов), и есть много способов его улучшить, но это полностью - Функциональный пример того, как выполнять основную работу.
Если у вас есть возможность прикрепить все строки для каждого IP-адреса, я могу предложить следующее
#!/bin/bash
first="-A INPUT -i eth0 -p tcp -m tcp --dport 22 -d 8.8.8.8 -s 10.2.2.1 --syn -j ACCEPT"
second="-A INPUT -i eth0 -p tcp -m tcp --dport 22 -d 8.8.8.8 -s 10.2.2.2 --syn -j ACCEPT"
third="-A INPUT -i eth0 -p tcp -m tcp --dport 22 -d 8.8.8.8 -s 10.2.2.3 --syn -j ACCEPT"
sed 's/.*@ADMIN_IPS@.*/${first}\n${second}\n${third}/g' file
Возможно, это не самое чистое, но может быть решением
попробуйте
{{1 }}Пожалуйста, попробуйте :::::
given::
echo $ADMIN_IPS
8.8.10.1,8.8.10.2,8.8.10.3
и входной файл / tmp / myvar с содержимым, как показано ниже: ::
-A INPUT -i @ PUBLIC_INTERFACE @ -p tcp -m tcp --dport 80 -d @ PUBLIC_IP @ --syn -j ACCEPT
-A INPUT -i @ PUBLIC_INTERFACE @ -p tcp -m tcp --dport 22 -d @ PUBLIC_IP @ -s @ ADMIN_IPS @ --syn -j ACCEPT
-A INPUT -i @ PUBLIC_INTERFACE @ -p tcp -m tcp --dport 443 -d @ PUBLIC_IP @ --syn -j ПРИНЯТЬ
while read j; do savedline=$(echo "$j"|sed 's/@PUBLIC_INTERFACE@/eth0/'|sed 's/@PUBLIC_IP@/1.1.1.1/'); for i in $(echo $ADMIN_IPS |tr ',' '\n'); do echo $savedline|sed "s/@ADMIN_IPS@/$i/";done|uniq;done < /tmp/myvar
Попытайтесь сообщить нам об этом.