4

I have a text file containing a list of nine-digit numbers like this:

550411876
550425175
550426504

And a second text file that contains a single line like this:

09Y20171031    13415520171031    134155AP|AO|ABxxxxxxxxx|AC|CH|BIN|

I need to make a text file that contains many occurrences of the line from the second file, but with the xxxxxxxxx replaced with the number from the first file (the xxxxxxxxx is literally in the file, it's not just an example). In this case the result would be:

09Y20171031    13415520171031    134155AP|AO|AB550411876|AC|CH|BIN|
09Y20171031    13415520171031    134155AP|AO|AB550425175|AC|CH|BIN|
09Y20171031    13415520171031    134155AP|AO|AB550426504|AC|CH|BIN|

How do I do this in Linux?

0

5 Answers 5

6

Assuming the numbers are in the file numbers and that the template file that you'd like to use is file:

awk -F'|' -v OFS='|' 'NR==FNR { n[++i] = $0; next } { for (i in n) { $3 = substr($3,1,2) n[i]; print } }' numbers file

This would first read the numbers into the array n, and then, for the template file, use the first two characters from the third |-delimited field, concatenate that with the numbers from the n array and print the result once for each number.

The -F'|' -v OFS='|' options makes sure that we both read and write the data as |-delimited.

Then just redirect the output of this into a new file.

This does not rely on the template to contain xxxxxxxxx.

Testing it:

$ cat file
09Y20171031    13415520171031    134155AP|AO|AB something something|AC|CH|BIN|

$ awk -F'|' -v OFS='|' 'NR==FNR { n[++i] = $0; next } { for (i in n) { $3 = substr($3,1,2) n[i]; print } }' numbers file
09Y20171031    13415520171031    134155AP|AO|AB550425175|AC|CH|BIN|
09Y20171031    13415520171031    134155AP|AO|AB550426504|AC|CH|BIN|
09Y20171031    13415520171031    134155AP|AO|AB550411876|AC|CH|BIN|

A bash-only approach (no external utilities) which uses the nine x-es:

template=$(<file)
while read number; do
    printf '%s\n' "${template//xxxxxxxxx/$number}"
done <numbers
0
5

Apparently your template has only one line...

If this is true we can

for a in $(< list)
do
    printf "09Y20171031    13415520171031   134155AP|AO|AB%s|AC|CH|BIN|\n" $a
done
1
  • 1
    why for a in $(< list) instead of while read a; ...; done < list ?
    – wjandrea
    Oct 31, 2017 at 21:14
3

Another awk approach:

awk 'NR==FNR{ a[++c]=$1; next }
     { for(i in a) { r=$0; sub(/x{9}/,a[i],r); print r } }' file1 file2
  • a[++c]=$1 - capturing numbers from the file1 into array a

  • for(i in a) - iterating through nine-digit numbers

  • sub(/x{9}/,a[i],r) - substitute the crucial sequence xxxxxxxxx with next number contained in a[i]


The output:

09Y20171031    13415520171031    134155AP|AO|AB550411876|AC|CH|BIN|
09Y20171031    13415520171031    134155AP|AO|AB550425175|AC|CH|BIN|
09Y20171031    13415520171031    134155AP|AO|AB550426504|AC|CH|BIN|
4
  • 1
    I would have done it the other way around : awk 'NR==1{split($0,z,/x{9}/);next}{printf ("%s%s%s\n", z[1], $0, z[2])}' file2 file1 Oct 31, 2017 at 14:58
  • 1
    @don_crissti, good trick, you may post it as an answer with explanations to present an alternative way for the community Oct 31, 2017 at 15:12
  • There are already too many answers. If you like it then you may include it in your answer and I'll delete my comment here. Oct 31, 2017 at 15:13
  • 1
    @don_crissti, I don't want to assume your original idea. Post it with explanations and you'll get my upvote Oct 31, 2017 at 15:17
3

with list being number list and tmpl being template file

for y in $(< list )
do
   sed -r -s s/[x]{9}/$y/ < tmpl
done

give

09Y20171031    13415520171031    134155AP|AO|AB550411876|AC|CH|BIN|
09Y20171031    13415520171031    134155AP|AO|AB550425175|AC|CH|BIN|
09Y20171031    13415520171031    134155AP|AO|AB550426504|AC|CH|BIN|

where

  • for y in $(< list ) read each line and put value into variable y
  • sed -r use gnu extension
  • -e s/[x]{9}/../ replace 9 occurence of character x ...
  • ../$y/ ... by content of var y

you will have to redirect for loop to a file

done > my_file
1
  • This works but will use n sed invocations where n=$(wc -l <list) so for a file with one million lines you're calling sed one million times. Oct 31, 2017 at 14:36
1

Perl can't be missing here.

p=$(< pat) perl -nlE'$l = $ENV{p}; $n=$_; $l =~ s/x{9}/$n/; say $l if /./' nums

pat is the pattern file name

p is an environment variable carrying the pattern

n is a Perl variable containing the number in each iteration

l contains line (first the pattern for each line, then after the substitution the actual line to print)

nums is the numbers file name

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .