Domanda

Ho un file che contiene un gran numero di occorrenze della stringa di Guid="GUID HERE" (dove GUID HERE è un GUID univoco ad ogni occorrenza) e voglio sostituire ogni GUID esistente con un nuovo GUID univoco.

Questa è una macchina di sviluppo di Windows, in modo che possa generare GUID unici con uuidgen.exe (che produce un GUID su stdout ogni volta che viene eseguito). Ho sed e tale disposizione (ma non awk stranamente).

Sono fondamentalmente cercando di capire se è possibile (e in caso affermativo, come) usare l'output di un programma a linea di comando come testo di sostituzione in sostituzione espressione sed modo che possa effettuare questa sostituzione con un minimo di sforzo da parte mia. Non ho bisogno di usare sed - se c'è un altro modo per farlo, come ad esempio qualche pazzo vim-fu o qualche altro programma, che avrebbe funzionato, come pure - ma preferirei soluzioni che utilizzano un set minimo di * programmi nix dato che non sono realmente sulle macchine * nix.

Per essere chiari, se ho un file in questo modo:

etc etc Guid="A" etc etc Guid="B"

Vorrei che diventi questo:

etc etc Guid="C" etc etc Guid="D"

dove A, B, C, D sono GUID reali, naturalmente.

(per esempio, ho visto xargs usato per cose simili a questo, ma non è disponibile sulle macchine ho bisogno di questo per funzionare su, o. Potrei installarlo se è davvero l'unico modo, anche se io preferirei non)

È stato utile?

Soluzione

Ho riscritto la soluzione C # in PowerShell. Ho pensato che sarebbe stato più facile per voi per eseguire uno script PowerShell poi compila un C # exe.

Passi per l'utilizzo di questo:

  1. scaricare / installare PowerShell
  2. Salvare il codice qui sotto da qualche parte, di nome GuidSwap.ps1
  3. Modificare il nome del file e $ $ OutputFileName variabili in base alle proprie esigenze
  4. Esegui PowerShell -noexit c: \ percorso \ a \ 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 $outputFilename

Write-Output "Done"

Altri suggerimenti

Sareste aperto a compilazione di un C # console app per fare questo? Ho sbattuto questo in su rapido reale. Ci vuole un nome di file come argomento della riga di comando, trova tutto ciò che si presenta come un GUID, lo sostituisce con un nuovo GUID, e scrive il nuovo contenuto del file.

Date un'occhiata:

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;
            }
        }
    }
}

Ero alla ricerca di un modo per sostituire tutti i GUID in una soluzione di Visual Studio, così ho preso la risposta a questa domanda StackOverflow (GuidSwap.ps1) ed esteso in modo che lo script registra GUID che viene fatto riferimento in più file . Un esempio è mostrato nell'intestazione di seguito.

<#
    .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 $scriptpath
cd $dir
$ScriptName = $MyInvocation.MyCommand.Name

If(!($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-Date
Write-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-Date
Write-Verbose @"

--- SCRIPT END $ScriptName $endTime ---

Total elapsed: $($stopWatch.Elapsed)
"@

si può solo catturare l'uid in una variabile prima, quindi eseguire le sed?

@echo off
setlocal enabledelayedexpansion
for /f %%x in ('uuidgen.exe') do (
        set uid=%%x
)
sed -e "s/Guid=\"\(.*\)\"/Guid=\"!uid!\"/g" file

Mi piace molto la soluzione BigJoe714. Ho preso un passo ulteriore trovare tutti i file di estensione specifici e sostituire tutti i GUID.

<pre>
<code>
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace AllGuidSwap
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                if (args.Length == 0) throw new ApplicationException("No filename specified");

                string directory = args[0]; //Path
                string extensionToFind = args[1]; //Extension to find

                if (!Directory.Exists(directory)) throw new ApplicationException("directory not found");

                var allFiles = Directory.GetFiles(directory).Where(a => a.EndsWith(extensionToFind));

                foreach (var filename in allFiles)
                {
                    if (!File.Exists(filename)) throw new ApplicationException("File not found");

                    var sr = new StreamReader(filename);
                    var text = sr.ReadToEnd();
                    sr.Close();

                    var sbNew = new StringBuilder();

                    var pattern = "[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}";

                    var lastStart = 0;
                    foreach (Match m in Regex.Matches(text, pattern))
                    {
                        sbNew.Append(text.Substring(lastStart, m.Index - lastStart));
                        sbNew.Append(Guid.NewGuid().ToString().ToUpperInvariant());
                        lastStart = m.Index + m.Length;
                    }

                    sbNew.Append(text.Substring(lastStart));

                    var sw = new StreamWriter(filename, false);
                    sw.Write(sbNew.ToString());
                    sw.Flush();
                    sw.Close();
                }

                Console.WriteLine("Successful");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.ReadKey();
        }
    }
}

</code>
</pre>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top