동일한 줄을 여러 값으로 바꾸기 위해 "sed..."와 동일한 작업을 수행하는 쉬운 방법이 있습니까?

동일한 줄을 여러 값으로 바꾸기 위해 "sed..."와 동일한 작업을 수행하는 쉬운 방법이 있습니까?

파일이 있는데 그 내용 중 일부를 바꾸고 싶습니다.변하기 쉬운쉘 스크립트의 데이터.

-A INPUT -i lo -s @LOCAL_IP@ -j ACCEPT

여기서는 @LOCAL_IP@을 IP 주소로 바꾸고 싶습니다. 다음을 사용합니다.

... | sed -e 's/@LOCAL_IP@/192.168.1.1/' | ...

아마도 쉘 스크립트의 변수에 192.168.1.1이 있을 것입니다. 그러나 이는 단지 기본적인 아이디어를 제공하기 위한 것입니다.

이제 다음과 같은 또 다른 규칙이 있습니다.

-A INPUT -i @PUBLIC_INTERFACE@ -p tcp -m tcp --dport 22
            -d @PUBLIC_IP@ -s @ADMIN_IPS@ --syn -j ACCEPT

(이것은 긴 줄이며, 가독성을 위해 여기서는 끊어졌습니다.)

실제 입력 파일에는 입력 파일의 내용과 유사한 줄이 많이 포함되어 있지만 대부분은 그렇지 않습니다 @ADMIN_IPS@. 예를 들면 다음과 같습니다.

-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 ACCEPT

이 경우 @PUBLIC_INTERFACE@ 및 @PUBLIC_IP@을 쉽게 바꿀 수 있습니다. 이는 이전 줄과 완전히 동일합니다. 그러나 @ADMIN_IPS@는 2~3개의 IP 주소 목록입니다. 결과는 다음과 같아야 합니다.

-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 80 -d 8.8.8.8 --syn -j ACCEPT

Linux와 같은 도구를 사용하여 한 줄을 여러 줄로 변환하는 비교적 쉬운 방법이 있습니까 sed? (Ubuntu 16.04) 이 경우 이 세 개의 ADMIN_IPS주소는 다음과 같이 변수에 정의됩니다.

ADMIN_IPS=8.8.10.1,8.8.10.2,8.8.10.3

공백으로 구분되거나 배열일 수도 있습니다.

답변1

이것은 추악한 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 교체로 입력 줄의 복사본을 인쇄합니다. .

답변2

시도해 보세요:::::

given::
echo $ADMIN_IPS 

8.8.10.1,8.8.10.2,8.8.10.3

입력 파일 /tmp/myvar의 내용은 다음과 같습니다::::

-A 입력 -i @PUBLIC_INTERFACE@ -p tcp -m tcp --dport 80 -d @PUBLIC_IP@ --syn -j 수락

-A 입력 -i @PUBLIC_INTERFACE@ -p tcp -m tcp --dport 22 -d @PUBLIC_IP@ -s @ADMIN_IPS@ --syn -j 수락

-A 입력 -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

우리에게 알려주세요.

답변3

perl여기에 사용된 솔루션이 있습니다 Text::Template. 스크립트는 문자열 변수( )에서 템플릿을 읽고 $tstr모든 대체를 수행합니다(배열을 반복하는 일부 내장된 Perl 코드 포함). @ADMIN_IPS그런 다음 결과를 인쇄합니다.

(데비안 시스템에서는 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설정을 제외하고 코드는 약 5줄뿐입니다. for템플릿의 루프 수를 계산하는 방법에 따라 6 또는 7이 될 수 있습니다 .

이 예제에 표시된 대로 템플릿은 파일 이름, 라인 배열, 열린 파일 핸들(표준 입력 포함) 또는 문자열에서 읽을 수 있습니다.

템플릿 내에서 계산, 테이블 조회, 데이터베이스 쿼리(예: 모듈 사용 DBI), CSV 파일에서 데이터 추출, 서브루틴 및 외부 프로그램의 출력 가져오기 및 처리 등을 수행할 수 있습니다. 당신은 그것을 무엇이든 사용할 수 있습니다 perl.

간단한 스칼라 변수의 경우 템플릿의 중괄호 안에 변수 이름을 삽입하기만 하면 됩니다(예: using Variable $foo, use {$foo}). 배열, 해시, 계산 등의 경우 perl템플릿에 일부 코드를 포함 해야 합니다 .


다음은 명령줄의 처음 두 인수에서 템플릿 파일 이름과 구성 변수 파일 이름을 읽는 버전입니다.

(데비안 시스템에서는 설치 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;

참고: 구성 파일은 파일과 매우 유사하고 유사한 파일이 있을 수 있으므로 default.각 해시 키 이름의 시작 부분에서 스크립트를 제거해야 합니다. 섹션에 없는 모든 구성 변수는 해당 섹션에 있는 것으로 간주됩니다. 이와 같은 변수를 사용하여 템플릿을 작성하는 것은 지루하므로 해결책은 해시의 키를 수정하는 것입니다 ..INI[sections]default{$default.PUBLIC_INTERFACE}%vars

그런데 이러한 .INI 형식의 파일이 [sections]반드시 문제가 되는 것은 아닙니다. 템플릿에서 이를 최대한 활용할 수 있습니다. 그러나 default.와 함께 사용하면 접두사는 의미가 없고 짜증스럽습니다 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

[default]파일을 첫 번째 줄에 삽입하면 정확히 같은 방식으로 작동합니다.

또한 대부분의 .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

이는 의도적으로 미니멀하며(즉, 매우 빠르고 지저분합니다. 예를 들어 파일의 존재를 확인하려고 시도하지도 않습니다) 이를 개선할 수 있는 방법은 많지만 기본 작업을 완료하는 방법에 대한 완전한 기능적 예입니다.

답변4

각 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

가장 깨끗하지는 않지만 해결책이 될 수 있습니다.

시도 해봐

관련 정보