需要将file1中与string1的第一列匹配的字符串替换为file1的第二列

时间:2011-10-12 05:28:26

标签: perl bash sed awk

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

我有file1:

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以对文件2进行操作。

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

我愿意使用perl,ruby或python,或者真正实现这一目标的任何方法,我非常想简要解释一下如何解决这个问题。

请询问任何澄清,我很乐意提供。

提前非常感谢!

6 个答案:

答案 0 :(得分:2)

将文件1中的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正则表达式。

答案 1 :(得分:1)

perl  -ne '/(.*):(.*)/; (exists $ips{$1}) ? (print "$ips{$1}\n") : ($ips{$1} = $2);' f1 f2

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

答案 2 :(得分:1)

sed -e "s:$(sed -e ':a;$!N;s/\n/:g;s:/g;ta' file1):" file2

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

Id使用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

答案 5 :(得分:0)

感谢所有出色的答案!

我受到他们的启发,创造了一个红宝石版本:(它可以使用一些工作/减少,它不是非常rubyesque,但它的工作原理)

#!/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