LINUX.ORG.RU

Можно ли в awk собрать значения из нескольких строк в одну в определенном порядке

 ,


0

1

Здравствуйте. Есть 2 файла:

cat attribute.txt

6647|Вид обуви|кеды
6646|Страна бренда|Россия
6645|Страна пошива|Россия
6645|Страна бренда|Россия
6646|Вид обуви|ботинки
...

и

cat product.txt

6645|значение3|значение4
6646|значение1|значение2
6647|значение5|значение6

В первом в хаотичном порядке перечислены названия атрибутов и их значения для различных товаров (id товара в 1 столбце). Во втором - собственно сами товары, где 1 товару соответствует 1 строка. скриптом собираю файл, в котором хаотичные атрибуты будут упорядочены по определенным столбцам второго файла в виде |Имя атрибута|Значение атрибута|. Если нет атрибута для товара - то пустое значение. Вот так, для вышеописанного примера:

6645|значение3|значение4|Страна пошива|Россия|Страна бренда|Россия|||
6646|значение1|значение2|||Страна бренда|Вид обуви|ботинки
6647|значение5|значение6|||||Вид обуви|кеды

Видов атрибутов в attribute.txt - много, в product.txt нужно поместить 10 из них, указанные в переменных в начале скрипта

Сделал скрипт:

#!/bin/bash
attribute=attribute.csv
product=product.csv
atr1='Страна пошива'
atr2='Страна бренда'
atr3='Вид обуви'
atr4='...'
atr5='...'
atr6='...'
atr7='...'
atr8='...'
atr9='...'
atr10='...'

awk -F "|" -v attribute=$attribute -v atr1=$atr1 '
BEGIN {OFS="|";
        while(getline < attribute) {
                name[$1]=$2;
                value[$1]=$3
                }
               };
        $2 == atr1 {
                $40=name[$1];
                $41=value[$1]
                     };
          { print $1 "|" $2 "|" $3 "|" $40 "|" $41 }' $product > temp01

... 
и так еще 8 awk
...

awk -F "|" -v attribute=$attribute -v atr10=$atr10 '
BEGIN {OFS="|";
        while(getline < attribute) {
                name[$1]=$2;
                value[$1]=$3
                }
               };
        $2 == atr10 {
                $40=name[$1];
                $41=value[$1]
                     };
          { print $1 "|" $2 "|" $3 "|" .... "|" $21 "|" $22 "|" $40 "|" $41 }' temp10 > $product

Такое решение работает, но очевидно, что оно не оптимально. Можно ли это сделать одним awk, а не 10?

А чо не на перле, ванлайнером?

vvviperrr ★★★★★
()

Можно. В begin грузишь все атрибуты в многомерный массив, потом поточно обрабатываешь файл с продуктами и для каждого по id дергаешь нужный атрибут из твоих 10

yoghurt ★★★★★
()
Ответ на: комментарий от Neuro75
bash ~/code/awk$ awk -f merge.awk -vattrsfile=attr.txt -vkeysfile=keys.txt prod.txt
6645|значение3|значение4|Россия|Россия||||||||
6646|значение1|значение2||Россия|ботинки|||||||
6647|значение5|значение6|||кеды|||||||
bash ~/code/awk$ cat attr.txt
6647|Вид обуви|кеды
6646|Страна бренда|Россия
6645|Страна пошива|Россия
6645|Страна бренда|Россия
6646|Вид обуви|ботинки
bash ~/code/awk$ cat keys.txt
Страна пошива
Страна бренда
Вид обуви
bash ~/code/awk$ cat prod.txt
6645|значение3|значение4
6646|значение1|значение2
6647|значение5|значение6
bash ~/code/awk$ cat merge.awk
BEGIN {
  FS="|"
  OFS="|"
  while (getline < attrsfile) {
    attrs[$1, $2] = $3
  }
  while (getline < keysfile) {
    keys[k++] = $0
  }
}
{
  id = $1
  print $1, $2, $3, \
    attrs[id, keys[0]], \
    attrs[id, keys[1]], \
    attrs[id, keys[2]], \
    attrs[id, keys[3]], \
    attrs[id, keys[4]], \
    attrs[id, keys[5]], \
    attrs[id, keys[6]], \
    attrs[id, keys[7]], \
    attrs[id, keys[8]], \
    attrs[id, keys[9]]
}
yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Имейте в виду, что этот код использует GNU-расширение getline и не будет работать в других реализациях, таких как mawk

annulen ★★★★★
()
Ответ на: комментарий от yoghurt
bash ~/code/awk$ awk -f merge.awk -vattrsfile=attr.txt -vkeysfile=keys.txt prod.txt
6645|значение3|значение4|Россия|Россия||||||||
6646|значение1|значение2||Россия|ботинки|||||||
6647|значение5|значение6|||кеды|||||||
bash ~/code/awk$ gawk -f merge.awk -vattrsfile=attr.txt -vkeysfile=keys.txt prod.txt
6645|значение3|значение4|Россия|Россия||||||||
6646|значение1|значение2||Россия|ботинки|||||||
6647|значение5|значение6|||кеды|||||||
bash ~/code/awk$ original-awk -f merge.awk -vattrsfile=attr.txt -vkeysfile=keys.txt prod.txt
6645|значение3|значение4|Россия|Россия||||||||
6646|значение1|значение2||Россия|ботинки|||||||
6647|значение5|значение6|||кеды|||||||
bash ~/code/awk$ mawk -f merge.awk -vattrsfile=attr.txt -vkeysfile=keys.txt prod.txt
6645|значение3|значение4|Россия|Россия||||||||
6646|значение1|значение2||Россия|ботинки|||||||
6647|значение5|значение6|||кеды|||||||
yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

