¿Cómo puedo fusionar archivos línea por línea?

22

gato archivo1

foo
ice
two

cat file2

bar
cream
hundred

Salida deseada:

foobar
icecream
twohundred

file1 y file2 siempre tendrán la misma cantidad de líneas en mi escenario, en caso de que eso facilite las cosas.

TuxForLife
fuente

Respuestas:

34

La herramienta adecuada para este trabajo es probablemente paste

paste -d '' file1 file2

Ver man pastepara más detalles.


También puedes usar el prcomando:

pr -TmJS"" file1 file2

dónde

  • -T apaga la paginación
  • -mJ m archivos erge, J líneas completas oining
  • -S"" separe las columnas con una cadena vacía

Si realmente quisieras hacerlo usando bash shell puro (no recomendado), entonces esto es lo que sugeriría:

while IFS= read -u3 -r a && IFS= read -u4 -r b; do 
  printf '%s%s\n' "$a" "$b"
done 3<file1 4<file2

(Solo se incluye esto porque el tema surgió en los comentarios a otra solución de bash puro propuesta).

conductor de acero
fuente
1
Impresionante, gracias por la solución muy simple. ¿Debería preocuparme alguna vez por la portabilidad cuando se trata de usar pegar?
TuxForLife
1
@ user264974 paste está en GNU Coreutils, por lo que probablemente estés bastante seguro.
nettux
8

A través de way:

awk '{getline x<"file2"; print $0x}' file1
  • getline x<"file2"lee la línea completa del archivo2 y la mantiene en la variable x .
  • print $0ximprime toda la línea desde el archivo1 usando $0luego xcuál es la línea guardada del archivo2 .
αғsнιη
fuente
Muy bueno tener una alternativa awk, ¡puedo usar esto en su lugar!
TuxForLife
4

pasteEs el camino a seguir . Si desea verificar otros métodos, aquí hay una pythonsolución:

#!/usr/bin/env python2
import itertools
with open('/path/to/file1') as f1, open('/path/to/file2') as f2:
    lines = itertools.izip_longest(f1, f2)
    for a, b in lines:
        if a and b:
            print a.rstrip() + b.rstrip()
        else:
            if a:
                print a.rstrip()
            else:
                print b.rstrip()

Si tiene pocas líneas:

#!/usr/bin/env python2
with open('/path/to/file1') as f1, open('/path/to/file2') as f2:
    print '\n'.join((a.rstrip() + b.rstrip() for a, b in zip(f1, f2)))

Tenga en cuenta que para un número desigual de líneas, esta terminará en la última línea del archivo que termina primero.

heemayl
fuente
3

Además, con pure bash(tenga en cuenta que esto ignorará totalmente las líneas vacías):

#!/bin/bash

IFS=$'\n' GLOBIGNORE='*'
f1=($(< file1))
f2=($(< file2))
i=0
while [ "${f1[${i}]}" ] && [ "${f2[${i}]}" ]
do
    echo "${f1[${i}]}${f2[${i}]}" >> out
    ((i++))
done
while [ "${f1[${i}]}" ]
do
    echo "${f1[${i}]}" >> out
    ((i++))
done
while [ "${f2[${i}]}" ]
do
    echo "${f2[${i}]}" >> out
    ((i++))
done
kos
fuente
Esto esta simplemente mal. No funciona en absoluto. Utilice mapfilepara leer los archivos en matrices, o use un bucle while con dos readcomandos, leyendo de cada uno su fd.
geirha
@geirha Tienes razón, me equivoqué con la sintaxis, está bien ahora.
kos
no exactamente. Con el código actualizado, se ignorarán las líneas vacías, y si alguna línea contiene caracteres globales, la línea podría reemplazarse por nombres de archivo coincidentes. Así que nunca use array=( $(cmd) )o array=( $var ). Usar en su mapfilelugar.
geirha
@geirha Tienes razón, por supuesto, me ocupé de los caracteres glob pero dejé la nueva línea ignorada, porque para hacer eso y para hacer una solución decente necesita ser reescrita. Especifiqué esto y dejaré esta versión en caso de que sea útil para alguien mientras tanto. Gracias por tus puntos hasta ahora.
kos
2

La manera perl, fácil de entender:

#!/usr/bin/perl
$filename1=$ARGV[0];
$filename2=$ARGV[1];

open(my $fh1, "<", $filename1) or die "cannot open < $filename1: $!";
open(my $fh2, "<", $filename2) or die "cannot open < $filename2: $!";

my @array1;
my @array2;

while (my $line = <$fh1>) {
  chomp $line;
  push @array1, $line;
}
while (my $line = <$fh2>) {
  chomp $line;
  push @array2, $line;
}

for my $i (0 .. $#array1) {
  print @array1[$i].@array2[$i]."\n";
}

Empezar con:

./merge file1 file2

Salida:

foobar
icecream
twohundred
AB
fuente