-1

I built a script (bash) to replace a figure in a specific position on a .txt file.

This is the file, data.txt:

Date; Buy; Sell; Coupon; Fee
21/6/2019;0.0000000000;0.0000000000;0.0000000000

I want to replace the 12th character.

Find below the part of the code that changes the position if the 12th position is equal to "0" change the figure to "59"

sed 's/^\(.\{11\}\)0/\159/' data.txt

All good so far, the problem is when I want to make the changes only on the last row (#34)image a file (data.txt) as:

Date; Buy; Sell; Coupon; Fee
26/4/2006;0.0000000000;-200000000.0;0.0000000000
4/5/2006;0.0000000000;-100000000.0;0.0000000000
4/5/2006;0.0000000000;-300000000.0;0.0000000000
1/12/2006;0.0000000000;0.0000000000;-30000000.00
5/12/2006;0.0000000000;-250000000.0;0.0000000000
8/12/2006;0.0000000000;-250000000.0;0.0000000000
19/12/2006;0.0000000000;-650000000.0;0.000000000
18/1/2007;0.0000000000;-250000000.0;0.0000000000
1/2/2007;0.0000000000;-250000000.0;0.0000000000
2/2/2007;0.0000000000;-720000000.0;0.0000000000
28/3/2007;0.0000000000;-200000000.0;0.0000000000
28/3/2007;0.0000000000;-400000000.0;0.0000000000
3/5/2007;0.0000000000;-250000000.0;0.0000000000
3/5/2007;0.0000000000;-750000000.0;0.0000000000
3/5/2007;0.0000000000;-250000000.0;0.0000000000
5/6/2007;0.0000000000;-500000000.0;0.0000000000
3/7/2007;0.0000000000;-300000000.0;0.0000000000
3/12/2007;0.0000000000;0.0000000000;-281000000.0
1/12/2008;0.0000000000;0.0000000000;-281000000.0
1/12/2009;0.0000000000;0.0000000000;-281000000.0
1/12/2010;0.0000000000;0.0000000000;-281000000.0
5/4/2011;525000000.00;0.0000000000;0.0000000000
1/12/2011;0.0000000000;0.0000000000;-254750000.0
2/11/2012;1348000000.0;0.0000000000;0.0000000000
2/11/2012;840000000.00;0.0000000000;0.0000000000
3/12/2012;0.0000000000;0.0000000000;-145350000.0
2/12/2013;0.0000000000;0.0000000000;-145350000.0
1/12/2014;0.0000000000;0.0000000000;-145350000.0
1/12/2015;0.0000000000;0.0000000000;-145350000.0
1/12/2016;0.0000000000;0.0000000000;-145350000.0
1/12/2017;0.0000000000;0.0000000000;-145350000.0
3/12/2018;0.0000000000;0.0000000000;-145350000.0
21/6/2019;0.0000000000;0.0000000000;0.0000000000

I used the following:

#!/bin/bash

i=1
while read line;do
if((i==34));then
sed 's/^\(.\{11\}\)0/\159/' data.txt
fi
((i++))

Seems that there is a problem with the condition, the script runs a never stop, no output produced is like a loop without end.

Casimir et Hippolyte
  • 88,009
  • 5
  • 94
  • 125
Lorenzo Castagno
  • 528
  • 1
  • 10
  • 27
  • Please wrap your samples/codes in CODE TAGS as per forum rules, edit your post and let us know then. – RavinderSingh13 Jul 08 '19 at 12:53
  • Subbing the twelfth character will produce inconsistent results because your first field (date) is variable-length. Please edit your question to include your exact desired output. – vintnes Jul 08 '19 at 12:58
  • Hello community, I edited my post now. Thanks for your help. – Lorenzo Castagno Jul 08 '19 at 13:01
  • 1
    @LorenzoCastagno Please edit your question to include exact desired output. – vintnes Jul 08 '19 at 13:09
  • Possible duplicate of [sed + remove string only in the last line of the file](https://stackoverflow.com/questions/3576139/sed-remove-string-only-in-the-last-line-of-the-file) – Corentin Limier Jul 08 '19 at 13:18
  • Hi @vintnes My desired output: 21/6/2019;59;0.0000000000;0.0000000000 – Lorenzo Castagno Jul 08 '19 at 15:30
  • That does not just `replace the 12th character` as you said you wanted. It replaces a whole `0.000...` number with `59` and deletes all lines up until that last line. – Ed Morton Jul 08 '19 at 15:35

2 Answers2

1

Try this:

sed '$s/^\([^;]\+;\)0\(.*\)/\159\2/' input

The address $ tells sed to work only on the last line in the file. Instead of replacing the 12th character it is probably wise to replace the character after the first ;.

It makes absolutely no sense to split the lines for sed with a bash. It is one of sed's core features to loop over lines.

ceving
  • 21,900
  • 13
  • 104
  • 178
  • 2
    Capturing the end of the line (after the 0) is useless. – Casimir et Hippolyte Jul 08 '19 at 13:21
  • @CasimiretHippolyte Why? He says: "I want to replace the 12th character." – ceving Jul 08 '19 at 13:57
  • 1
    You should mention that requires GNU sed for `\+` but if you have to use GNU sed then you could use EREs like `sed -E '$s/^([^;]+;)0(.*)/\159\2/'` to avoid so much escaping and then that'd work in OSX/BSD sed too. If you wanted a POSIX version it'd be `sed '$s/^\([^;]\{1,\};\)0\(.*\)/\159\2/'`. @CasimiretHippolyte is right though, you don't need the 2nd capture group, all you need is `sed -E '$s/^([^;]+;)0/\159/'` – Ed Morton Jul 08 '19 at 15:24
  • @CasimiretHippolyte why is useless? I want to replace the 12th character in the last row! – Lorenzo Castagno Jul 08 '19 at 15:29
  • 1
    @LorenzoCastagno run `echo 'abc' | sed 's/b/X/'` and it'll output `aXc`. Now run `echo 'abc' | sed 's/b\(.*\)/X\1/'` and it'll also output `aXc`. Now ask yourself - what benefit did writing `\(.*\)` and `\1` in the 2nd script to capture what's left on the line after the part you're interested in give you? The answer is none, just like with the script in this answer. – Ed Morton Jul 08 '19 at 15:31
  • 1
    Indeed `sed '$s/^\([^;]*;\)0/\159/'` does exactly the same thing (without to have to describe the end of the line). – Casimir et Hippolyte Jul 08 '19 at 16:17
0

The following works with gawk, mawk, original-awk, and busybox:

awk 'END {if(int($2)==0) $2=59; print}' {O,}FS=\; data.txt

Outputs:

21/6/2019;59;0.0000000000;0.0000000000

On the last record (line), awk checks the integer value of the second semicolon ; delimited field, changing it to 59 if it's equal to zero.

vintnes
  • 2,014
  • 7
  • 16
  • That's relying on undefined behavior since the value of `$0` in the `END` section is undefined by POSIX. In some awks it'll be the value of the last $0 read, in others it'll be null and in others it could be/do anything else. It'll do what you want in GNU awk, idk which others. – Ed Morton Jul 08 '19 at 19:09
  • @EdMorton I tested against `mawk`, `gawk`, and `original-awk`. It feels preposterous to discard a working solution on the basis of a weak standard. I would very much like to know if you find a version that doesn't do this. – vintnes Jul 08 '19 at 20:01
  • 1
    HP-UX awk is one (see https://stackoverflow.com/q/29970249/1745001). See also the discussion on this at https://www.gnu.org/software/gawk/manual/gawk.html#I_002fO-And-BEGIN_002fEND - `all of BWK awk, mawk, and gawk preserve the value of $0 for use in END rules. Be aware, however, that some other implementations and many older versions of Unix awk do not.`. I'm not suggesting you discard the solution, just mention that it'll only behave as expected with an awk that supports $0, etc. as desired in the END section. – Ed Morton Jul 08 '19 at 20:05
  • 1
    @EdMorton valuable information; will keep this in mind for the older NACs I have to deal with. – vintnes Jul 08 '19 at 20:10
  • idk what a `NAC` is but dont assume this is only a problem with older UNIX installations or awk versions, as the awk manual says "**some other implementations** and many older versions of Unix awk do not." - it's not just "many older vesions". Just remember to say "works if..." if/when you're counting on this and then it's all good. – Ed Morton Jul 08 '19 at 20:12