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:
- Fusionando dos csvs en uno con columnas [duplicado]
- Combinar 2 archivos csv en powershell [duplicado]
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
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