Replace multiple Character between two characters using sed or perl

Viewed 242

I want to replace character e.g. ',' with ';' only between two consecutive '[' and ']' using sed or perl linux command.

Input

abcd,e%sa,@ert&,,qet|,[,,efeg,sy bh|slhg],oiuy#,[abh,ohg;,a oiu],ayba

Ouptut

abcd,e%sa,@ert&,,qet|,[;;efeg;sy bh|slhg],oiuy#,[abh;ohg;;a oiu],ayba

I tried to create sed substitution command

echo "abcd,e%sa,@ert&,,qet|,[,,efeg,sy bh|slhg],oiuy#,[abh,ohg;,a oiu],ayba" | sed 's/\[\([^]]*\),\([^[]*\)\]/\[\1;\2\]/g'

Output

abcd,e%sa,@ert&,,qet|,[,,efeg;sy bh|slhg],oiuy#,[abh,ohg;;a oiu],ayba

but it is replacing only once in sub-string between each '[' and ']'.

What mistake I'm doing in the above sed command or any other way to do the same?

3 Answers

Using Perl, you could use

(?:\G(?!\A)|\[)[^],]*\K,

See a demo on regex101.com.


In Perl this could be:

perl -pi.bak -e 's/(?:\G(?!\A)|\[)[^],]*\K,/;/g' test.txt

I know you said you wanted sed or perl but the code to do this would be very clear and simple in GNU awk:

$ awk -v RS='[[][^]]+]' '{gsub(/,/,";",RT); printf "%s%s", $0, RT}' file
abcd,e%sa,@ert&,,qet|,[;;efeg;sy bh|slhg],oiuy#,[abh;ohg;;a oiu],ayba

In the above you just set RS to the regexp you want to find ([ then not ]s then ]), then replace , with ; within the strings that match that regexp (as stored in RT) and then print the result. No cryptic escape sequences or arcane combinations of single character runes required.

I expect a sed script that can do this will be GNU-specific, and perl is non-standard so if you have or can install either of those then you should have or be able to install GNU awk too.

Modified sample input, hope my understanding of the question is correct:

$ s='a,b,c[,,1,23,4]e,w[6,7,,32]j,g'

$ echo "$s" | sed ':a s/\[\([^]]*\),\([^[]*\)\]/\[\1;\2\]/; ta'
a,b,c[;;1;23;4]e,w[6;7;;32]j,g

The sed command above is modified from the one mentioned in the question to use a loop to replace all occurrences. You can simplify it to sed -E ':a s/(\[[^]]*),([^[]*])/\1;\2/; ta'

:a marks a label for the substitute command. ta will branch to label a only if the substitution succeeds. See https://www.gnu.org/software/sed/manual/sed.html#Programming-Commands for documentation.


You can do it without loop using perl

$ echo "$s" | perl -pe 's/\[[^]]+]/$&=~tr|,|;|r/ge'
a,b,c[;;1;23;4]e,w[6;7;;32]j,g

The e flag allows to use Perl code in replacement section. Here $&=~tr|,|;|r will replace , with ; only for the matched portion ($& is similar to & in sed - gives entire matching portion)

Related