Comment puis-je fusionner des fichiers ligne par ligne?

22

cat file1

foo
ice
two

cat file2

bar
cream
hundred

Sortie désirée:

foobar
icecream
twohundred

file1 et file2 auront toujours le même nombre de lignes dans mon scénario, au cas où cela rendrait les choses plus faciles.

TuxForLife
la source

Réponses:

34

Le bon outil pour ce travail est probablement paste

paste -d '' file1 file2

Voir man pastepour plus de détails.


Vous pouvez également utiliser la prcommande:

pr -TmJS"" file1 file2

  • -T désactive la pagination
  • -mJ fichiers m erge, J oining lignes complètes
  • -S"" séparez les colonnes avec une chaîne vide

Si vous vouliez vraiment le faire en utilisant du shell bash pur (non recommandé), voici ce que je suggérerais:

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

(N'inclure cela que parce que le sujet a fait des commentaires sur une autre solution proposée de pure-bash.)

tournevis
la source
1
Génial, merci pour la solution très simple. Dois-je m'inquiéter de la portabilité quand il s'agit d'utiliser de la pâte?
TuxForLife
1
La pâte @ user264974 est dans GNU Coreutils, vous êtes donc probablement en sécurité.
nettux du
8

Par le biais de manière :

awk '{getline x<"file2"; print $0x}' file1
  • getline x<"file2"lit la ligne entière à partir de file2 et contient la variable x .
  • print $0ximprime la ligne entière du fichier1 en utilisant $0alors xqui est la ligne enregistrée du fichier2 .
αғsнιη
la source
Très bien d'avoir une alternative awk, je peux l'utiliser à la place!
TuxForLife
4

pasteest la voie à suivre . Si vous souhaitez vérifier d'autres méthodes, voici une pythonsolution:

#!/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 vous avez peu de lignes:

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

Notez que pour un nombre inégal de lignes, celle-ci se terminera à la dernière ligne du fichier qui se termine en premier.

heemayl
la source
3

Aussi, avec pure bash(notez que cela ignorera totalement les lignes vides):

#!/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
la source
Ceci est tout simplement faux. Ça ne marche pas du tout. Soit utiliser mapfilepour lire les fichiers dans des tableaux, soit utiliser une boucle while avec deux readcommandes, lisant chacune de leur fd.
geirha
@geirha Vous avez raison, j'ai foiré la syntaxe, ça va maintenant.
kos
pas assez. Avec le code mis à jour, les lignes vides seront ignorées et si une ligne contient des caractères globaux, la ligne peut être remplacée par des noms de fichiers correspondants. Donc, n'utilisez jamais array=( $(cmd) )ou array=( $var ). Utilisez mapfileplutôt.
geirha
@geirha Vous avez bien sûr raison, j'ai pris soin des caractères globaux mais j'ai laissé la nouvelle ligne ignorée, car pour ce faire et pour en faire une solution décente, elle doit être réécrite. Je l'ai spécifié et laisserai cette version au cas où cela serait utile à quelqu'un en attendant. Merci pour vos points jusqu'à présent.
kos
2

La façon Perl, facile à comprendre:

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

Commencer avec:

./merge file1 file2

Sortie:

foobar
icecream
twohundred
UN B
la source