파일이 있는데 그 내용 중 일부를 바꾸고 싶습니다.변하기 쉬운쉘 스크립트의 데이터.
-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
가장 깨끗하지는 않지만 해결책이 될 수 있습니다.
시도 해봐