Comparação lado a lado de mais de dois arquivos contendo valores numéricos

8

Eu tenho três arquivos contendo uma sequência classificada de números, um por linha:

file1

1
2
3

arquivo2

1
3
4

arquivo3

1
5

Eu quero "alinhar" esses três arquivos lado a lado como o seguinte:

file1  file2  file3
1      1      1
2      
3      3
       4
              5

Eu tentei com, sdiffmas ele só funciona com 2 arquivos

cheseaux
fonte
Você testa diff3?
Costas
O @Costas diff3não possui esse formato de saída.
Kusalananda
@ Costas Sim, eu testei com diff3e como @ Kusalananda afirmou corretamente, ele não produz essa saída. Além disso, eu estou procurando uma solução genérica (para n arquivos, n> 2)
Cheseaux
Se você comparar linha por linha, por que 5na quinta linha o resultado é o terceiro?
Costas
Eu não comparo linha por linha
cheseaux

Respostas:

6

Você pode processar cada arquivo e imprimir uma linha com algum caractere, por exemplo, Xpara cada número ausente na sequência 1 a max (onde max é o último número desse arquivo), pasteos resultados substituem esse caractere por espaço:

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 ' '

Se um determinado valor estiver ausente de todos os arquivos, você obterá linhas vazias na saída (na verdade elas não estão vazias, elas contêm apenas espaços em branco).
Para removê-los, substitua tr X ' ' por sed '/[[:digit:]]/!d;s/X/ /g' Além disso, se você precisar de um cabeçalho, sempre poderá executar algo assim primeiro:

 printf '\t%s' file1 file2 file3 | cut -c2-
don_crissti
fonte
Eu tive dificuldade para descobrir o significado da ;1parte no final. Estou usando {print $0}, em vez disso, um IMHO um pouco menos enigmático. De qualquer forma, obrigado novamente
cheseaux
5

Uma solução geral com o awk: requer o 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   

Adotou uma abordagem diferente e mais simples, com base no comentário 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
fonte
Entendi. Resposta atualizada
glenn jackman
3

Uma solução com bash, join, paste, e mau gosto:

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

Exceto pelo último sort -n, tudo isso funciona com itens de texto em vez de números, desde que os itens não contenham guias (mas TABpodem ser alterados para qualquer outro separador). Além disso, isso poderia ser feito com apenas 3 arquivos temporários e algumas coisas aleatórias (mas isso apenas aumentaria o mau gosto).

Satō Katsura
fonte