Home > Back-end >  How to capture one digit with sed and replace the other?
How to capture one digit with sed and replace the other?

Time:10-08

In a text file test.txt are many lines of text, of which I want to extract a single line matching:

blabla 28.40.00 blabla

I would like to replace the first digit of the middle number (in this case 4) by three. That is, no matter what the middle number is (40, 41, 52, 63 etc), I would like it to be replaced by a number starting with 3 (40 becomes 30, 41 becomes 31, 52 becomes 32, 63 becomes 33 etc).

The following line matches the middle number and replaces it with the alphabet a:

cat test.txt |awk '/blabla/'|sed -E s_[[:digit:]][[:digit:]]_3_2

output: blabla 28.3.00 blabla

But when I want to replace only the first digit, sed doesn't work:

cat test.txt |awk '/blabla/'|sed -E s_[[:digit:]]\([[:digit:]]\)1_3\1_2

output: blabla 28.40.00 blabla

What am I doing wrong?

CodePudding user response:

To always replace the third digit in the line:

$ echo 'blabla 28.40.00 blabla' | sed 's/[0-9]/3/3'
blabla 28.30.00 blabla

If the number before the . can have more than 2 digits, here's one workaround:

$ echo 'blabla 528.40.00 blabla' | sed 's/\.[0-9]/.3/'
blabla 528.30.00 blabla


In your second attempt, you should quote the command and use unescaped parenthesis. You also seem to have an extra 1 after the capture group.

$ echo 'blabla 28.40.00 blabla' | sed -E 's_[[:digit:]]([[:digit:]])_3\1_2'
blabla 28.30.00 blabla

Also, you don't need awk in this case. You can add filter in sed as well:

$ echo 'blabla 28.40.00 blabla' | sed -E '/blabla/ s_[[:digit:]]([[:digit:]])_3\1_2'
blabla 28.30.00 blabla

CodePudding user response:

This will match the two digits between '.' and replace the first digit with a 3.

echo blabla 28.40.00 blabla | sed -n '/blabla/s/\.[0-9]\([0-9]\)\./.3\1./p'

returns

blabla 28.30.00 blabla

CodePudding user response:

I think the main problem is the stray 1 in the expression (right before _3). Try:

sed -E 's@[[:digit:]]([[:digit:]])@3\1@2'

But sed seems awkward for this. What do you want to do with 3 digit numbers? Assuming you want to change 129 to 39, you might prefer the less awkward:

echo 'blabla 28.129.00 blabla' | awk '/blabla/{$2 = 30   $2 % 10}1' FS=. OFS=.

(This will change whitespace in the line and you need to worry about how to deal with lines in which "blabla" contains a ., but the question doesn't really give enough information to know how to handle those situations. A similar issue occurs with sed solutions if blahblah contains pairs of digits.)

CodePudding user response:

First of all cat is unnecessary as awk/sed can directly operate on a file.

Secondly since you are already using awk, you can do this job in awk itself without doing awk | sed.

Consider this gnu-awk solution:

awk '/blabla/ {
   $0 = gensub(/([^0-9]*[0-9] \.)[0-9]/, "\\13", "1")
} 1' test.txt

blabla 28.30.00 blabla

This gensub function matches 0 or more non-digits followed 1 digits and following dot in a capture group. Finally we match a digit that we want to replace. In the replacement we substitute original captured value back using \\1 followed by the new digit we want i.e. 3.

CodePudding user response:

As you have many lines and you want to replace the first digit of the middle number, you can match that format of numbers with 2 dots in between.

Use the 2 capture groups in the replacement \13\2.

sed -E 's/([0-9]\.)[0-9]([0-9]*\.[0-9])/\13\2/' test.txt

The pattern matches

  • ^ Start of string
  • ([0-9]\.) Capture group 1, match a single digit and a dot
  • [0-9] Match the single digit (to replace)
  • ([0-9]*\.[0-9]) Capture group 2, match optional digits, then . and a single digit

Output

blabla 28.30.00 blabla
  • Related