【问题标题】:Need to replace string in file2 that matches first column of file1 with second column of file1需要将 file2 中与 file1 的第一列匹配的字符串替换为 file1 的第二列
【发布时间】:2011-10-14 01:05:18
【问题描述】:

所以,如果标题没有意义,这就是我想要做的:

我有文件 1:

66.115.135.84:123.123.123.1
66.115.135.85:123.123.123.2
66.115.135.86:123.123.123.3
66.115.135.87:123.123.123.4
66.115.135.88:123.123.123.5
66.115.135.89:123.123.123.6
66.115.135.90:123.123.123.7
66.115.135.91:123.123.123.8
66.115.135.92:123.123.123.9
66.115.135.93:123.123.123.10
66.115.135.94:123.123.123.11
66.115.135.95:123.123.123.12
66.115.135.96:123.123.123.13
66.115.135.97:123.123.123.14

如您所见,它是 ip 地址,以“:”分隔

File2 基本上是一个 apache 虚拟主机条目,或 httpd.conf 文件。这并不重要。只要知道 file2 包含 file1 第一列中某处的 IP 地址。并且它们需要被file1的第二列替换。

出于某种原因,这个简单的问题让我感到困惑。我尝试了一些非常棘手的事情,但一直卡住。

我知道我可以使用 awk 将它们分开,并且我知道我可以将其通过管道传输到 sed 中以作用于 file2。

但我似乎无法理解将第 1 列“映射”到第 2 列的最佳方法,以便实际发生这种情况。

我愿意使用 perl、ruby、python 或任何实现此目的的方法,我非常希望简要说明您如何解决此问题。

请要求任何澄清,我很乐意提供。

提前非常感谢!

