Comparaison côte à côte de plus de deux fichiers contenant des valeurs numériques

8

J'ai trois fichiers contenant une séquence triée de nombres, un par ligne:

file1

1
2
3

file2

1
3
4

file3

1
5

Je souhaite "aligner" ces trois fichiers côte à côte comme suit:

file1  file2  file3
1      1      1
2      
3      3
       4
              5

J'ai essayé avec sdiffmais ça ne marche qu'avec 2 fichiers

cheseaux
la source
Testez-vous diff3?
Costas
@Costas diff3n'a pas ce format de sortie.
Kusalananda
@Costas Oui, j'ai testé avec diff3et comme @Kusalananda l'a correctement indiqué, il ne produit pas cette sortie. Aussi, je recherche une solution générique (pour n fichiers, n> 2)
cheseaux
Si vous comparez ligne par ligne, pourquoi se 5trouve- t -il à la cinquième ligne au lieu du troisième?
Costas
Je ne compare pas ligne par ligne
cheseaux

Réponses:

6

Vous pouvez traiter chaque fichier et imprimer une ligne avec un caractère, par exemple Xpour chaque numéro manquant dans la séquence 1- max (où max est le dernier numéro de ce fichier), pasteles résultats remplacent ensuite ce caractère par un espace:

paste \
<(awk 'BEGIN{n=1};{while (n<$1) {print "X";n++}};{n=$1+1};1' file1) \
<(awk 'BEGIN{n=1};{while (n<$1) {print "X";n++}};{n=$1+1};1' file2) \
<(awk 'BEGIN{n=1};{while (n<$1) {print "X";n++}};{n=$1+1};1' file3) \
| tr X ' '

Si une certaine valeur manque dans tous les fichiers, vous obtiendrez des lignes vides dans votre sortie (en fait, elles ne sont pas vides, elles ne contiennent que des blancs).
Pour les supprimer, remplacez tr X ' ' par sed '/[[:digit:]]/!d;s/X/ /g' Également, si vous avez besoin d'un en-tête, vous pouvez toujours exécuter quelque chose comme ceci en premier:

 printf '\t%s' file1 file2 file3 | cut -c2-
don_crissti
la source
;1Cependant, j'ai eu du mal à comprendre ce que la partie à la fin signifiait. J'utilise à la {print $0}place, un peu moins crypté à mon humble avis. Quoi qu'il en soit, merci encore
cheseaux
5

Une solution générale avec awk: nécessite GNU awk

gawk -v level=0 '
    FNR==1 {level++; head[level]=FILENAME}
    !seen[$1]++ { n++; idx[$1] = n }
    { out[idx[$1]][level] = $1 }
    END {
        for (j=1; j<=level; j++) {
            printf "%s\t", head[j]
        }
        print ""
        for (i=1; i<=n; i++) {
            for (j=1; j<=level; j++) {
                printf "%s\t", out[i][j]
            }
            print ""
        }
    }
' file{1,2,3,4}
file1   file2   file3   file4   
1   1   1       
2           2   
3   3           
    4       4   
        5       
            6   

A adopté une approche différente et plus simple à ce sujet sur la base du commentaire de Don:

gawk '
    FNR==1 { printf "%s\t", FILENAME }
    { seen[$1][FILENAME] = $1 } 
    END {
        print ""
        PROCINFO["sorted_in"]="@ind_num_asc"
        for (i in seen) {
            for (j=1; j<=ARGC; j++) {
                printf "%s\t", seen[i][ARGV[j]]
            } 
            print ""
        }
    }
' file{1,2,3,4}
file1   file2   file3   file4       
    1   1           
            2       
3   3               
    4       4       
5       5           
            6       
7                   
glenn jackman
la source
Je l'ai. Réponse mise à jour
glenn jackman
3

Une solution avec bash, join, pasteet mauvais goût:

#! /usr/bin/env bash

if [ $# -lt 3 ]; then exit 1; fi

files=( '' "$@" )

declare -a temps
for ((i=0; i<=$#; i++)); do
    [ $i -eq 0 -o -f "${files[$i]}" ] || exit 1
    temps[$i]=$( mktemp -t "${0##*/}"_$$_XXXXXXXX ) || exit 1
done
trap 'rm -f "${temps[@]}"' EXIT HUP INT QUIT TERM

cat "$@" | sort -u >"${temps[0]}"

TAB=$( printf '\t' )
for ((i=1; i<=$#; i++)); do
    join -j1 -a1 -t"$TAB" "${temps[0]}" <(paste "${files[$i]}" "${files[$i]}") | \
        sed "/^[^$TAB]\$/ s/\$/$TAB/" >"${temps[$i]}"
done

printf '%s' ${files[1]}
for ((i=2; i<=$#; i++)); do
    printf '\t%s' ${files[$i]}
    let j=i-1
    let k=i-2
    join -j1 -t"$TAB" "${temps[$j]}" "${temps[$i]}" >"${temps[$k]}"
    cat "${temps[$k]}" >"${temps[$i]}"
done
printf '\n'

cut -d "$TAB" -f 2- <"${temps[$#]}" | sort -n

À l'exception du dernier sort -n, tout cela fonctionne avec des éléments de texte plutôt qu'avec des nombres, tant que les éléments ne contiennent pas d'onglets (mais TABpeuvent être modifiés en n'importe quel autre séparateur). En outre, cela pourrait être fait avec seulement 3 fichiers temporaires et quelques modifications (mais cela ne ferait qu'augmenter le mauvais goût).

Satō Katsura
la source