I couldn't quite get this in grep or sed, but Perl came to the rescue, and could be quite extensible.:
$ perl -e 'while($stdin = <>) {@matches = $stdin =~ /(tech)[^0-9]*([0-9x][0-9x.]*)/g; print "@matches\n" if @matches}' < INPUTFILE
# Assign standard input ( in Perl) to a variable to use it for matching
# and to detect when the input is empty (i.e. EOF)
while ($stdin = ) {
# Assign the matches from $stdin to array @matches
# /(tech)[^0-9]*([0-9x][0-9x.]*)/g;
# (tech): match the term 'tech', () puts it into @matches
# [^0-9]*: match any non-number to discard, since .* was too greedy
# and matched up to the last number
# ([0-9x][0-9x.]*): match the pattern 0, 0.x, 0.9.x, etc., () puts it into @matches
@matches = $stdin =~ /(tech)[^0-9]*([0-9x][0-9x.]*)/g;
# Display the array from above if @matches is not empty
print "@matches\n" if @matches
}
# Assign standard input (<> in Perl) to a variable to use it for matching and to detect when the input is empty (i.e. EOF) while ($stdin = <>) { # Assign the matches from $stdin to array @matches # /(tech)[^0-9]*([0-9x][0-9x.]*)/g; # (tech): match the term 'tech', () puts it into @matches # [^0-9]*: match any non-number to discard, since .* was too greedy and matched up to the end number # ([0-9x][0-9x.]*): match the pattern 0, 0.x, 0.9.x, etc., () puts it into @matches @matches = $stdin =~ /(tech)[^0-9]*([0-9x][0-9x.]*)/g; # Display the array from above if @matches is not empty print "@matches\n" if @matches } When this is applied to the file, the output is:
tech 1.2
tech 1
tech 0.1
tech 10.1.3
tech 7.5
tech 8.0
tech 1.3.x
tech 5.x
tech 2.0.4x