複数のIPアドレスとホスト名を含むファイルと、1行に複数の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