У меня нет большого опыта работы с awk, поэтому я пытаюсь преодолеть одну проблему с ним. У меня есть два файла с именами file1.txt и file2.txt. File1.txt:
20 101 1 2 3 4
20 102 5 6 7 8
20 108 3 3 3 3
Файл2.txt:
20 100 99 99 99 99
20 101 11 22 33 44
20 103 55 66 77 88
После первых двух столбцов в каждом из файлов всегда следует 4 значения.
Я пытаюсь объединить эти файлы в один. Я объединяю их по первому и второму столбцам.
В полученном файле должно быть 10 столбцов. Первые два — ключевые, следующие 4 столбца — значения из первого файла, а последние 4 — из второго.
В результирующем файле каждая запись из первого файла, не имеющая соответствия со вторым файлом (и наоборот), будет иметь дополнительные нули, представляющие отсутствующие значения.
Все разделяется пробелами.
Результат должен выглядеть так:
20 100 0 0 0 0 99 99 99 99
20 101 1 2 3 4 11 22 33 44
20 102 5 6 7 8 0 0 0 0
20 103 0 0 0 0 55 66 77 88
20 108 3 3 3 3 0 0 0 0
Это мой скрипт awk, который я модифицировал, найдя его в Интернете:
BEGIN {
OFS=" "
}
{
i=$1 OFS $2 #Making key out of first and second field of the first file
}
NR==FNR {
A[i]=$0 #Saving records from first file to an array using first two columns as index
next
}
#Next part assumes that I'm reading file2.txt
i in A {
printf "%s",A[i] #Here, I have a match with first file, and I want to print the joined record I saved from file1.txt
print $3,$4,$5,$6 #In order to print a joined record, after printing record from first file, I'm printing columns from the second file
delete A[i]
next
}
{ #Here I print records from file2.txt that don't have a match with file1.txt, and put zeroes to fill missing values
print 0,0,0,0,$3,$4,$5,$6
}
END { #In the END block I'm printing everything from file1.txt that doesn't have a match and print zeroes aftewards to fill missing values
for (i in A) { printf "%s",A[i]; print 0,0,0,0 }
}
Результат сортируется по второму столбцу, и все пропущенные значения заполняются нулями. Однако, результат, который я сейчас получаю, выглядит так:
20 100 0 0 0 0 99 99 99 99
11 22 33 443 4
20 103 0 0 0 0 55 66 77 88
20 108 3 3 3 3 0 0 0 0
0 0 0 0 6 7 8
Несмотря на то, что файл не отсортирован (я всегда могу использовать sort -k 2), некоторые строки не печатаются так, как я предполагал, и я не могу объяснить, почему он не может нормально вывести элемент массива A. Я пробовал разные вещи, например, временное изменение ORS (вообще никакого вывода) или использование print вместо printf (результат тоже выглядит странно).
Из-за отсутствия опыта возникают дополнительные вопросы:
Разумно ли использовать awk для выполнения этой задачи? Я пробовал использоватьприсоединиться, но в итоге застрял, потому что не смог напечатать столбец с символом новой строки в конце. Может быть, скрипт на Python будет полезнее?
Учитывая, что я буду использовать очень большие файлы для слияния, разумно ли использовать массивы с точки зрения памяти?
Заранее спасибо!
решение1
awk '!second { file1vals[$1 FS $2]=$0 }
second { print (($1 FS $2 in file1vals)?file1vals[$1 FS $2]: $1 FS $2 FS "0 0 0 0") FS $3, $4, $5, $6;
delete file1vals[$1 FS $2]
}
END{ for(x in file1vals) print file1vals[x], "0 0 0 0" }' file1 second=1 file2
это будет работать до тех пор, пока есть достаточно памяти для загрузки первогофайл1в память.
В первом блоке !second {...}
, который запускается только тогда, когда это первый файл, и мы загружаемфайл1в связанный массив по паре столбцов 1- го и 2- го в качестве ключей для массива.
Во втором блоке second {...}
, который запускается только тогда, когда это второй файл, мы выводим объединенные строки, те, которые имеют совпадающие ключи в обоих файлах, в противном случае мы выводим ключи и нули, а затем остальные столбцы изфайл2; затем мы также удаляем ключи из массива, в delete file1vals[$1 FS $2]
котором они существовали в обоих файлах.
В последнем блоке в END мы выводим оставшиеся несовпадающие ключи, относящиеся кфайл1.