他のファイルのパターンと一致するファイルの複数行にある複数の部分文字列を置き換える方法は?

他のファイルのパターンと一致するファイルの複数行にある複数の部分文字列を置き換える方法は?

複数の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 でなければ問題ありません。Nip_hostname.txtMpath_ips.txtip_hostname.txt

おすすめ記事