Сравнение бок о бок с более чем двумя файлами, содержащими числовые значения

У меня есть три файла, содержащие отсортированную последовательность чисел, по одной в строке:

file1

1 2 3 

file2

 1 3 4 

file3

 1 5 

Я хочу «выровнять» эти три файла бок о бок, как показано ниже:

 file1 file2 file3 1 1 1 2 3 3 4 5 

Я пробовал с sdiff но он работает только с 2 файлами

  • Как сохранить изменения в моем vim-буфере в качестве файла патча?
  • Есть ли способ определить предпочтительный инструмент слияния или diff в Unix?
  • Тихий результат с двумя идентичными файлами в diff: как их показать?
  • перечислить самый старый файл в каталогах в цикле
  • Как удалить дублированные каталоги, содержащие одни и те же файлы?
  • diff --git неизвестный вариант
  • Вывод различных строк при сравнении двух файлов
  • Различные результаты OS X / Linux с «find PATH -mount \ (-type f -o-type d \) -print0 | LC_ALL = C sort --zero-terminated> OUTPUT.txt "
  • 3 Solutions collect form web for “Сравнение бок о бок с более чем двумя файлами, содержащими числовые значения”

    Вы можете обрабатывать каждый файл и печатать строку с некоторым символом, например 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' Также, если вам нужен заголовок, вы всегда можете запустить что-то вроде этого:

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

    Общее решение с 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 

    Взял другой и более простой подход к этому, основываясь на комментарии Дона:

     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 

    Решение с bash , join , paste и bad taste:

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

    За исключением последнего sort -n , все это работает с любыми текстовыми элементами, а не с числами, если элементы не содержат вкладок (но TAB можно изменить на любой другой разделитель). Кроме того, это можно сделать только с тремя временными файлами и некоторыми перетасовками вокруг (но это только увеличит плохой вкус).

    Linux и Unix - лучшая ОС в мире.