How to capture one digit with sed and replace the other?

218 Views Asked by At

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 number 3:

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?

7

There are 7 best solutions below

0
Sundeep On BEST ANSWER

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
0
toppk On

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
0
William Pursell On

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.)

0
anubhava On

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.

0
The fourth bird On

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
0
petrus4 On
#!/bin/sh

f1=$(echo "blabla 28.40.00 blabla" | cut -d' ' -f1)
f2=$(echo "blabla 28.40.00 blabla" | cut -d' ' -f2)
f3=$(echo "blabla 28.40.00 blabla" | cut -d' ' -f3)
mf1=$(echo "${f2}" | cut -d'.' -f1) 
mf2=$(echo "${f2}" | cut -d'.' -f2) 
mf3=$(echo "${f2}" | cut -d'.' -f3)
fixed=$(echo "${mf2}" | sed s'@.@3@')

echo "${f1} ${mf1}.${fixed}.${mf3} ${f3}" > output
0
potong On

This might work for you (GNU sed):

sed -E '/blabla/s/[0-9]([0-9]+)/3\1/2' file

Focus on lines containing blabla.

Replace the first digit of two or more digits with 3 in the second occurrence of two or more digits.