Pregunta

Tengo un archivo que contiene un gran número de ocurrencias de la cadena Guid="GUID HERE" (donde GUID HERE es un GUID único en cada caso) y quiero reemplazar cada GUID existente con un nuevo GUID único.

Esto está en un equipo de desarrollo de Windows, por lo que puede generar GUID únicos con uuidgen.exe (que produce un GUID en la salida estándar cada vez que se ejecute). He sed y tales disponibles (pero no awk por extraño que parezca).

Básicamente, estoy tratando de averiguar si es posible (y si es así, cómo) para usar la salida de un programa de línea de comando como el texto de reemplazo en una expresión de sustitución sed para que pueda realizar esta sustitución con un mínimo del esfuerzo de mi parte. No necesito usar sed - si hay otra manera de hacerlo, como por ejemplo algún loco vim-fu o algún otro programa, que funciona tan bien - pero preferiría soluciones que utilizan un conjunto mínimo de * nix programas ya que no estoy realmente en las máquinas * nix.

Para que quede claro, si tengo un archivo de la siguiente manera:

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

Me gustaría que se convierta en esto:

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

donde A, B, C, D son los GUID reales, por supuesto.

(por ejemplo, he visto xargs usado para cosas similares a esta, pero no es disponible en las máquinas que necesito esto para ejecutar el, tampoco. Yo podría instalarlo si es realmente la única manera, aunque yo prefiero no)

¿Fue útil?

Solución

Reescribí la solución C # en PowerShell. Pensé que sería más fácil para que ejecute un script de PowerShell a continuación, compila un C # exe.

Pasos para usar esto:

  1. descargar / instalar PowerShell
  2. Guardar el código de abajo en algún lugar, llamado GuidSwap.ps1
  3. Modificar el nombre de archivo y $ $ OutputFileName variables para adaptarse a sus necesidades
  4. Ejecutar PowerShell -noexit c: \ ubicación \ 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"

Otros consejos

¿Usted estaría abierto a la compilación de una aplicación de consola de C # para hacer esto? Saqué esto muy rápido. Se necesita un nombre de archivo como argumento de línea de comandos, encuentra algo que se parece a un GUID, lo reemplaza con un nuevo GUID, y escribe el nuevo contenido del archivo.

Tome un vistazo:

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

Yo estaba buscando una forma de reemplazar todos los GUID en una solución de Visual Studio, así que tomé la respuesta a esta pregunta StackOverflow (GuidSwap.ps1) y se extendió de tal manera que la secuencia de comandos realiza un seguimiento de los GUID que se hace referencia en varios archivos . Un ejemplo se muestra en la cabecera a continuación.

<#
    .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)
"@

sólo se puede capturar el fluido en una variable en primer lugar, entonces la sed?

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

A mi me gusta la solución por BigJoe714. Tomé un paso más allá para encontrar todos los archivos de extensión específicos y reemplazar todos los 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>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top