У меня есть 3 файла с разделителями табуляции, как показано ниже:
Файл 1:
1 Hhe.7
2 Hpyl.1
10 Hac.2
Файл 2:
3 Hac.2
15 Hpyl.1
33 Hhe.7
Файл 3:
70 Hpyl.1
23 Hhe.7
9 Hac.2
Как объединить эти файлы (используя командную строку) в один файл, чтобы получить следующий вывод:
1 33 23 Hhe.7
2 15 70 Hpyl.1
10 3 9 Hac.2
В маленьком сценарии Python можно объединить неограниченное количество файлов:
#!/usr/bin/env python3
import sys
#read the files, split the lines for reordering
lines = sum([[l.strip().split() for l in open(f).readlines()]\
for f in sys.argv[1:]], [])
# get the unique last sections
values = set(map(lambda x:x[1], lines))
# combine them with the combined first sections
newlist = [[y[0] for y in lines if y[1]==x]+[x] for x in values]
for l in newlist:
print(("\t").join(l))
Скопируйте его в пустой файл, сохраните его как merge.py
, выполните его командой:
python3 /path/to/merge.py file1, file2, file3 (file4, file5 etc.)
Вывод на Ваших файлах в качестве примера:
10 3 9 Hac.2
1 33 23 Hhe.7
2 15 70 Hpyl.1
Как упомянуто, количество файлов в принципе неограниченно, если я добавляю 4-й файл:
40 Hhe.7
50 Hpyl.1
60 Hac.2
и выполненный команда:
python3 /path/to/merge.py file1, file2, file3, file4
вывод будет:
40 23 33 1 Hhe.7
50 70 15 2 Hpyl.1
60 9 3 10 Hac.2
Классический инструмент UNIX для этого join
:
NAME
join - join lines of two files on a common field
SYNOPSIS
join [OPTION]... FILE1 FILE2
DESCRIPTION
For each pair of input lines with identical join fields, write a line
to standard output. The default join field is the first, delimited by
blanks.
Однако join
i) нуждается в его входе, который будет отсортирован, для работы и ii) может только иметь дело с 2 файлами. Так, Вы могли сделать что-то ужасное и неэлегантное как:
Вид каждый файл на втором поле и сохраняет как новый файл
sort -k2 file1 > sorted1
sort -k2 file2 > sorted2
sort -k2 file3 > sorted3
Файлы 1 и 2 соединения в новый файл и затем присоединяются к третьему
$ join -j2 --nocheck-order sorted1 sorted2 > newfile
$ join -o 1.2,1.3,2.1,1.1 -1 1 -2 2 --nocheck-order newfile sorted3
10 3 9 Hac.2
1 33 23 Hhe.7
2 15 70 Hpyl.1
Используемые опции:
-1 FIELD
join on this FIELD of file 1
-2 FIELD
join on this FIELD of file 2
-j FIELD
equivalent to '-1 FIELD -2 FIELD'
--nocheck-order
do not check that the input is correctly sorted
-o FORMAT
obey FORMAT while constructing output line
FORMAT is one or more comma or blank separated
specifications, each being 'FILENUM.FIELD' or '0'.
Так, та команда присоединится на 1-м поле первого файла и 2-м поле второго файла, и распечатает 2-е поле первого файла (1.2
), затем третье поле первого файла (1.3
), первое поле второго файла (2.1
) и 1-е поле первого файла (1.1)
.
С другой стороны, Вы могли объединить всю вещь в одной великолепно сложной команде:
$ join -o 1.1,2.2,2.3,2.1 -1 2 -2 1 --nocheck-order <(sort -k2 file3) \
<(join -j2 --nocheck-order <(sort -k2 file1) <(sort -k2 file2))
9 10 3 Hac.2
23 1 33 Hhe.7
70 2 15 Hpyl.1
Если Вам не нравится тайная командная-строка-fu, можно всегда использовать немного сценария:
$ awk '{a[$NF]=$1"\t"a[$NF];} END{for(i in a){print a[i],i}}' file{1,2,3}
23 33 1 Hhe.7
9 3 10 Hac.2
70 15 2 Hpyl.1
Это - задание для join
, который может присоединиться на общих полях двух файлов:
$ join -11 -22 -o1.2,1.3,2.1,0 <(join -j2 <(sort -k2,2 f1.txt) <(sort -k2,2 f2.txt)) <(sort -k2,2 f3.txt)
10 3 9 Hac.2
1 33 23 Hhe.7
2 15 70 Hpyl.1
Как join
берет только два входных файла за один раз, мы использовали замену процесса (<()
) для передачи вывода join
- луг сначала два файла с третьим.
С awk
:
awk -F"\t" -v OFS="\t" '!(a[$2]){a[$2]=$1;next}
{a[$2]=a[$2]"\t"$1}
END{
for ( i in a) {
print a[i],i
}
}'
Ответ от:
Следующий сценарий должен сделать, внешнее объединение на столбце (поле) 1 всех файлов с разделением табуляцией передало как аргументы. Это использует команду соединения, которая делает внешнее объединение на отсортированных файлах, 2 файла за один раз.
Это присоединится к каждой строке в файлах, включая строки заголовка. Если Вы хотите, чтобы заголовки были исключены, изменили два
sort
команды к чему-то, что производит отсортированный файл, который опускает их.#!/bin/sh if test $# -lt 2 then echo usage: gjoin file1 file2 ... exit 1 fi sort -t $'\t' -k 1 "$1" > result shift for f in "$@" do sort -t $'\t' -k 1 "$f" > temp join -1 1 -2 1 -t $'\t' result temp > newresult mv newresult result done cat result rm result temp
Если у Вас есть более старая оболочка,
$'\t'
не будет заменен вкладкой, таким образом, необходимо будет использовать 'ВКЛАДКУ', куда Вы помещаете литеральную вкладку между кавычками.Оптимизация возможна если, вместо
/bin/sh
, можно использовать современную оболочку, такую как удар или ksh; например, строкиsort -t $'\t' -k 1 "$f" > temp join -1 1 -2 1 -t $'\t' result temp > newresult
может быть заменен
join -1 1 -2 1 -t $'\t' result <(sort -t $'\t' -k 1 "$f") > newresult