This article explores the method to re-program a script to correctly identify the version numbers that need updating.
Introduction
The requirement in my job was encountered recently, wherein access was given to a folder full of configuration files.
The task is to fix a script that replaced the file version numbers.
The issue was that the script was not making the correct replacement, thereby fouling up the operation of a system that depended on the configuration files.
The middles of some version numbers that contained, but did not begin with, a specific digit, were being replaced when this was not the intended functionality.
We explore in this article the way to re-program the script in order to correctly identify the version numbers that need updating.
Background
The NuGet package called PostSharp is highly useful. However, its authors frequently update it. There is a requirement in our organization, that all software depending on the PostSharp NuGet packages must always use the very latest version.
Currently, the PostSharp version number is 6.10.15
as of the time of writing this article. I have a number of packages.config files that need to have the version number updated in them, so then I can do a nuget restore
on the command line to update the packages.
Therefore, I conceived the notion to write a C# script in LINQPad that would iterate through the packages.config files and *.csproj files in a root directory, replacing the version numbers in the files with the latest version that was scraped from the NuGet website.
Regular-Expression ("Regex" For Short) Hell
The Task at Hand
I am trying to match a version number (to replace it in various config files).
The version number I want to match:
- Always consists of three numbers,
0-99
inclusive, separated by dots - The first number is always a
6
For example, 6.10.14
is an example version number.
Current Code
To do the replacement, I utilized the code:
public static class Update
{
private static string PostSharpVersionToLatest(string contents)
{
var result = string.Empty;
if (string.IsNullOrWhiteSpace(contents)) return result;
try
{
result =
Regex.Replace(
contents,
@"6\.\d+\.\d+",
LATEST_POSTSHARP_VERSION
);
result =
Regex.Replace(
result,
@"6\.\d+\.\d+\-rc",
LATEST_POSTSHARP_VERSION
);
}
catch (Exception ex)
{
Console.WriteLine(ex);
result = string.Empty;
}
return result;
}
}
The Regex being used to find and replace version numbers is 6\.\d+\.\d+
.
It seemed like the correct Regex at the time.
However, elsewhere in the text files I am searching and doing replacements in, there are other version numbers, say, 16.301.204959.29
, or somesuch. The pattern I am utilizing matches these also, and that's not what I want. I want only the 6.
in the beginning to match, not the version numbers that have, say, 16.
in the beginning, to also match.
The problem that keeps happening is that the 6.301.204959
text in the longer version number is also matched and replaced, which leads to packages.config files getting corrupted.
Regex Hero to the Rescue
There is a really handy utility out there -- which I recommend you get -- called Regex Hero.
I am going to utilize it to find out what's wrong with my regex. The Regex Hero window, shown matching the target string 6.10.14
with the regex 6\.\d+\.\d+
, is visible in Figure 1.
Figure 1. The Regex Hero window is shown with the target string 6.10.14 being matched by the regex 6\.\d+\.\d+.
It appears that our pattern works perfectly. However, let's suppose the longer version number, 16.301.204959.29
, is also in our text files that we're doing our find and replace operation in.
We put it into Regex Hero, in the Target String box, using the same value for the Regular Expression field, and get the results as shown in Figure 2:
Figure 2. Regex Hero displays a partial match to the target string 16.301.204959.29.
One can imagine that this may lead to problems if we try to do a textual Replace operation and we only wish to target those version numbers that explicitly begin with a 6
and not a 16
, say, or 36
, etc.
Eat Your Carets [sic]
I know I can utilize a caret (^
) at the beginning of my regex. According to this blog post:
Quote:
You can use the caret symbol (^) at the start of a regular expression to indicate that a match must occur at the beginning of the searched text.
My thought process was to try the following regex: ^6\.\d+\.\d+
. As can be seen in Regex Hero, there are no matches to the version number 16.301.204959.29
, as is shown in Figure 3:
Figure 3. Regex Hero displays no matches for the version number 16.301.204959.29 with the regex ^6\.\d+\.\d+.
Let me just try the sample version number 6.10.14
with my new regex, as shown in Figure 4:
Figure 4. Regex Hero displays a positive match on the value 6.10.14 with the regex ^6\.\d+\.\d+.
Conclusions
We can see that the issue of creating a regex to match only those values that start with a specific digit can be created by using a caret, ^
, at the beginning of the regex.
History
- 20th August, 2022: Initial version