powershell batch-file cmd merge

Comando CMD o Powershell para combinar(combinar) líneas correspondientes de dos archivos



batch-file merge (6)

¿Es posible usar CMD y Powershell para combinar 2 archivos en 1 archivo como este:

file1-line1 tab file2-line1 file1-line2 tab file2-line2 file1-line3 tab file2-line3

Por lo tanto, toma el archivo 1, línea 1 e inserta una pestaña y luego inserta el archivo 2, línea 1. Entonces, ¿esto para todas las líneas posteriores en cada archivo?


En PowerShell, y suponiendo que ambos archivos tengan exactamente el mismo número de líneas:

$f1 = Get-Content file1 $f2 = Get-Content file2 for ($i = 0; $i -lt $f1.Length; ++$i) { $f1[$i] + "`t" + $f2[$i] }


Hay varias preguntas recientes bloqueadas [duplicadas] que se vinculan con esta pregunta como:

No estoy de acuerdo porque difieren en una forma en que esta pregunta se refiere a archivos de texto y la otra a archivos csv . Como regla general, recomendaría no manipular archivos que representan objetos (como xml , json y csv ). En su lugar, recomiendo importar estos archivos (a objetos), realizar los cambios correspondientes y Convertir / Exportar los resultados nuevamente en un archivo.

Un ejemplo donde todas las soluciones generales dadas en este tema resultarán en una salida incorrecta para estos "duplicados" es donde, por ejemplo, ambos archivos csv tienen un nombre de columna (propiedad) común.
El Join-Object general (ver también: En Powershell, ¿cuál es la mejor manera de unir dos tablas en una? ) -on la lista de dos objetos cuando simplemente se omita el parámetro -on . Por lo tanto, esta solución se ajustará mejor a las otras preguntas "duplicadas" ( csv ). Tome Merge 2 csv files en powershell [duplicado] de @Ender como ejemplo:

$A = ConvertFrom-Csv @'' ID,Name 1,Peter 2,Dalas ''@ $B = ConvertFrom-Csv @'' Class Math Physic ''@ $A | Join $B ID Name Class -- ---- ----- 1 Peter Math 2 Dalas Physic

En comparación con las soluciones de combinación de "texto" que se proporcionan en esta respuesta, el cmdlet general Join-Object es capaz de manejar diferentes longitudes de archivos y le permite decidir qué incluir ( LeftJoin , RightJoin o FullJoin ). Además, usted tiene control sobre qué columnas desea incluir ( $A | Join $B -Property ID, Name ) el pedido ( $A | Join $B -Property ID, Class, Name ) y mucho más que no se puede hacer. solo concatenando texto.

Específico a esta pregunta:

Como esta pregunta específica se refiere a archivos de texto en lugar de archivos csv , deberá -Header File1 nombre de encabezado (propiedad) (por ejemplo, -Header File1 ) al impartir el archivo y eliminar el encabezado ( Select-Object -Skip 1 ) al exportar el resultado:

$File1 = Import-Csv ./File1.txt -Header File1 $File2 = Import-Csv ./File2.txt -Header File2 $File3 = $File1 | Join $File2 $File3 | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select-Object -Skip 1 | Set-Content ./File3.txt


Probablemente la solución más simple es usar un puerto de Windows de la utilidad de paste Linux (por ejemplo, paste.exe de UnxUtils ):

paste C:/path/to/file1.txt C:/path/to/file2.txt

Desde la página del manual :

DESCRIPCIÓN

Escriba líneas que consisten en las líneas secuencialmente correspondientes de cada ARCHIVO, separadas por TAB, a la salida estándar.

Para una solución PowerShell ( ish ), usaría dos lectores de flujo :

$sr1 = New-Object IO.StreamReader ''C:/path/to/file1.txt'' $sr2 = New-Object IO.StreamReader ''C:/path/to/file2.txt'' while ($sr1.Peek() -ge 0 -or $sr2.Peek() -ge 0) { if ($sr1.Peek() -ge 0) { $txt1 = $sr1.ReadLine() } else { $txt1 = '''' } if ($sr2.Peek() -ge 0) { $txt2 = $sr2.ReadLine() } else { $txt2 = '''' } "{0}`t{1}" -f $txt1, $txt2 }

Esto evita tener que leer los dos archivos completamente en la memoria antes de fusionarlos, lo que conlleva el riesgo de agotamiento de la memoria para archivos grandes.


Solución Powershell:

$file1 = Get-Content file1 $file2 = Get-Content file2 $outfile = "file3.txt" for($i = 0; $i -lt $file1.length; $i++) { "$($file1[$i])`t$($file2[$i])" | out-file $outfile -Append }


Una solución generalizada que admite múltiples archivos , basada en la excelente solución System.IO.StreamReader Ansgar Wiechers :

La capacidad de PowerShell para invocar miembros (propiedades, métodos) directamente en una colección y hacer que se invoquen automáticamente en todos los elementos de la colección ( enumeración de miembros , v3 +) permite una generalización fácil:

# Make sure .NET has the same current dir. as PS. [System.IO.Directory]::SetCurrentDirectory($PWD) # The input file paths. $files = ''file1'', ''file2'', ''file3'' # Create stream-reader objects for all input files. $readers = [IO.StreamReader[]] $files # Keep reading while at least 1 file still has more lines. while ($readers.EndOfStream -contains $false) { # Read the next line from each stream (file). # Streams that are already at EOF fortunately just return "". $lines = $readers.ReadLine() # Output the lines separated with tabs. $lines -join "`t" } # Close the stream readers. $readers.Close()

Get-MergedLines (código fuente a continuación; invocar con -? Para obtener ayuda) envuelve la funcionalidad en una función que:

  • acepta un número variable de nombres de archivo , tanto como argumento como a través de la canalización

  • usa un separador configurable para unir las líneas (el valor predeterminado es una pestaña)

  • permite recortar instancias de separador final

function Get-MergedLines() { <# .SYNOPSIS Merges lines from 2 or more files with a specifiable separator (default is tab). .EXAMPLE > Get-MergedLines file1, file2 ''<->'' .EXAMPLE > Get-ChildItem file? | Get-MergedLines #> param( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias(''PSPath'')] [string[]] $Path, [string] $Separator = "`t", [switch] $TrimTrailingSeparators ) begin { $allPaths = @() } process { $allPaths += $Path } end { # Resolve all paths to full paths, which may include wildcard resolution. # Note: By using full paths, we needn''t worry about .NET''s current dir. # potentially being different. $fullPaths = (Resolve-Path $allPaths).ProviderPath # Create stream-reader objects for all input files. $readers = [System.IO.StreamReader[]] $fullPaths # Keep reading while at least 1 file still has more lines. while ($readers.EndOfStream -contains $false) { # Read the next line from each stream (file). # Streams that are already at EOF fortunately just return "". $lines = $readers.ReadLine() # Join the lines. $mergedLine = $lines -join $Separator # Trim (remove) trailing separators, if requested. if ($TrimTrailingSeparators) { $mergedLine = $mergedLine -replace (''^(.*?)(?:'' + [regex]::Escape($Separator) + '')+$''), ''$1'' } # Output the merged line. $mergedLine } # Close the stream readers. $readers.Close() } }


@echo off setlocal EnableDelayedExpansion rem Next line have a tab after the equal sign: set "TAB= " Rem First file is read with FOR /F command Rem Second file is read via Stdin < file2.txt (for /F "delims=" %%a in (file1.txt) do ( Rem Read next line from file2.txt set /P "line2=" Rem Echo lines of both files separated by tab echo %%a%TAB%!line2! ))

Más detalles en esta publicación