1

My awk script reads records from file1, finds the same records in file2 and substitutes alternate positions (of the record) with a defined symbol in that. But few of the values are not getting substituted as desired. Only the first instance of the word is getting substituted, eg

  • TYTYTY into T##Y##
  • TYTYTYGGHG into T##Y##GGHG
  • LEFT into L##T
  • LEFT NAME into L##T NAME

Expected output is given below.

My code-

awk ' FNR==NR
{
if ($0 in word)
next
word[$0]=$0
for (i=1;i<=NF;i++)
 {
   old=$i
   new=""
   while (old) {
         len=length(old)
         new=new substr(old,1,1) substr("##",1,len-1)
         old=substr(old,4)
       }
      id=index(word[$0],$i)
      word[$0]=substr(word[$0],1,id-1) new substr(word[$0],id+length($i))
  }
 next
}

{
 for (i in word)
 {
  regex = "\\<" i "\\>"
  gsub(regex, ext[i])
  #id=index($0,i)
  #while(id>0) {
  #$0=substr($0,1,id-1) word[i] substr($0,id+length(word[i])) 
  #id=index($0,i)
 }
}
print 
}' records test.html > output.html
$ cat records

LEFT NAME
LEFT NAME 2
LEFT 
LEFT 123
TYTYTYGGHG
TYTYTY    
AB 8263
AB SCENARIO DEBUG
AB 8263 SCENARIO DEBUG

$ cat test.html

<html>
<body>
<hr><br><>span class="table">TabA</span><table>
<tr class="column">
 <td>LEFT NAME</td>
 <td>LEFT</td>
 <td></td>
 <td>LEFT NAME 2</td>
 <td>LEFT 123</td>
 <td>TYTYTYGGHG</td>
 <td></td>
 <td>TYTYTY</td>
</tr>
<tr class="data">
<td></td>
<td></td>
<td></td>
<td>AB 8263</td>
<td></td>
<td></td>
<td>AB SCENARIO DEBUG</td>
<td>AB 8263 SCENARIO DEBUG</td>
</tr>
</table>
</body>
</html>

desired op -

<html>
<body>
<hr><br><>span class="table">TabA</span><table>
<tr class="column">
 <td>L##T N##E</td>
 <td>L##T</td>
 <td></td>
 <td>L##T N##E 2</td>
 <td>L##T 1##</td>
 <td>T##Y##G##G</td>
 <td></td>
 <td>T##Y##</td>
</tr>
<tr class="data">
<td></td>
<td></td>
<td></td>
<td>A# 8##3</td>
<td></td>
<td></td>
<td>A# S##N##I# D##U#</td>
<td>A# 8##3 S##N##I# D##U#</td>
</tr>
</table>
</body>
</html>

Current output -

<html>
<body>
<hr><br><>span class="table">TabA</span><table>
<tr class="column">
 <td>L##T NAME</td>
 <td>L##T</td>
 <td></td>
 <td>L##T NAME 2</td>
 <td>L##T 123</td>
 <td>T##Y##GGHG</td>
 <td></td>
 <td>T##Y##</td>
</tr>
<tr class="data">
<td></td>
<td></td>
<td></td>
<td>A# 8##3</td>
<td></td>
<td></td>
<td>A# S##N##I# D##U#</td>
<td>A# 8##3 SCENARIO DEBUG</td>
</tr>
</table>
</body>
</html>

1 Answer 1

0

I'm surprised this isn't a dupe, although https://stackoverflow.com/questions/34375020/why-the-output-of-array-using-awk-is-not-in-right-order comes pretty close.

TLDR: awk arrays are not ordered (in general)

When you iterate over an array in awk using e.g. for (i in word) the order that the 'index' values (aka subscripts or keys) are returned is unspecified by the standard; traditionally these arrrays are implemented as a hashtable and iterated in the order of that hashtable, which is some hashcode modulo some value, possibly modified or re-hashed in case of collision (so-called 'closed hashing'). This is NOT the same as the order the entries/keys were inserted, and usually it is deterministic but complicated enough to appear random from the point of view of a human.

Thus in your example, it is quite possible, depending on what awk you use and your exact data, for the for loop to process the entry for LEFT before it processes LEFT NAME; as a result a line containing LEFT NAME matches LEFT and is changed to L##T NAME, and subsequently when the loop tries LEFT NAME that does NOT match so the line isn't further changed. Similarly TYTYTY is processed first after which TYTYTYGGHG doesn't match, and AB 8263 is processed first after which AB 8263 SCENARIO DEBUG doesn't match. Running on a different awk or with different file1 data is very likely to produce different orders and thus different results; running in a different environment may do so.

Also in your FNR==NR (i.e. file1) branch, new-new substr(old,1,1) substr("##",1,len-1) needs to be = not -. Personally I'd find it bit more natural to make that sub-loop

    old=$i; new=""; for(c=1; c<=length(old); c++) new = new (c%3==1? substr(old,c,1): "#")

although the difference is minor. Although you should be aware your sub-loop, if any word in file1 consists exactly of one or more 0 digits, optionally preceded by + or - and optionally followed by e and any digits, will change that to an empty string, not a same-length masked version.

2
  • Thanks for the explanation Dave. If not awk, what would be the better way to achieve the desired result in this case? May 12 at 5:34
  • I tried using word boundary to substitute the values using gsub (updated code) still its not producing the desired op. May 12 at 9:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.