여러 IP 주소와 호스트 이름이 포함된 파일과 한 줄에 여러 IP 주소가 있는 일부 폴더가 포함된 다른 파일이 있습니다.
IP_호스트 이름.txt
host1 10.1.1.1
host2 10.2.2.2
host3 10.3.3.3
host100 10.50.50.50
path_ips.txt
/path1/foo/bar 10.1.1.1 10.2.2.2 10.3.3.3
/path2/foo/bar 10.3.3.3 10.7.7.7
/path3/foo/bar 10.4.4.4 10.8.8.8 10.29.29.29 10.75.75.75
/path100/foo/bar 10.60.60.60
IP 주소를 변경하고 싶어요path_ips.txt호스트 이름이 나오는 파일IP_호스트 이름.txt각 IP 주소와 일치하는 파일입니다.
원하는 출력path_ips.txt
/path1/foo/bar host1 host2 host3
/path2/foo/bar host3 host7
/path3/foo/bar host4 host8 host29 host75
/path100/foo/bar host60
나는 중첩된 sed를 사용하여 이 작업을 시도했습니다.읽는 동안루프는 다음과 같습니다:
#!/bin/sh
while read -r line
do
IP=$(echo $line| awk '{print $1}')
HN=$(echo $line| awk '{print $2}')
while read -r line2
do
sed -i "s/$IP/$HN/g" path_ips.txt
echo $line2 #to see the progress
done < path_ips.txt
done < ip_hostname.txt
IP 주소와 호스트 이름 목록이 그다지 크지 않을 때 처음에는 잘 작동하지만 더 큰 목록을 사용하려고 하면 잘 작동합니다.IP_호스트 이름.txt파일을 열면 이상하게 동작하고 결과가 예상과 다릅니다. 말할 필요도 없이 완료하는 데 오랜 시간이 걸립니다.
이를 수행하는 더 좋고 효율적인 방법이 있습니까?
답변1
스크립트의 문제점은 sed
일치하는 각 IP 주소에 대해 별도의 명령을 실행하므로 파일이 클 때 스크립트 속도가 매우 느려진다는 것입니다.
또한 중첩 루프가 있으므로 O(N*M)
알고리즘에 시간 복잡도가 있습니다.
더 나은 접근 방식은 대체 수행을 사용하는 것입니다. awk
이 방법을 사용하면 한 번에 모든 대체를 수행할 수 있습니다.
$ awk 'NR==FNR{h[$2]=$1;next}{for (i=2;i<=NF;i++) if ($i in h) $i = h[$i]}1' ip_hostname.txt path_ips.txt
/path1/foo/bar host1 host2 host3
/path2/foo/bar host3 host7
/path3/foo/bar host4 host8 host29 host75
...
/path100/foo/bar host60
또는 더 읽기 쉬운 형식으로
awk '
NR == FNR {
h[$2] = $1
next
}
{
for (i=2; i<= NF; i++)
if ($i in h)
$i = h[$i]
}
1
' ip_hostname.txt path_ips.txt
이것은 파일 크기 O((N+M)lon(N))
와 파일 크기가 복잡해야 합니다 . 제대로 작동하려면 메모리에 들어갈 수 있어야 하지만 최신 컴퓨터에서는 크기가 몇 GB가 아니면 문제가 되지 않습니다.N
ip_hostname.txt
M
path_ips.txt
ip_hostname.txt
답변2
이 작업은 sed에서 완전히 수행할 수 있지만 일반적으로 awk 답변이 더 읽기 쉽습니다.
#file toggle
1{x;s:^$:<IPs>:;x}
/^EOF$/{x;s:<IPs>:<paths>:;x;d}
#store hostname file
x;/<IPs>/{x;H;d}
#process path file
x;s: :>&:;s:$: :;G
:loop
s:>( [^ ]+)( .*<paths>.*)\n([^ ]+)\1: \3>\2\n\3\1:
tloop
s:> .*::p
예제에 표시된 대로 코드에서는 공백을 파일 구분 기호로 가정합니다. 즉, 공백이 포함된 경로가 있으면 답이 틀릴 가능성이 높습니다.
이는 GNU sed를 사용하여 테스트되었지만 다른 sed 버전이 있을 수 있습니다. 그래도 문제가 해결되지 않으면 알려 주시기 바랍니다.
달리기:
sed -nrf SCRIPT_FILE ip_hostname.txt <(echo EOF) path_ips.txt > output.txt
참고: <(echo EOF)
첫 번째 입력 파일이 끝나는 시점을 스크립트에 알리는 데 사용됩니다.
답변3
POSIX awk를 사용하십시오.
$ cat tst.awk
NR==FNR {
map[$2] = $1
next
}
match($0,/([[:space:]]+([0-9]{1,3}\.){3}[0-9]{1,3})+$/) {
path = substr($0,1,RSTART-1)
$0 = substr($0,RSTART,RLENGTH)
for ( i=1; i<=NF; i++ ) {
$i = ($i in map ? map[$i] : $i)
}
$0 = path OFS $0
}
{ print }
$ awk -f tst.awk ip_hostname.txt path_ips.txt
/path1/foo/bar host1 host2 host3
/path3/foo/bar 10.4.4.4 10.8.8.8 10.29.29.29 10.75.75.75
/path100/foo/bar 10.60.60.60
이는 경로에 공백이 포함된 경우에도 작동합니다. 단, 경로의 파일 이름 부분이 공백으로 끝나고 그 뒤에 IP 주소처럼 보이는 문자열이 올 수 있는 경우는 예외입니다. 예를 들어 IP 주소 대신 파일 이름의 일부인 /path/foo/bar 1.1.1.1
경우 . 이런 일이 발생하면 ip_hostname.txt에서 다른 형식을 사용하여 경로와 IP 주소를 구분해야 합니다.1.1.1.1
bar 1.1.1.1
답변4
다음 Perl 스크립트는 첫 번째 입력 파일( ip_hostname.txt
)을 읽고 %IPs라는 연관 배열(해시)을 구축합니다. 여기서 키는 IP 주소이고 값은 호스트 이름입니다.
성능 최적화를 위해 해시의 각 키는 실제로 %IPs
단어 경계 표시( ) 및 이스케이프된 메타 문자( & )가 포함된 IP 주소( )의 미리 컴파일된 정규식이므로 문자가 아닌 리터럴을 의미합니다.qr//
\b
\Q
\E
.
.
ip_hostname.txt
정규식을 미리 컴파일하면 path_ips.txt의 줄당 IP 주소당 1(즉, 줄 수 x 의 줄 수 path_ips.txt
)에서 IP 주소당 1개 까지 정규식을 컴파일하는 데 소요되는 CPU 시간이 최소화됩니다 . 파일 중 하나 또는 둘 다 큰 경우 성능에 상당한 영향을 미칩니다.
변수는 $first
스크립트가 첫 번째 파일을 읽고 있는지 추적하는 데 사용됩니다. 메인 루프 이전에는 true(1)로 초기화되고 while
각 입력 파일의 끝에서는 false(0)로 설정됩니다.
첫 번째 파일을 처리한 후 두 번째 파일( )의 각 줄에 대해 해시를 path_ips.txt
반복하여 각 IP 주소를 검색하고 이를 연결된 호스트 이름으로 바꿉니다. %IPs
그런 다음 (수정될 수 있는) 입력 행을 인쇄합니다.
각 줄에서 일치하는 IP 주소만 변경하고 나머지(공백 포함)는 그대로 둡니다.
#!/usr/bin/perl
use strict;
my %IPs;
my $first = 1;
while(<>) {
if ($first) {
chomp; # strip \n or \r\n line-endings
my ($host,$ip) = split; # assumes whitespace delimited input
$IPs{qr/\b\Q$ip\E\b/} = $host;
} else {
foreach my $ip (keys %IPs) {
s/$ip/$IPs{$ip}/g;
};
print;
};
$first = 0 if eof;
};
#use Data::Dump qw(dd);
#dd \%IPs;
예를 들어 다른 이름으로 저장 map-hostnames.pl
하고 chmod +x
.
출력 예( ip_hostname.txt
질문에 언급된 모든 IP/호스트에 대한 매핑을 포함하도록 파일 편집):
$ ./map-hostnames.pl ip_hostname.txt path_ips.txt
/path1/foo/bar host1 host2 host3
/path2/foo/bar host3 host7
/path3/foo/bar host4 host8 host29 host75
/path100/foo/bar host60
그런데, 해시가 어떻게 생겼는지 보려면 %IPs
스크립트의 마지막 두 줄의 주석 처리를 해제하세요(필수).데이터::덤프모듈을 설치해야 함). 다음과 같이 보이지만 ip_hostname.txt
실제 파일의 내용이 포함되어 있습니다.
{
"(?^:\\b10\\.1\\.1\\.1\\b)" => "host1",
"(?^:\\b10\\.29\\.29\\.29\\b)" => "host29",
"(?^:\\b10\\.2\\.2\\.2\\b)" => "host2",
"(?^:\\b10\\.3\\.3\\.3\\b)" => "host3",
"(?^:\\b10\\.4\\.4\\.4\\b)" => "host4",
"(?^:\\b10\\.50\\.50\\.50\\b)" => "host100",
"(?^:\\b10\\.60\\.60\\.60\\b)" => "host60",
"(?^:\\b10\\.75\\.75\\.75\\b)" => "host75",
"(?^:\\b10\\.7\\.7\\.7\\b)" => "host7",
"(?^:\\b10\\.8\\.8\\.8\\b)" => "host8",
}