【问题讨论】:

    标签: perl bash sed awk


    【解决方案1】:

    将 file1 中的 IP 对读入哈希,例如$ip{$old} = $new。我假设没有重复的IP。通过 file2 查找 IP,并使用正则表达式,例如:

    s#($IPregex)# $ip{$1} // $1 #eg;
    

    代码如下:

    use autodie;
    
    open my $fh, '<', "file1";
    my %ip;
    while (<$fh>) {
        chomp;
        my ($key, $val) = split /:/, $_, 2;
        $ip{$key} = $val;
    }
    
    open $fh, '<', "file2";
    my $rx = qr/\b\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3}\b/;
    
    while (<$fh>) {
        s#($rx)# $ip{$1} // $1 #eg;
        print;
    }
    

    根据需要重定向到输出文件。可能需要更好的 IP 正则表达式。

    【讨论】:

    • 我没看到这个。这很棒,而且可读性很强。感谢所有我要“窃取”并重新利用其代码的人;)
    • @Kevin 你不是在偷东西,凯文。如果我担心有人窃取我的代码,我就不会在互联网上发布它。 =P 享受。
    【解决方案2】:
    perl  -ne '/(.*):(.*)/; (exists $ips{$1}) ? (print "$ips{$1}\n") : ($ips{$1} = $2);' f1 f2
    

    这会遍历文件 f1,然后是文件 f2。它将它们拆分为 ':' 字符,如果我们之前没有看过前半部分,请将其粘贴在哈希中。如果我们之前看过前半部分,请打印我们存储在哈希中的值。

    【讨论】:

    • 我还不能让这个工作,但我知道它的去向,太棒了!
    【解决方案3】:
    sed -e "s:$(sed -e ':a;$!N;s/\n/:g;s:/g;ta' file1):" file2
    

    内部 sed 为外部 sed 创建一个多表达式正则表达式以应用于 file2..
    为了安全地更新您的原始文件,您可以通过sponge(来自包moreutils)将输出传输到ir。

    【讨论】:

      【解决方案4】:
      awk '
        FILENAME == ARGV[1] {
          split($0, ary, /:/)
          map[ary[0]] = ary[1]
          next
        }
        {
          for (i=1; i<=NF; i++) {
            if ($i in map)
              $i = map[$i]
          }
          print
        }
      ' file1 file2 > file2.new
      

      【讨论】:

      • 起初我在 awk 中遇到了类似的情况,无法让我的工作。谢谢!谁说 awk 过时且难以理解... ;)
      【解决方案5】:

      我使用 perl。让我们称之为mapper.pl。将映射文件作为 arg,然后将 stdin 映射到 stdout。所以你就这样使用它

      perl mapper.pl file1 < file2 > file2.new
      

      mapper.pl 程序类似于:

      use strict;
      use warnings;
      
      # Prototypes
      sub readMap($);
      
      # Main program
      {
          if( scalar(@ARGV) != 1 )
          {
              die "usage: mapper.pl mapfile";
          }
          my %map = readMap( $ARGV[0] );
          while( my $line = <STDIN> )
          {
              foreach my $old ( keys(%map) )
              {
                  my $old_re = $old;
                  # Escape metacharacters
                  $old_re =~ s/\W/\\$&/g;
                  $line =~ s/$old_re/$map{$old}/g;
              }
              print $line;
          }
      } # END main
      
      sub readMap($)
      {
          my $mapname = $_[0];
          my %map;
          open( MAPFILE, "<$mapname" ) || die "open($mapname): $!";
          while( my $line = <MAPFILE> )
          {
              if( $line =~ /^\s*([^:]+):(.*?)\s*$/ )
              {
                  $map{$1} = $2;
              }
              else
              {
                  warn "Invalid line: $line";
              }
          }
          close( MAPFILE );
          return( %map );
      } # END readMap
      

      【讨论】:

      • 哇。我对其进行了测试,并且效果很好。在我完全理解之前,我将不得不解决这个问题,因为我不经常使用 perl。我真的很感激。
      • 您可以使用/\Q$old\E/... 而不是尝试转义复制变量中的所有字母数字字符。或quotemeta.
      • @TLP,谢谢伙计。每天学些新东西。也令人沮丧,我已经做了将近 10 年的狡猾方式。适合我在学习语言时浏览手册页。再次感谢。
      • @Sodved 我知道你的意思。 SO 非常适合学习你不知道你需要知道的东西。 =)
      【解决方案6】:

      感谢所有出色的答案!

      我受到他们的启发创建了一个 ruby​​ 版本:(它可以使用一些工作/减少,它还不是很 ruby​​esque,但它可以工作)

      #!/usr/bin/ruby
      #replaces old ips for new ips in virt file
      @orig_ips=Array.new
      @new_ips=Array.new
      File.open("/home/kevin/scripts/ruby_scripts/test.virt", "r").each do |line|
        if line =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
          @orig_ips.push(line.split.last.chop)
        end
      end
      
      File.open("/home/kevin/scripts/ruby_scripts/new_ip_list", "r").each do |line|
        @new_ips.push(line.split.last)
      end
      
      f = File.open("/home/kevin/scripts/ruby_scripts/test.virt")
      working_file = f.read
      for count in 0..@orig_ips.count - 1  do
        old = @orig_ips[count]
        new = @new_ips[count]
        working_file.gsub!(old, new)
      end
      puts working_file
      

      【讨论】:

      • 你可以说File.open(filename).each {}而不是File.foreach(filename) {}。使用chomp 而不是chop(它更安全,因为它只删除了换行符)。你不喜欢 new_ip_list 文件吗?如果你 File.open 没有阻止,你应该在完成后关闭它。你可以说File.read(filename) 而不是f=File.open(filename); f.read; f.close
      • 实际上,我搞砸了,我忘记了,我在发布之前更改了输入文件,因此,我使用了一个 new_ips 文件,该文件只有一行由石灰 ip 地址列表组成,并且正在执行直接在虚拟主机文件上。所以我实际上是在砍掉“>”。真的很脏,它已经进化了,你关闭文件是绝对正确的。还有我刚刚保存在内存库中的 foreach,谢谢!
      猜你喜欢
      • 2015-04-16
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      • 2020-06-01
      • 1970-01-01
      • 2016-01-02
      • 2020-03-24
      • 2017-09-07
      相关资源
      最近更新 更多