Sed / awk or another: one liner to increase the number by 1, keeping space characters

EDIT . I donโ€™t know in advance on which โ€œcolumnโ€ my numbers will be, and I would like to have a single line. Obviously sed does not do arithmetic, so maybe an awk single line solution?

I have a line: (note the interval)

eh oh 37 

and I want him to become:

 eh oh 36 

(so I want to keep the interval)

With awk, I cannot find how to do this as long as I have:

 echo "eh oh 37" | awk '$3>=0&&$3<=99 {$3--} {print}' 

But it gives:

 eh oh 36 

(space characters where they are lost because the field separator is equal to '')

Is there a way to ask awk something like "print the output using the same field delimiters as the input?"

Then I tried something else, using the awk sub (.., ..) method:

 ' sub(/[0-9][0-9]/, ...) {print}' 

but there is no cigar yet: I do not know how to refer to regexp and do arithmetic on it in the second argument (which I left with "...").

Then I tried with sed, but got stuck after that:

 echo "eh oh 37" | sed -e 's/\([0-9][0-9]\)/.../' 

Can I do arithmetic from sed using the link to the corresponding numbers, and so that the output does not change the number of space characters?

Please note that this is related to my question regarding Emacs and how to apply it to some (large) Emacs region (using the replacement area using the Emacs-on-region shell-shell), but this is not an identical question: this one is about how " keep spaces "when working with awk / sed / etc.

+4
source share
5 answers

Below is the ghostdog74 answer option , which does not require the number to be anchored at the end of the line. This is achieved with match rather than relying on a number in a specific position.

This will replace the first number with its value minus one:

 $ echo "eh oh 37 aaa 22 bb" | awk '{n = substr($0, match($0, /[0-9]+/), RLENGTH) - 1; sub(/[0-9]+/, n); print }' eh oh 36 aaa 22 bb 

Using gsub instead of sub would replace both "37" and "22" with "36". If there is only one number on the line, it does not matter what you use. This way it will handle numbers with a trailing space plus other non-numeric characters that may be there (after some space).

If you have gawk , you can use gensub like this to select an arbitrary number inside the string (just set which value):

 $ echo "eh oh 37 aaa 22 bb 19" | awk -v which=2 'BEGIN { regex = "([0-9]+)\\>[^0-9]*"; for (i = 1; i < which; i++) {regex = regex"([0-9]+)\\>[^0-9]*"}} { match($0, regex, a); n = a[which] - 1; # do the math print gensub(/[0-9]+/, n, which) }' eh oh 37 aaa 21 bb 19 

The second ( which=2 ) number went from 22 to 21. And the built-in spaces are saved.

It broke into several lines to make it easier to read, but it was copy / passive.

+5
source
 $ echo "eh oh 37" | awk '{n=$NF+1; gsub(/[0-9]+$/,n) }1' eh oh 38 

or

 $ echo "eh oh 37" | awk '{n=$NF+1; gsub(/..$/,n) }1' eh oh 38 
+3
source

sort of

 number=`echo "eh oh 37" | grep -o '[0-9]*'` sed 's/$number/`expr $number + 1`/' 
+2
source

What about:

 $ echo "eh oh 37" | awk -F'[ \t]' '{$NF = $NF - 1;} 1' eh oh 36 
+1
source

The solution will not save the number of decimal places, so if the number is 10, then the result is 9, even if you would like to have 09.

I did not write the shortest possible code, it should remain readable

Here I create a printf pattern using RLENGTH, so it becomes% 02d (2 is the length of the matching pattern)

 $ echo "eh oh 10 aaa 22 bb" | awk '{n = substr($0, match($0, /[0-9]+/), RLENGTH)-1 ; nn=sprintf("%0" RLENGTH "d", n) sub(/[0-9]+/, nn); print }' eh oh 09 aaa 22 bb 
0
source

Source: https://habr.com/ru/post/1307604/


All Articles