Search code examples
bashsedregexp-replace

sed replace string with pipe and stars


I have the following string:

|**barak**.version|2001.0132012031539|

in file text.txt. I would like to replace it with the following:

|**barak**.version|2001.01.2012031541|

So I run:

sed -i "s/\|\*\*$module\*\*.version\|2001.0132012031539/|**$module**.version|$version/" text.txt

but the result is a duplicate instead of replacing:

|**barak**.version|2001.01.2012031541|**barak**.version|2001.0132012031539|

What am I doing wrong? Here is the value for module and version:

$ echo $module
barak
$ echo $version
2001.01.2012031541

Solution

  • Assumptions:

    • lines of interest start and end with a pipe (|) and have one more pipe somewhere in the middle of the data
    • search is based solely on the value of ${module} existing between the 1st/2nd pipes in the data
    • we don't know what else may be between the 1st/2nd pipes
    • the version number is the only thing between the 2nd/3rd pipes
    • we don't know the version number that we'll be replacing

    Sample data:

    $ module='barak'
    $ version='2001.01.2012031541'
    
    $ cat text.txt
     **barak**.version|2001.0132012031539|     <<<=== leave this one alone
    |**apple**.version|2001.0132012031539|
    |**barak**.version|2001.0132012031539|     <<<=== replace this one
    |**chuck**.version|2001.0132012031539|
    |**barak**.peanuts|2001.0132012031539|     <<<=== replace this one
    

    One sed solution with -Extended regex support enabled and making use of a capture group:

    $ sed -E "s/^(\|[^|]*${module}[^|]*).*/\1|${version}|/" text.txt
    

    Where:

    • \| - first occurrence (escaped pipe) tells sed we're dealing with a literal pipe; follow-on pipes will be treated as literal strings
    • ^(\|[^|]*${module}[^|]*) - first capture group that starts at the beginning of the line, starts with a pipe, then some number of non-pipe characters, then the search pattern (${module}), then more non-pipe characters (continues up to next pipe character)
    • .* - matches rest of the line (which we're going to discard)
    • \1|${version}| - replace line with our first capture group, then a pipe, then the new replacement value (${version}), then the final pipe

    The above generates:

     **barak**.version|2001.0132012031539|
    |**apple**.version|2001.0132012031539|
    |**barak**.version|2001.01.2012031541|     <<<=== replaced
    |**chuck**.version|2001.0132012031539|
    |**barak**.peanuts|2001.01.2012031541|     <<<=== replaced