1

I'm looking to substitute text inside a file after a certain pattern.

For example:

The content of example.txt is

Something==x.y.z

I'd like to change it to

Something>=x.y.z,<x.y.z+1.0

I know I can use sed -i 's/==/>=/g' example.txt to change the == but I do not know add <x.y.z+1.0 after a certain pattern.

(Please note that x.y.z is a random number)

EDIT: it is for Python packages. Examples

argcomplete==1.12.3
youtube-dl==2021.6.6
systemd-python==234

would become

argcomplete>=1.12.3,<1.12.3+1.0
youtube-dl>=2021.6.6,<2021.6.6+1.0
systemd-python>=234,<234+1.0
0

1 Answer 1

3

The following sed command assumes that there is exactly one == in the line and extracts the parts before and after it as groups 1 and 2 which can be used in the substitution.

sed 's/\(.*\)==\(.*\)/\1>=\2,<\2+1.0/'

With the input

Something==x.y.z
argcomplete==1.12.3
youtube-dl==2021.6.6
systemd-python==234

the output is

Something>=x.y.z,<x.y.z+1.0
argcomplete>=1.12.3,<1.12.3+1.0
youtube-dl>=2021.6.6,<2021.6.6+1.0
systemd-python>=234,<234+1.0

To edit the file in-place, add option -i and the name of the input file:

sed -i 's/\(.*\)==\(.*\)/\1>=\2,<\2+1.0/' example.txt 

Explanation:

Pattern:

  • . = any character
  • * = the preceding pattern repeated from 0 to any number
  • --> .* = any number of any characters
  • \(...\) = capture the text matching the enclosed pattern
  • == = literal text
  • \(.*\)==\(.*\) = any text captured as group 1 followed by == and any text captured as group 2

Replacement:

  • \1, \2 = text from capture group 1 or 2
  • other parts are literal text here
  • \1>=\2,<\2+1.0 = group 1 >= group2 ,< group2 +1.0

As mentioned in they's comment, the first pattern before the literal == can be omitted, resulting in

sed 's/==\(.*\)/>=\1,<\1+1.0/'

The explanation is similar with the difference that sed will only modify the matching part of the line. So the part before == will be preserved, and only one capture group for the part after the == is necessary.

A difference in the behavior of the two patterns is that .*==... will match the last == while ==... will match the first one because the .* part matches the longest possible text that is followed by ==.

1
  • 1
    Somewhat shorter: sed 's/==\(.*\)/>=\1,<\1+1.0/', unless you explicitly want to match the last == on the line.
    – Kusalananda
    Commented Jan 25, 2022 at 9:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .