Transform matched group in sed multiple times

Viewed 118

I have the following types of statements on multiple lines in multiple files (these are not necessarily on a line by themselves):

xref:my-awesome-link[This link]

I need to change this to:

xref:this-link[This link]

I have got to:

xref:this link[This link]

using (posting the whole thing which is inside a shell script):

find ./ -name "*.md.adoc" -type f -exec sh -c \
  'gsed -i "s/xref\:.*\[\(.*\)\]/xref\:\L\1\E\[\1\]/g" {}' \;

xref\:.*\[\(.*\)\] - the reg exp that captures the string inside [].

\L\1\E\ - does the transformation for lowercase.

What I am struggling with now, is how to transform the xref:this link[This link] to xref:this-link[This link]. I know that I need to transform the matched group but haven't been able to figure out how to do both the lowercase AND replace the spaces with '-'.

I am running this on a MacOS using GNU Sed.

Thanks!

3 Answers

Since you said your sed is gnu sed, this is an alternative solution using gnu sed's e flag instead of label and branch:

sed -r 's/(xref:).*\[(.*)]/echo "\1$(echo "\L\2\E"\|sed "s# #-#g")[\2]"/ge'

Let's do some test:

$ echo "xref:my-awesome-link[FOO Bar BLAH]"|sed -r 's/(xref:).*\[(.*)]/echo "\1$(echo "\L\2\E"\|sed "s# #-#g")[\2]"/ge'
xref:foo-bar-blah[FOO Bar BLAH]
$ cat ip.txt
xref:my-awesome-link[This link is good]

$ sed -E 's/xref:.*\[(.*)]/xref:\L\1\E[\1]/; :a s/ ([^[]+\[)/-\1/; ta' ip.txt
xref:this-link-is-good[This link is good]

:a s/ ([^[]+\[)/-\1/; ta is the main change. :a is a label to mark the substitution command that follows. ([^[]+\[) matches a space character only if it is followed by non [ characters and a [ character (so this will fail if there are more than one [ character in the line). Then, if the substitution succeeds, ta will be executed which branches back to the substitution command and this repeats until the substitution fails.


You can also use perl to do it in single substitution, as it allows Perl code in replacement section.

perl -pe 's/xref:\K.*(?=\[(.*)])/lc($1) =~ tr| |-|r/e'

You may use

find ./ -name "*.md.adoc" -type f -exec gsed -i 's/xref\:.*\[\(.*\)\]/xref\:\L\1\E\[\1\]/g; :a;s/\(xref:[^[]*\) /\1-/;ta;' {} \;

I added :a;s/\(xref:[^[]*\) /\1-/;ta;:

  • :a - label a
  • s/\(xref:[^[]*\) /\1-/ - finds xref:, then 0+ chars other than [ (Group 1, \1) and then a space, and replaces with the Group 1 value and a -
  • ta - a jump to label a upon a successful replacement
Related