Wie kann ich Dateien zeilenweise zusammenführen?

22

Katzendatei1

foo
ice
two

Katzendatei2

bar
cream
hundred

Gewünschte Ausgabe:

foobar
icecream
twohundred

Datei1 und Datei2 haben in meinem Szenario immer die gleiche Anzahl von Zeilen, falls dies die Sache einfacher macht.

TuxForLife
quelle

Antworten:

34

Das richtige Werkzeug für diesen Job ist wahrscheinlich paste

paste -d '' file1 file2

Siehe man pastefür weitere Einzelheiten.


Sie können auch den folgenden prBefehl verwenden:

pr -TmJS"" file1 file2

woher

  • -T Deaktiviert die Paginierung
  • -mJ m erge Dateien, J oining volle Linien
  • -S"" Trennen Sie die Spalten mit einem leeren String

Wenn Sie es wirklich mit einer reinen Bash-Shell machen wollten (nicht empfohlen), dann ist dies das, was ich vorschlagen würde:

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

(Dies nur, weil das Thema in Kommentaren zu einer anderen vorgeschlagenen Pure-Bash-Lösung auftauchte.)

Stahlfahrer
quelle
1
Super, danke für die sehr einfache Lösung. Sollte ich mir jemals Gedanken über die Portabilität bei der Verwendung von Paste machen?
TuxForLife
1
@ user264974 paste ist in GNU Coreutils enthalten, daher sind Sie wahrscheinlich ziemlich sicher.
Nettux
8

Durch weg:

awk '{getline x<"file2"; print $0x}' file1
  • getline x<"file2"Liest die gesamte Zeile aus Datei2 und speichert sie in der Variable x .
  • print $0xdruckt die gesamte Zeile aus Datei1 aus, wobei $0dann xdie gespeicherte Zeile von Datei2 verwendet wird .
αғsнιη
quelle
Sehr gut, um eine awk-Alternative zu haben, ich kann dies stattdessen verwenden!
TuxForLife
4

pasteist der Weg zu gehen . Wenn Sie andere Methoden überprüfen möchten, finden Sie hier eine pythonLösung:

#!/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()

Wenn Sie nur wenige Zeilen haben:

#!/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)))

Beachten Sie, dass bei einer ungleichen Anzahl von Zeilen diese an der letzten Zeile der Datei endet, die zuerst endet.

heemayl
quelle
3

Auch mit pure bash(beachte, dass dies leere Zeilen völlig ignoriert):

#!/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
quelle
Das ist einfach falsch. Es funktioniert überhaupt nicht. Verwenden Sie diese Option mapfile, um die Dateien in Arrays einzulesen, oder verwenden Sie eine while-Schleife mit zwei readBefehlen, die von jedem ihrer Felder lesen.
Geirha
@geirha Du hast Recht, ich habe die Syntax durcheinander gebracht, jetzt ist es in Ordnung.
Kos
nicht ganz. Mit dem aktualisierten Code werden leere Zeilen ignoriert, und wenn eine Zeile Glob-Zeichen enthält, wird die Zeile möglicherweise durch übereinstimmende Dateinamen ersetzt. Also niemals array=( $(cmd) )oder verwenden array=( $var ). Verwenden Sie mapfilestattdessen.
Geirha
@geirha Sie haben natürlich Recht, ich habe mich um die Glob-Zeichen gekümmert, aber ich habe den Zeilenumbruch ignoriert, denn um das zu tun und eine anständige Lösung daraus zu machen, muss er neu geschrieben werden. Ich habe dies angegeben und lasse diese Version, falls es in der Zwischenzeit für jemanden nützlich sein sollte. Vielen Dank für Ihre bisherigen Punkte.
Kos
2

Der Perl-Weg, leicht zu verstehen:

#!/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";
}

Beginnen mit:

./merge file1 file2

Ausgabe:

foobar
icecream
twohundred
AB
quelle