Repeated regex replacements with overlapping lookbehinds

Viewed 67

I want

oy 1 x 1 x 1
   1 x 1 x 1

to become

oy 1 y 1 y 1
   1 x 1 x 1

(basically, replacing x by y if the line starts with oy, reusing y).

I have tried

echo "oy 1 x 1 x 1" | sed -E "s/(o(.)(.*))x/\1\2/g"
echo "oy 1 x 1 x 1" | perl -pe "s/(?<=o)(.)(.*)x/\1\2\1/g"
echo "oy 1 x 1 x 1" | perl -pe "s/o(.)(.*)\Kx/\1/g"

but each replaces only the last occurence.

These non-greedy variants

echo "oy 1 x 1 x 1" | perl -pe "s/(?<=o)(.)(.*?)x/\1\2\1/g"
echo "oy 1 x 1 x 1" | perl -pe "s/o(.)(.*?)\Kx/\1/g"

replace only the first occurence.

What else can I try?

2 Answers

This might work for you (GNU sed):

sed -E ':a;s/\<(o(.) .*)x/\1\2/;ta' file

If there is a word oy where y is any character, match that character and replace any x's throughout the rest of the line with it.

$ perl -pe 's/x/substr($_,1,1)/ge if /^o/' ip.txt
oy 1 y 1 y 1
   1 x 1 x 1
  • /^o/ if line starts with o
  • substr($_,1,1) second character of input line as replacement string
  • e flag to allow Perl code in replacement section

If the string to replace may match the first two characters, then:

$ echo 'oy 1 o 1 o 1' | perl -pe 's/o/substr($_,1,1)/ge if /^o/'
yy 1 y 1 y 1
$ echo 'oy 1 o 1 o 1' | perl -pe 's/..\Ko/substr($_,1,1)/ge if /^o/'
oy 1 y 1 y 1

I'm not sure if using substr on $_ while the s command modifies $_ is dangerous. If so, you can use these alternatives:

perl -pe '$c=substr($_,1,1); s/x/$c/g if /^o/'

# courtesy @ikegami
perl -pe 's/x/$c/g if ($c) = /^o(.)/'
Related