rohitsoren
rohitsoren

Reputation: 121

Replace multiple Character between two characters using sed or perl

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?

Upvotes: 2

Views: 668

Answers (3)

Ed Morton
Ed Morton

Reputation: 204638

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.

Upvotes: 3

Jan
Jan

Reputation: 43199

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

Upvotes: 3

Sundeep
Sundeep

Reputation: 23697

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)

Upvotes: 2

Related Questions