Replacing all GUIDs in a file with new GUIDs from the command line Replacing all GUIDs in a file with new GUIDs from the command line shell shell

Replacing all GUIDs in a file with new GUIDs from the command line


I rewrote the C# solution in PowerShell. I figured it would be easier for you to run a powershell script then compile a C# exe.

Steps for using this:

  1. Download/install powershell
  2. Save the code below somewhere, named GuidSwap.ps1
  3. Modify the $filename and $outputFilename variables to suit your needs
  4. Run powershell -noexit c:\location\to\guidswap.ps1

## GuidSwap.ps1#### Reads a file, finds any GUIDs in the file, and swaps them for a NewGUID##$filename = "d:\test.txt"$outputFilename = "d:\test_new.txt"$text = [string]::join([environment]::newline, (get-content -path $filename))$sbNew = new-object system.text.stringBuilder$pattern = "[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}"$lastStart = 0$null = ([regex]::matches($text, $pattern) | %{    $sbNew.Append($text.Substring($lastStart, $_.Index - $lastStart))    $guid = [system.guid]::newguid()    $sbNew.Append($guid)    $lastStart = $_.Index + $_.Length})$null = $sbNew.Append($text.Substring($lastStart))$sbNew.ToString() | out-file -encoding ASCII $outputFilenameWrite-Output "Done"


I was looking for a way to replace all GUIDs in a Visual Studio solution, so I took the answer to this StackOverflow question (GuidSwap.ps1) and extended it so that the script keeps track of GUIDs that are referenced across multiple files. An example is shown in the header below.

