Yacob
Yacob

Reputation: 525

How to replace a string through grep and perl

I wanted to grep a string at the first occurrence ONLY from a file (file.dat) and replace it by reading from another file (output). I have a file called "output" as an example contains "AAA T 0001"

#!/bin/bash
procdir=`pwd`

cat output | while read lin1 lin2 lin3

do

  srt2=$(echo $lin1 $lin2 $lin3 | awk '{print $1,$2,$3}')

  grep -m 1 $lin1  $procdir/file.dat | xargs -r0 perl -pi -e 's/$lin1/$srt2/g'

done

Basically what I wanted is:

When ever a string "AAA" is grep'ed from the file "file.dat" at the first instance, I want to replace the second and third column next to "AAA" by "T 0001" but still keep the first column "AAA" as it is. Th above script seems not to work.

Basically "$lin1" and $srt2 variables are not understood inside 's/$lin1/$srt2/g'

Example:

in my file.dat I have a row

AAA D ---- CITY COUNTRY

What I want is :

AAA T 0001 CITY COUNTRY

Any comments are very appreciated.

Upvotes: 0

Views: 1436

Answers (2)

ikegami
ikegami

Reputation: 386676

perl -i -e'
   {
      my $f = shift(@ARGV);
      open(my $fh, "<", $f)
         or die("Can'\''t open $f: $!\n");
      while (<$fh>) {
         my ($s,$r) = /^(\S+)\s++(.*)$/;
         $repl{$s} = $r;
      }
   }

   while (<>) {
      s{^(\S+)\s+\K(\S+\s*\S+)}{ delete($repl{$1}) // $2 }e;
      print;
   }
' output file.dat

Upvotes: 0

Greg Bacon
Greg Bacon

Reputation: 139701

Perl shines at this sort of task.

The code below reads the replacements from output and remembers the new fields that go with the first occurrence of each key. Using Perl’s in-place editing, the program then reads the input line by line. When necessary, the code replaces fields 2 and 3 and also deletes the key from %replace so as to only replace the first instance.

#! /usr/bin/env perl

use strict;
use warnings;

die "Usage: $0 replacements data-file\n" unless @ARGV == 2;

sub read_replacements {
  my($path) = @_;

  my %replace;
  open my $fh, "<", $path or die "$0: open $path: $!";
  while (<$fh>) {
    chomp;
    my($key,$f2,$f3) = split;
    warn "$0: $path:$.: multiple replacements for key '$key'" if $replace{$key};
    $replace{$key} = [$f2,$f3];
  }

  %replace;
}

my %replace = read_replacements shift @ARGV;
$^I = "~";  # in-place editing backup extension
while (<>) {
  chomp;
  my($key,@rest) = split;
  if ($replace{$key}) {
    splice @rest, 0, 2 => @{$replace{$key}};
    $_ = join(" ", $key, @rest) . $/;
    delete $replace{$key};
  }
  print;
}

Sample run:

$ cat output
AAA T 0001

$ cat file.dat
AAA D ---- CITY COUNTRY

$ ./replace-first output file.dat

$ cat file.dat
AAA T 0001 CITY COUNTRY

$ cat file.dat~
AAA D ---- CITY COUNTRY

In-place editing is a nice feature, and you could readily extend the above program to replace fields in an arbitrary number of files.

Upvotes: 1

Related Questions