Как я могу объединить два файла по столбцу с awk? - PullRequest
2 голосов
/ 26 марта 2020

У меня есть два следующих текстовых файла:

file1

-7.7
-7.4
-7.3
-7.3
-7.3

file2

4.823
5.472
5.856
4.770
4.425

И я хочу объединить их рядом, разделив их запятая:

file3

-7.7,4.823
-7.4,5.472
-7.3,5.856
-7.3,4.770
-7.3,4.425

Я знаю, что это легко сделать с помощью paste -d ',' file1 file2 > file3, но я хочу решение, которое позволило бы мне контролировать каждую итерацию, так как мой набор данных большой и мне также нужно добавить другие столбцы в выходной файл. Например:

A,-7.7,4.823,3
A,-7.4,5.472,2
B,-7.3,5.856,3
A,-7.3,4.770,1
B,-7.3,4.425,1

Вот что я получил до сих пор:

awk 'NR==FNR {a[$count]=$1; count+=1; next} {print a[$count] "," $1; count+=1;}' file1 file2 > file3

Вывод:

-7.3,4.823
-7.3,5.472
-7.3,5.856
-7.3,4.770
-7.3,4.425

Я новичок в bash и awk, так что приветствуется подробный ответ:)

Редактировать:
Предположим, у меня есть каталог с парами файлов, заканчивающийся двумя расширениями: .ext1 и .ext2. Эти файлы имеют параметры, включенные в их имена, например, file_0_par1_par2.ext1 имеет свою пару file_0_par1_par2.ext2. Каждый файл содержит 5 значений. У меня есть функция, чтобы извлечь его серийный номер и параметры из его имени. Моя цель - записать в одном файле csv (file_out.csv) значения, присутствующие в файлах, а также параметры, извлеченные из их имен.
Код:

for file1 in *.ext1 ; do
    for file2 in *.ext2 ; do
        # for each file ending with .ext2, verify if it is file1's corresponding pair
        # I know this is extremely time inefficient, since it's a O(n^2) operation, but I couldn't find another alternative
        if [[ "${file1%.*}" == "${file2%.*}" ]] ; then
            # extract file_number, and par1, par2 based on some conditions, then append to the csv file
            paste -d ',' "$file1" "$file2" | while IFS="," read -r var1 var2;
            do
                echo "$par1,$par2,$var1,$var2,$file_number" >> "file_out.csv" 
            done
        fi
    done
done

Ответы [ 5 ]

2 голосов
/ 30 марта 2020

Способ эффективно выполнить то, что описывает ваш обновленный вопрос:

Предположим, у меня есть каталог с парами файлов, заканчивающийся двумя расширениями: .ext1 и .ext2. Эти файлы имеют параметры, включенные в их имена, например, file_0_par1_par2.ext1 имеет свою пару file_0_par1_par2.ext2. Каждый файл содержит 5 значений. У меня есть функция, чтобы извлечь его серийный номер и параметры из его имени. Моя цель - записать в одном файле csv (file_out.csv) значения, присутствующие в файлах, вместе с параметрами, извлеченными из их имен.

for file1 in *.ext1 ; do
    for file2 in *.ext2 ; do
        # for each file ending with .ext2, verify if it is file1's corresponding pair
        # I know this is extremely time inefficient, since it's a O(n^2) operation, but I couldn't find another alternative
        if [[ "${file1%.*}" == "${file2%.*}" ]] ; then
            # extract file_number, and par1, par2 based on some conditions, then append to the csv file
            paste -d ',' "$file1" "$file2" | while IFS="," read -r var1 var2;
            do
                echo "$par1,$par2,$var1,$var2,$file_number" >> "file_out.csv" 
            done
        fi
    done
done

будет (не проверено):

for file1 in *.ext1; do
    base="${file1%.*}"
    file2="${base}.ext2"
    paste -d ',' "$file1" "$file2" |
    awk -v base="$base" '
        BEGIN { split(base,b,/_/); FS=OFS="," }
        { print b[3], b[4], $1, $2, b[2] }
    '
done > 'file_out.csv'

Само выполнение base="${file1%.*}"; file2="${base}.ext2" будет в N ^ 2 раза (с учетом N пар файлов) более эффективным, чем for file2 in *.ext2 ; do if [[ "${file1%.*}" == "${file2%.*}" ]] ; then, а само выполнение | awk '...' будет на порядок эффективнее, чем | while IFS="," read -r var1 var2; do echo ...; done (см. почему используется оболочка-l oop -процесс-текст считается плохой практикой ), так что вы можете ожидать значительного улучшения производительности по сравнению с существующим сценарий.

2 голосов
/ 27 марта 2020

Ваша команда не выполнена:

awk 'NR==FNR {a[$count]=$1; count+=1; next} {print a[$count] "," $1; count+=1;}' file1 file2 > file3

Не используйте $count, но count, начните с счетчика 1 и сбросьте счетчик до 1 при запуске в файле2. Последние два условия могут быть добавлены с помощью FNR==1 {count=1} или {count=FNR}.
Когда count всегда совпадает с FNR, зачем использовать count?

awk 'NR==FNR {a[FNR]=$1; next} {print a[FNR] "," $1; }' file1 file2
2 голосов
/ 26 марта 2020

Вы можете использовать ваши решения с «пастой». Просто добавьте while l oop, например, чтобы взять контроль над каждой итерацией.

paste -d ',' file1 file2 | while IFS="," read -r lineA lineB;
do
    # you can build new file here like you need
    echo "$lineA,$lineB"
done
0 голосов
/ 27 марта 2020
awk 'BEGIN {FS=","} {getline file2_line < "file2.txt"; print $1","file2_line }' file1.txt

Блок begin устанавливает разделитель полей на запятую, но это относится только к данным в file1.txt

Первый оператор в основной части скрипта сохраняет значение этой строки из file2 .txt в переменную с именем file2_line. Эта переменная содержит всю строку из file2.txt, и данные в строке не разделяются на поля обычным способом. Это означает, что если file2.txt также был разделен запятыми, вы, вероятно, захотите использовать функцию разбиения awk, чтобы разбить строку на массив для работы с отдельными полями.

В awk можно объединить просто записывая строковые значения одно за другим, поэтому print $1","file2_line записывает первое поле из первого файла, буквенную запятую и строковое значение для этой строки file2.txt, которую мы сохранили ранее.

0 голосов
/ 27 марта 2020

Два совета, которые могут помочь:

Во-первых, я подозреваю, что скрипт Awk, который делает то, что вы хотите, будет слишком длинным для однострочника. Я написал бы многострочный скрипт, который принимает file1 и file2 в качестве аргументов, и сохранил бы его в файле с именем mymerge.awk или чем-то еще. Вот скелет:

#!/usr/bin/awk -f

BEGIN {
    file1=ARGV[1]; file2=ARGV[2]
}

# The guts of your script go here.

Затем вы можете просто сделать исполняемый скрипт (chmod +x mymerge.awk) и вызывать его из оболочки: mymerge.awk file1 file2. Преимущество этого подхода состоит в том, что ваш скрипт легко читать, повторно использовать и обслуживать.

Второй совет: используйте Awk's getline < file1 для чтения данных из file1 вместо stdin. Аналогично для file2. Чтобы сохранить строки, которые вы только что прочитали, в переменных, вы можете сказать

getline var1 < file1; getline var2 < file2

В Руководстве пользователя Gnu Awk есть подробное описание getline и способы его использования.

Сегодня вечером я не смогу написать и протестировать рабочий скрипт для вас, но я надеюсь, что это поможет вам добиться определенного прогресса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...