Skip to main content
The 2024 Developer Survey results are live! See the results
added 36 characters in body
Source Link
user8017719
user8017719
$ echo 'Num123that456are7899900contained0018166intext' |\
>   | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp' 

123 456 7899900 0018166
$ echo 'Number 123 inside text' \
   | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'
123
123
 
s/[^0-9]*                           # several non-digits
         \([0-9]\{1,\}\)            # followed by one or more digits
                        [^0-9]*     # and followed by more non-digits.
                               /\1/ # gets replaced only by the digits.
$ echo 'Number 123 in text' \
   | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/' 

123
$ echo 'Number xxx in text' \
   | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
$ echo 'N 123 in 456 text' \
  | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp' 

123 456
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" \
   | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp' 

123 456 7899900 0018166
$ echo 'Num123that456are7899900contained0018166intext' |
> sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
$ echo 'Number 123 inside text' | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'
123

s/[^0-9]*                           # several non-digits
         \([0-9]\{1,\}\)            # followed by one or more digits
                        [^0-9]*     # and followed by more non-digits.
                               /\1/ # gets replaced only by the digits.
$ echo 'Number 123 in text' | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/'
123
$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
$ echo 'N 123 in 456 text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp'
123 456
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
$ echo 'Num123that456are7899900contained0018166intext' \
   | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp' 

123 456 7899900 0018166
$ echo 'Number 123 inside text' \
   | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'

123
 
s/[^0-9]*                           # several non-digits
         \([0-9]\{1,\}\)            # followed by one or more digits
                        [^0-9]*     # and followed by more non-digits.
                               /\1/ # gets replaced only by the digits.
$ echo 'Number 123 in text' \
   | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/' 

123
$ echo 'Number xxx in text' \
   | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
$ echo 'N 123 in 456 text' \
  | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp' 

123 456
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" \
   | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp' 

123 456 7899900 0018166
Source Link
user8017719
user8017719

run(s) of digits

This answer works with any count of digit groups. Example:

$ echo 'Num123that456are7899900contained0018166intext' |
> sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166

Expanded answer.

Is there any way to tell sed to output only captured groups?

Yes. replace all text by the capture group:

$ echo 'Number 123 inside text' | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'
123

s/[^0-9]*                           # several non-digits
         \([0-9]\{1,\}\)            # followed by one or more digits
                        [^0-9]*     # and followed by more non-digits.
                               /\1/ # gets replaced only by the digits.

Or with extended syntax (less backquotes and allow the use of +):

$ echo 'Number 123 in text' | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/'
123

To avoid printing the original text when there is no number, use:

$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
  • (-n) Do not print the input by default.
  • (/p) print only if a replacement was done.

And to match several numbers (and also print them):

$ echo 'N 123 in 456 text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp'
123 456

That works for any count of digit runs:

$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166

Which is very similar to the grep command:

$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | grep -Po '\d+'
123
456
7899900
0018166

About \d

and pattern: /([\d]+)/

Sed does not recognize the '\d' (shortcut) syntax. The ascii equivalent used above [0-9] is not exactly equivalent. The only alternative solution is to use a character class: '[[:digit:]]`.

The selected answer use such "character classes" to build a solution:

$ str='This is a sample 123 text and some 987 numbers'
$ echo "$str" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'

That solution only works for (exactly) two runs of digits.

Of course, as the answer is being executed inside the shell, we can define a couple of variables to make such answer shorter:

$ str='This is a sample 123 text and some 987 numbers'
$ d=[[:digit:]]     D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D+($d+)$D*/\1 \2/p"

But, as has been already explained, using a s/…/…/gp command is better:

$ str='This is 75577 a sam33ple 123 text and some 987 numbers'
$ d=[[:digit:]]     D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D*/\1 /gp"
75577 33 123 987

That will cover both repeated runs of digits and writing a short(er) command.