<#    .Synopsis    Replace all GUIDs in specified files under a root folder, carefully keeping track     of how GUIDs are referenced in different files (e.g. Visual Studio solution).    Loosely based on GuidSwap.ps1:    http://stackoverflow.com/questions/2201740/replacing-all-guids-in-a-file-with-new-guids-from-the-command-line    .NOTES    Version:        1.0    Author:         Joe Zamora (blog.idmware.com)    Creation Date:  2016-03-01    Purpose/Change: Initial script development    .EXAMPLE    .\ReplaceGuids.ps1 "C:\Code\IDMware" -FileNamePatterns @("*.sln","*.csproj","*.cs") -Verbose -WhatIf#># Add common parameters to the script.[CmdletBinding()]param(    $RootFolder    ,$LogFolder='.'    ,[String[]]$FileNamePatterns    ,[switch]$WhatIf)$global:WhatIf = $WhatIf.IsPresent# Change directory to the location of this script.$scriptpath = $MyInvocation.MyCommand.Path$dir = Split-Path $scriptpathcd $dir$ScriptName = $MyInvocation.MyCommand.NameIf(!($RootFolder)){    Write-Host @"Usage: $ScriptName  -RootFolder <RootFolder> [Options]Options:    -LogFolder <LogFolder>                      Defaults to location of script.    -FileNamePatterns @(*.ext1, *.ext2, ...)    Defaults to all files (*).    -WhatIf                                     Test run without replacements.    -Verbose                                    Standard Powershell flags.    -Debug"@    Exit}if ($LogFolder -and !(Test-Path "$LogFolder" -PathType Container)){    Write-Host "No such folder: '$LogFolder'"    Exit}<#    .Synopsis    This code snippet gets all the files in $Path that contain the specified pattern.    Based on this sample:    http://www.adminarsenal.com/admin-arsenal-blog/powershell-searching-through-files-for-matching-strings/#>function Enumerate-FilesContainingPattern {[CmdletBinding()]param(    $Path=(throw 'Path cannot be empty.')    ,$Pattern=(throw 'Pattern cannot be empty.')    ,[String[]]$FileNamePatterns=$null)    $PathArray = @()    if (!$FileNamePatterns) {        $FileNamePatterns = @("*")    }    ForEach ($FileNamePattern in $FileNamePatterns) {        Get-ChildItem $Path -Recurse -Filter $FileNamePattern |        Where-Object { $_.Attributes -ne "Directory"} |        ForEach-Object {            If (Get-Content $_.FullName | Select-String -Pattern $Pattern) {                $PathArray += $_.FullName            }        }    }    $PathArray} <# function Enumerate-FilesContainingPattern #># Timestamps and performance.$stopWatch = [System.Diagnostics.Stopwatch]::StartNew()$startTime = Get-DateWrite-Verbose @"--- SCRIPT BEGIN $ScriptName $startTime ---"@# Begin by finding all files under the root folder that contain a GUID pattern.$GuidRegexPattern = "[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}"$FileList = Enumerate-FilesContainingPattern $RootFolder $GuidRegexPattern $FileNamePatterns$LogFilePrefix = "{0}-{1}" -f $ScriptName, $startTime.ToString("yyyy-MM-dd_HH-mm-ss")$FileListLogFile = Join-Path $LogFolder "$LogFilePrefix-FileList.txt"$FileList | ForEach-Object {$_ | Out-File $FileListLogFile -Append}Write-Host "File list log file:`r`n$FileListLogFile"cat $FileListLogFile | %{Write-Verbose $_}# Next, do a read-only loop over the files and build a mapping table of old to new GUIDs.$guidMap = @{}foreach ($filePath in $FileList){    $text = [string]::join([environment]::newline, (get-content -path $filePath))    Foreach ($match in [regex]::matches($text, $GuidRegexPattern)) {        $oldGuid = $match.Value.ToUpper()        if (!$guidMap.ContainsKey($oldGuid)) {            $newGuid = [System.Guid]::newguid().ToString().ToUpper()            $guidMap[$oldGuid] = $newGuid        }    }}$GuidMapLogFile = Join-Path $LogFolder "$LogFilePrefix-GuidMap.csv""OldGuid,NewGuid" | Out-File $GuidMapLogFile$guidMap.Keys | % { "$_,$($guidMap[$_])" | Out-File $GuidMapLogFile -Append }Write-Host "GUID map log file:`r`n$GuidMapLogFile"cat $GuidMapLogFile | %{Write-Verbose $_}# Finally, do the search-and-replace.foreach ($filePath in $FileList) {    Write-Verbose "Processing $filePath"    $newText = New-Object System.Text.StringBuilder    cat $filePath | % {         $original = $_        $new = $_        $isMatch = $false        $matches = [regex]::Matches($new, $GuidRegexPattern)        foreach ($match in $matches) {            $isMatch = $true            $new = $new -ireplace $match.Value, $guidMap[$match.Value.ToString().ToUpper()]        }                $newText.AppendLine($new) | Out-Null        if ($isMatch) {            $msg = "Old: $original`r`nNew: $new"            if ($global:WhatIf) {                Write-Host "What if:`r`n$msg"            } else {                Write-Verbose "`r`n$msg"            }        }    }    if (!$global:WhatIf) {        $newText.ToString() | Set-Content $filePath    }}# Timestamps and performance.$endTime = Get-DateWrite-Verbose @"--- SCRIPT END $ScriptName $endTime ---Total elapsed: $($stopWatch.Elapsed)"@


Would you be open to compiling a C# console app to do this? I whipped this up real quick. It takes a filename as a command line argument, finds anything that looks like a GUID, replaces it with a new GUID, and writes the new contents of the file.

Take a look:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.IO;using System.Text.RegularExpressions;namespace GUIDSwap{    class Program    {        static int Main(string[] args)        {            try            {                if (args.Length == 0) throw new ApplicationException("No filename specified");                string filename = args[0];                filename = filename.TrimStart(new char[] { '"' }).TrimEnd(new char[] { '"' });                if (!File.Exists(filename)) throw new ApplicationException("File not found");                StreamReader sr = new StreamReader(filename);                string text = sr.ReadToEnd();                sr.Close();                StringBuilder sbNew = new StringBuilder();                string pattern = "[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}";                int lastStart = 0;                foreach (Match m in Regex.Matches(text, pattern))                {                    sbNew.Append(text.Substring(lastStart, m.Index - lastStart));                    sbNew.Append(Guid.NewGuid().ToString());                    lastStart = m.Index + m.Length;                }                sbNew.Append(text.Substring(lastStart));                StreamWriter sw = new StreamWriter(filename, false);                sw.Write(sbNew.ToString());                sw.Flush();                sw.Close();                return 0;            }            catch (Exception ex)            {                Console.WriteLine(ex.Message);                return 1;            }        }    }}