Reputation: 2309
Trying to understand how exactly to escape everything for this. I'm trying to essentially find and replace this instance in a file, set :repo_url, ''
but there could be any number of characters in between the single quotes.
One example of a line I'd need to replace is:
set :repo_url, 'git://github.com/something.com.git'
I've tried the following already:
contents.gsub!("set :repo_url, /^\'(.*)\'$/", "set :repo_url, 'test'")
contents.gsub!("set :repo_url, /^'(.*)'$/", "set :repo_url, 'test'")
contents.gsub!("set :repo_url, '(.*)", "set :repo_url, 'test'")
None of those work, this does but only when there's nothing between the quotes:
contents.gsub!("set :repo_url, ''", "set :repo_url, 'test'")
What am I missing? Sorry I'm still learning Ruby + Regex.
Upvotes: 1
Views: 456
Reputation: 84413
There's more than one way to do what you want, including fixing quoting issues with special syntax and manipulating match variables to control the text that will be replaced.
Using the following should work:
contents.gsub!(
/^set :repo_url[^'"]+\K(.).*?\1/,
'\1https://example.com/foo.git\1'
)
and the way it works is explained in detail below.
If all you want to do is avoid quoting issues, you can use Ruby's %q()
syntax for your strings to avoid having to escape quotes. However, if you are having other trouble with your match, you can create a more robust regular expression like /^set :repo_url[^'"]+\K(.).*?\1/
. For example:
contents = <<~EOF
set :repo_url, 'git://github.com/foo/bar.git'
set :repo_url, 'git://github.com/foo/baz.git' # a comment
set :repo_url, "git://github.com/foo/bar.git" # using doublequotes
EOF
contents.gsub!(
/^set :repo_url[^'"]+\K(.).*?\1/,
'\1https://example.com/foo.git\1'
)
puts contents
This will print:
set :repo_url, 'https://example.com/foo.git'
set :repo_url, 'https://example.com/foo.git' # a comment
set :repo_url, "https://example.com/foo.git" # using doublequotes
#=> nil
String#gsub and related methods work by replacing the match stored in $& with replacement text, so you can do a lot of interesting things by managing what's stored (or not) in the match and associated capture groups. The regular expression pattern works as follows:
^set :repo_url
Match the fixed string "set :repo_url" only at the beginning of each line.
[^'"]+
Match all non-quote characters.
\K
Use the undocumented "keep" flag to discard the exclude the stuff to the left from $&, preventing it from being clobbered by the replacement text.
(.)
Capture the quote character used, e.g. "
or '
. Stores the quote in both $& and $1.
.*?\1
Non-greedy match that adds all characters, up to and including the next matching quote character (stored in $1 and referenced by \1
), into $&.
The replacement string leverages the capture groups from the regular expression pattern to restore portions of the string that would otherwise get replaced by the String#gsub! method. It works like this:
\1
Add the quote character from capture group 1 to the replacement string.
https://example.com/foo.git
This is the actual text you want to replace your other URL with.
\1
Add the same quote character from capture group 1 again to the end of the replacement string.
Because everything to the left of \K
and to the right of the closing quote is not stored in $& by our regular expression, we don't have to use capture groups or backreferences to include those portions of the line into our replacement string. There are certainly other ways to express the match or the replacement, but this works very well for the posted examples.
Upvotes: 1
Reputation: 16727
contents.gsub!(/^set :repo_url, '.*?'$/, "set :repo_url, 'test'")
should do what you want
Upvotes: 0