4

I have a file that contains the map between the words. I have to refer to that file and replace those words with the mapped ones in some files. For example, below file has the table of words that are mapped like

1.12.2.4               1
1.12.2.7               12
1.12.2.2               5
1.12.2.4               4
1.12.2.6               67
1.12.2.12              5

I will have many files that has those key words (1.12.2.*). I want to search for these key words and replace those words with the corresponding mapping taken from this file. How to do this in shell. Suppose a file contains the following lines say

The Id of the customer is 1.12.2.12. He is from Grg. 
The Name of the machine is ASB
The id is 1.12.2.4. He is from Psg.

After executing the script, the Numbers "1.12.2.12" and "1.12.2.4" should be replaced by 5 and 4 (referred from the master file). Can anyone help me out?

tripleee
  • 175,061
  • 34
  • 275
  • 318
user1667630
  • 297
  • 6
  • 15

3 Answers3

9

One way using GNU awk:

awk 'FNR==NR { array[$1]=$2; next } { for (i in array) gsub(i, array[i]) }1' master.txt file.txt

Results:

The Id of the customer is 5. He is from Grg.
The Name of the machine is ASB
The id is 4. He is from Psg.

To save output to a file:

awk 'FNR==NR { array[$1]=$2; next } { for (i in array) gsub(i, array[i]) }1' master.txt file.txt > name_of_your_output_file.txt

Explanation:

FNR==NR { ... }   # FNR is the current record number, NR is the record number
                  # so FNR==NR simply means: "while we process the first file listed
                  # in this case it's "master.txt"
array[$1]=$2      # add column 1 to an array with a value of column 2
next              # go onto the next record

{                 # this could be written as: FNR!=NR
                  # so this means "while we process the second file listed..."
for (i in array)  # means "for every element/key in the array..."
gsub(i, array[i]) # perform a global substitution on each line replacing the key
                  # with it's value if found
}1                # this is shorthand for 'print'

Adding word boundaries makes the matching more strict:

awk 'FNR==NR { array[$1]=$2; next } { for (i in array) gsub("\\<"i"\\>", array[i]) }1' master.txt file.txt
Steve
  • 51,466
  • 13
  • 89
  • 103
  • 1
    Obviously, this will break if the keys in `master.txt` are too similar – Steve Sep 13 '12 at 07:25
  • Hi.. I want these values to be written in a file. How can I do this? I am a newbie in shell scripting. Sorry :(.. Thanks in advance.. – user1667630 Sep 13 '12 at 07:29
  • Actually gsub replaces even if a part of the line matches.. Like suppose mapping file contains entries like 10.1.1.12 1 and the file where I need to change has two entries say. 10.1.1.12 and 10.1.1.1234 then this replaces both the lines.. How to get rid of that? – user1667630 Sep 14 '12 at 06:42
  • @steve: I missed the `next` in the first block, so ignore my previous comment. @user1667630: to match only whole words you can add word boundaries when matching, e.g.: `gsub("\\<" i "\\>", array[i])`; and it might be an idea to only let the first match have effect: `if( gsub("\\<" i "\\>", array[i]) ) break; – Thor Sep 14 '12 at 07:39
  • @Thor: Thanks for the excellent suggestion. I was unaware of this syntax :-) – Steve Sep 15 '12 at 03:13
6

You could have sed write a sed script for you:

The mappings:

cat << EOF > mappings
1.12.2.4               1
1.12.2.7               12
1.12.2.2               5
1.12.2.4               4
1.12.2.6               67
1.12.2.12              5
EOF

Input file:

cat << EOF > infile
The Id of the customer is 1.12.2.12. He is from Grg. 
The Name of the machine is ASB
The id is 1.12.2.4. He is from Psg.
EOF

Generate a script based on the mappings (GNU sed):

sed -r -e 's:([^ ]*) +(.*):s/\\b\1\\b/\2/g:' mappings

Output:

s/\b1.12.2.4\b/1/g
s/\b1.12.2.7\b/12/g
s/\b1.12.2.2\b/5/g
s/\b1.12.2.4\b/4/g
s/\b1.12.2.6\b/67/g
s/\b1.12.2.12\b/5/g

Evaluate with another sed (GNU sed):

sed -r -e 's:([^ ]*) +(.*):s/\\b\1\\b/\2/g:' mappings | sed -f - infile

Output:

The Id of the customer is 5. He is from Grg. 
The Name of the machine is ASB
The id is 1. He is from Psg.

Note that the mappings are treated as regular expressions, e.g. a dot (.) can mean any character, and may need escaping either in the mappings file or when generating the sed script.

Thor
  • 45,082
  • 11
  • 119
  • 130
  • This is not working.. I am getting this error while executing.. sed: -e expression #1, char 26: invalid reference \2 on `s' command's RHS – user1667630 Sep 14 '12 at 06:06
  • Forgot that I have aliased `sed` to `sed -r`. I've added `-r` to the relevant expressions. – Thor Sep 14 '12 at 07:24
  • Still I get error.. sed: file - line 1: unknown command: `.' !! Somethig is wrong i guess.. – user1667630 Sep 14 '12 at 17:33
  • 1
    @user1667630: what version of sed are you using? I just tested this with FreeBSD sed where it works if you replace the `-f -`with `-f /dev/stdin`. As mentioned elsewhere, matching the word boundary of the key is a good idea, so I added it, but now the generated expressions do not work. I think you should try to use GNU sed if you can, often called gsed, it gives you more options. – Thor Sep 14 '12 at 21:16
  • In the worst case, save the generated script to a temporary file, run `sed -f /tmp/temporary.sed`, then remove the temporary file. – tripleee Jan 11 '16 at 09:42
0

Since you havent provided any example,I guess this is what you want:

Input file

> cat temp
1.12.2.4  1
1.12.2.7  12
1.12.2.2  5
1.12.2.4  4
1.12.2.6  67
1.12.2.12  5

file to be relaced

> cat temp2
The Id of the customer is 1.12.2.12. He is from Grg. 
The Name of the machine is ASB
The id is 1.12.2.4. He is from Psg.

output

> temp.pl
The Id of the customer is 5. He is from Grg. 
The Name of the machine is ASB
The id is 4. He is from Psg

>

Below is the perl script.

#!/usr/bin/perl

use strict;
use warnings;

my %hsh=();

open (MYFILE, 'temp');
open (MYFILE2, 'temp2');

while (<MYFILE>) {
my@arr = split/\s+/;
$hsh{$arr[0]} = $arr[1];
}
my $flag;
while(<MYFILE2>)
{
$flag=0;
my $line=$_;
foreach my $key (keys %hsh)
{
   if($line=~/$key/)
   {
    $flag=1; 
    $line=~s/$key/$hsh{$key}/g;
    print $line;
   }
}
  if($flag!=1)
  {
  print $line;
  $flag=0;
  }
}
close(MYFILE);
close(MYFILE2);
Vijay
  • 65,327
  • 90
  • 227
  • 319