bash ~/code/awk$ cat keys.txt Страна пошива Страна бренда Вид обуви

А можно сделать так, чтобы keys не в отдельном файле определялись, а в самом скрипте:

#!/bin/bash
attribute=attribute.csv
product=product.csv
atr1='Страна пошива'
atr2='Страна бренда'
atr3='Вид обуви'
atr4='...'
atr5='...'
atr6='...'
atr7='...'
atr8='...'
atr9='...'
atr10='...'
Я пытался по разному приведенный пример запустить с atr, описанными в скрипте, но что-то не получается.

Neuro75
() автор топика
Ответ на: комментарий от Neuro75

В скрипте вместо чтения keys из файла заполни его руками. Ключ - индекс, значение - твой атрибут, что хочешь вывести.

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt
#!/bin/bash
attribute=attribute.csv
product=product.csv
atr1='Страна пошива'
atr2='Страна бренда'
atr3='Вид обуви'
atr4='...'
atr5='...'
atr6='...'
atr7='...'
atr8='...'
atr9='...'
atr10='...'

awk -F'|' -v atr1="$atr1" -v atr2="$atr2" -v atr3="$atr3" -v atr4="$atr4" -v atr5="$atr5" -v atr6="$atr6" -v atr7="$atr7" -v atr8="$atr8" -v atr9="$atr9" -v atr10="$atr10" -v attribute=$attribute
BEGIN {
  FS="|"
  OFS="|"
  while (getline < attribute) {
    attrs[$1, $2] = $3
  }
  while (getline < keysfile) {
    keys[k++] = $0
  }
}
{
  id = $1
  print $1, $2, $3, \
    attrs[id, keys[0]], \
    attrs[id, keys[1]], \
    attrs[id, keys[2]], \
    attrs[id, keys[3]], \
    attrs[id, keys[4]], \
    attrs[id, keys[5]], \
    attrs[id, keys[6]], \
    attrs[id, keys[7]], \
    attrs[id, keys[8]], \
    attrs[id, keys[9]]
} ' $product > out.txt

Не могу понять куда atr1...atr10 написать и, самое главное, что делать с

  while (getline < keysfile) {
    keys[k++] = $0
  }

Neuro75
() автор топика

Вот решение на python, если подходит (product_attrs нужно разместить в порядке, который нужен в записи):

import csv
from collections import defaultdict

product_attrs = ['Страна пошива', 'Страна бренда', 'Вид обуви', '4', '5', '6', '7', '8', '9', '10']
result_products = []

with open('attributes.txt') as attrs_file, open('products.txt') as products_file:
    attrs = defaultdict(dict)
    # format all attrs as {'attr': {'product_id': 'value'}}
    for attr_line in attrs_file.read().splitlines():
        id, attr, value = attr_line.split('|')
        attrs[attr][id] = value
    for product_line in products_file.read().splitlines():
        id, first_value, second_value = product_line.split('|')
        attrs_of_products = [id, first_value, second_value]
        for attr in product_attrs:
            if attrs.get(attr) and attrs[attr].get(id):
                attrs_of_products.extend([attr, attrs[attr][id]])
            else:
                attrs_of_products.extend(['', ''])
        result_products.append(attrs_of_products)


with open('result.csv', 'w') as result_file:
    writer = csv.writer(result_file, delimiter='|', quoting=csv.QUOTE_NONE)
    for line in result_products:
        writer.writerow(line)
6645|значение3|значение4|Страна пошива|Россия|Страна бренда|Россия||||||||||||||||
6646|значение1|значение2|||Страна бренда|Россия|Вид обуви|ботинки||||||||||||||
6647|значение5|значение6|||||Вид обуви|кеды||||||||||||||
conformist ★★★
()
Ответ на: комментарий от conformist

Вот, это верный подход. Ведь всяких там awk и sed на компьютере может и не быть. А python сегодня есть везде.

EXL ★★★★★
()

Загоняешь в sqlite3 и делаешь запросы SELECT какие только в голову придут. А всё это с башем, оком, седом и пр. утилем — малолетнее дрочево

futurama ★★★★★
()
Ответ на: комментарий от EXL

Не уловил немного. ОП задачу решает, а не упражняется в awk. Так то я сам совсем не против этих инструментов и пользуюсь ими, но в данном случае я бы взял что-то поудобнее.

conformist ★★★
()
Ответ на: комментарий от conformist

Я просто делаю скрипт, который закину на дебианский сервер для выполнения по крону.

Скрипт из таблиц файла делает фид, который потом фейсбук для фейсбук-магазина забирает. Задача несложная, рутинная и для выполнения по расписанию одного и того же скрипт - самое то.

Алгоритм выполнения выглядит так: mysqldamp'ом дернуть из БД нужные таблицы, из sql сделать текст с разделителями, обработать как надо и положить в место для забора.

В bashe вся эта работа делается тремя командами grep/sed/awk, которые я в небольшом, нужном для выполнения задачи объеме, освоил (ну если можно это так назвать). Неоптимально, где-то возможны более быстрые решения, но с грехом пополам работает.

В полном объеме скрипт сейчас строк 400 занимает, из них около 100 - это эти 10 awk. Даже визуально выглядит ужасающе. Вот и думал оптимизировать как-то.

Python и пр. я не умею. В ближайших планах освоить. И опять-таки для решения небольшого круга прикладных задач обработки данных. Но в готовый скрипт я питоновский кусок не засуну, а переписать уже написанное полностью на питоне не смогу, пока не разберусь что это за зверь вообще.

Neuro75
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.