Remove a specific part of string split by multiple-charater delimiter in bash

Viewed 95

I'm trying to process my string and steps as below:

mytext='\\[[123 one (/)\n\\[[456 two (/)\n\\[[789 three (/)'
myvar=456

And I want to remove a part of string (and delimiter) that match myvar after split by delimiter \n, the result should be

\\[[123 one (/)\n\\[[789 three (/)

But I still not find out the solution.

If the delimiter is a single character like :, I can be done with sed command:

mytext2='\\[[123 one (/):\\[[456 two (/):\\[[789 three (/)'
myvar2=456
echo $mytext2 | sed -E "s/[^:]*${myvar}[^:]*(:|$)//g"

Result as expected: \\[[123 one (/):\\[[789 three (/)

How can be done if delimiter is a multiple-character in this case? Thanks.

3 Answers

If you can find another delimiter to be used for example, :, you can first replace it with :,

echo $mytext | tr '\n' ':' | sed -E "s/[^:]*${myvar}[^:]*(:|$)//g"

The full script looks like this

mytext='\\[[123 one (/):\\[[456 two (/):\\[[789 three (/)'
myvar=456
v=$(echo $mytext | tr '\n' ':' | sed -E "s/[^:]*${myvar}[^:]*(:|$)//g")
echo $v

If the multiple character delimiter is something like abcd, you can try to use sed to replace it first instead of using tr.

Even if you use sed -E, you still lack some support (?<=, ?!, ?=, ? etc). I suggest you use perl (Perl Compatible Regular Expressions).

mytext='\\[[123 one (/)\n\\[[456 two (/)\n\\[[789 three (/)';
myvar=456;
echo $mytext | perl -pe "s/(?<=\\\n).*${myvar}.*?(\\\n|$)//g";

Details:

  • (?<=\\\n): string starts after \n. \\ escape character \
  • .*${myvar}.*?(\\\n|$): get string which contains value of variable myvar and ends with \n or end of line.

Result.

\\[[123 one (/)\n\\[[789 three (/)

I sugges awk in this case since you may specify the literal multichar delimiter pattern as the input/output field separator, iterate over the fields and discard all those not matching your value:

mytext='\\[[123 one (/)\n\\[[456 two (/)\n\\[[789 three (/)'
myvar=456
awk -v myvar=$myvar 'BEGIN{FS=OFS="\\\\n"} {s="";
    for (i=1; i<=NF; i++) { 
        if ($i !~ myvar) {s = s (i==1 ? "" : OFS) $i;}
    }
} END{print s}' <<< "$mytext"
# => \\[[123 one (/)\\n\\[[789 three (/)

See the online awk demo.

NOTES:

  • BEGIN{FS=OFS="\\\\n"} - sets the input/output field separator to \n
  • -v myvar=$myvar passes the myvar to awk
  • s="" - assigns s to an empty string
  • for (i=1; i<=NF; i++) {...} - iterates over all fields
  • if ($i !~ myvar) {...} - if the current field value matches myvar...
  • s = s (i==1 ? "" : OFS) $i;} - append either the current field value to s (if is the first field) or output separator and the current field value (if it is not the first)
  • END{print s} - prints s after the field checks.
Related