Есть ли простой способ сделать эквивалентную `sed…` замену одной и той же строки несколькими значениями?

При запуске, например:

bash -c "echo $ BASHPID; exec sleep 10"

или

echo $ ( echo $ BASHPID; exec sleep 10)

ваша текущая оболочка интерполирует переменную $ BASHPID до того, как ее увидит второй bash (или подоболочка). Решение состоит в том, чтобы предотвратить расширение этих переменных текущей оболочкой:

bash -c 'echo $BASHPID ; exec sleep 10'
5
08.07.2016, 05:31
4 ответа

Вот уродливое решение для 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-адресами администратора. заменены.

3
27.01.2020, 20:39

Вот решение 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

Он намеренно минималистичен (то есть очень быстрый и грязный, например, даже не пытается проверить существование файлов), и есть много способов его улучшить, но это полностью - Функциональный пример того, как выполнять основную работу.

1
27.01.2020, 20:39

Если у вас есть возможность прикрепить все строки для каждого 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 }}
0
27.01.2020, 20:39

Пожалуйста, попробуйте :::::

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

Попытайтесь сообщить нам об этом.

1
27.01.2020, 20:39

Теги

Похожие вопросы