6

My file has the content below:

rat|minty|ruhul|balaji|rat_123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat1_123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat2_123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat

I need to replace _ with | but only on the 5th field only.

Expected output:

rat|minty|ruhul|balaji|rat|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat1|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat2|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
20

With awk, use gsub() on the 5th field:

$ awk 'BEGIN{FS=OFS="|"} {gsub("_",FS,$5)}1' file
rat|minty|ruhul|balaji|rat|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat1|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat2|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat

Explanation

  • BEGIN{FS=OFS="|"}
    Set the field separator to |. This way, we can address $5 as the 5th field and so on.
  • {gsub("_",FS,$5)}
    Replace all the _ in the 5th field with FS. That is, with |.
  • 1
    Trigger awk's default action: print the current (modified) record.
  • 5
    Golf! gsub("_",FS,$5)+1. – Kaz Sep 28 '16 at 15:48
8

With sed provided the 5th field has only one _ to be replaced

$ sed -E 's/^(([^|]+\|){4}[^_]+)_/\1|/' ip.txt 
rat|minty|ruhul|balaji|rat|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat1|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat2|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat

Solution with perl (similar to awk one) if all _ in 5th field are to be replaced:

$ perl -F'\|' -lane '$F[4] =~ tr/_/|/; print join "|",@F' ip.txt 
rat|minty|ruhul|balaji|rat|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat1|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat2|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
  • If using Gnu sed, you could have something like s///5 which would only replace the 5th match. – someonewithpc Sep 28 '16 at 19:14
  • 1
    @someonewithpc, I don't think that is how it works.. it would replace 5th occurrence anywhere in the line... in this case, even sed 's/_/|/' would work, but in real data there may be _ spread anywhere – Sundeep Sep 29 '16 at 1:52
  • It wouldn't necessarily do that. You could make it so it matches all fields, but only replaces the fifth. – someonewithpc Sep 29 '16 at 6:08
  • @someonewithpc explain with this example? echo 'a_c_f_d_e_f_d|2|3|4|5_6|7' | sed 's/_/|/5' – Sundeep Sep 29 '16 at 6:15
2

With perl:

$ perl -F'\|' -ne '$F[4]=~s/_/|/; print join "|", @F' file
rat|minty|ruhul|balaji|rat|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat1|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat2|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat

The -a makes perl act like awk, splitting each input line on the character given by -F into fields saved in the array @F. Then, we substitute _ with | on the 5th field (arrays start at 0, so $F[4] is the 5th field) and then print the array joined by |.

You could also set the array separator to | and print "@F" which does the same thing but more concisely:

$ perl -F'\|' -ne '$"="|"; $F[4]=~s/_/|/; print "@F"' file
rat|minty|ruhul|balaji|rat|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat1|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat2|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat

Of course, in your specific example, the _ in the 5th field is also the first on in the line, so if your file is actually like that, it is enough to do:

$ perl -pe 's/_/|/' file 
rat|minty|ruhul|balaji|rat|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat1|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat2|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat

Or

$ sed 's/_/|/' file 
rat|minty|ruhul|balaji|rat|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat1|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat2|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat

You can use the same approach with awk:

$ awk -F'|' 'sub("_","|")' file 
rat|minty|ruhul|balaji|rat|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat1|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat2|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat

Or, to specify only the 5th field and replace all occurrences of _ in the 5th field, a shorter version of @fedorqui's answer:

$ awk -F'|' -vOFS='|' 'gsub("_","|",$5)' file 
rat|minty|ruhul|balaji|rat|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat1|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat
rat|minty|ruhul|balaji|rat2|123|decode|rat_123|abc|def|ghi|jkl|rat|cde|ind|rat

Note that these two awk solutions will only print on lines where the substitution was successful. If you can have lines that don't match the pattern (no _ in the 5th field), use @fedorqui's approach or one of the perl ones instead.

  • Interesting! Regarding your awk approaches: using sub() will make awk replace just once, so rat_1_2_3 will become rat|1_2_3. OP did not address this edge case, but it is worth noting (and the reason I used gsub() instead). Regarding printing the lines that have matched the pattern or not, Kaz suggested a nice trick: sub()+1 so that it will always evaluate to True and hence print the line. – fedorqui Sep 29 '16 at 8:53
  • 1
    @fedorqui you're quite right about gsub, of course, but since you've already mentioned it, I thought I'd go for sub. Note that I only used it for the example which assumes that we only want to replace the 1st _ on the line. And yeah, Kaz's golf is pretty neat! – terdon Sep 29 '16 at 9:02
  • Note that I only used it for the example which assumes that we only want to replace the 1st _ on the line. probably good to add that in answer... and specify that the last awk solution will replace all occurrences – Sundeep Sep 29 '16 at 10:18
  • 1
    @Sundeep OK, I made that explicit in the last example. I already mention that some of the others only remove the 1st _: Of course, in your specific example, the _ in the 5th field is also the first on in the line, so if your file is actually like that, it is enough to do: – terdon Sep 29 '16 at 10:23

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.