并排比较两个以上包含数值的文件


8

我有三个文件,其中包含排序的数字序列,每行一个:

文件1

1
2
3

文件2

1
3
4

文件3

1
5

我想并排“对齐”这三个文件,如下所示:

file1  file2  file3
1      1      1
2      
3      3
       4
              5

我尝试过,sdiff但仅适用于2个文件


你测试diff3吗?
Costas

@Costas diff3没有该输出格式。
库萨兰达

@Costas是的,我已经使用diff3@Kusalananda进行了正确的测试,它不会产生该输出。另外,我正在寻找通用解决方案(用于n个文件,n> 2)
cheseaux

如果逐行比较,为什么5结果在第五行而不是第三行?
Costas

我不会逐行比较
cheseaux'July 11''16

Answers:


6

您可以处理每个文件并打印一行带有某些字符的行,例如,X对于序列1- max(其中max是该文件中的最后一个数字)中的每个缺失数字,paste结果然后用空格替换该字符:

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

如果所有文件中都缺少某个值,则输出中将出现空行(实际上它们不是空的,它们仅包含空白)。
要删除它们,请替换tr X ' 'sed '/[[:digit:]]/!d;s/X/ /g' Also,如果您需要标头,则始终可以先运行以下命令:

 printf '\t%s' file1 file2 file3 | cut -c2-

不过,我很难弄清楚最后的;1部分是什么意思。我使用的是,{print $0}而不是神秘的恕我直言。无论如何,再次感谢
cheseaux '16

5

awk的一般解决方案:需要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   

根据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                   

得到它了。答案已更新
格伦·杰克曼

3

一个解决方案bashjoinpaste,和不好的味道:

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

除了last以外sort -n,所有这些都适用于任何文本项而不是数字,只要这些项不包含制表符(但TAB可以更改为任何其他分隔符)即可。另外,只需3个临时文件和一些混排的内容即可完成此操作(但这只会增加不好的味道)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.