Создание категории, если она не существует, распечатать текущие данные, если она существует
У меня есть несколько текстовых файлов со строками категорий и числовыми данными для каждой категории. Хотя существует ограниченное количество категорий, скажем, от a до g, не все файлы имеют каждую категорию, например:
file1:
a 199
b 20
c 70
e 1
f 200
file2:
a 11
b 103
c 232
d 92
f 7
g 201
file3:
a 100
b 120
c 9
d 20
e 33
f 123
g 9191
Я хотел бы создать выходные файлы так, чтобы, если категория уже существовала, воспроизводилась строка, но если категория не существовала, категория создавалась с числовым значением 0. Я подумал об использовании
$ awk '$1 ~ /category/ {print $0}'
для категорий, которые уже существуют, но я не уверен, как представить новые.
Любое решение, даже если оно не с awk
, будет оценено.
Благодарю.
3 ответа
Допустим, у вас также есть файл с категориями:
$ cat categories
a
b
c
d
e
f
g
Предположим также, что все категории являются простыми словами без пробелов и что все ваши файлы отсортированы по столбцу категории. РЕДАКТИРОВАТЬ: На самом деле, любая строка категории идет, но вам будет лучше использовать разделитель, как табуляцию или запятую или что-то в ваших файлах, в этом случае добавьте -t ","
к join
Команда ниже (если вы используете запятые, которые есть). Смотрите конец ответа для примера.
Тогда это решит вашу проблему:
$ join -a 1 -o 1.1,2.2 -e 0 categories file1
a 199
b 20
c 70
d 0
e 1
f 200
g 0
join
Утилита выполняет операцию реляционного соединения над первым столбцом (по умолчанию) двух файлов. Здесь мы кормим его categories
файл и первый пример файла.
-a 1
опция говорит "дай мне все строки из первого входного файла (categories
), даже если они не совпадают со строками во втором файле (file1
)".
-o 1.1,2.2
опция говорит: "Я хотел бы видеть столбец 1 из первого файла и столбец 2 из второго файла в выводе".
-e 0
опция говорит "замените все пропущенные значения на 0
".
РЕДАКТИРОВАТЬ: Помните, что оба входных файла join
должны быть отсортированы по полю, которое мы используем для выполнения объединения (первое поле всех файлов в этом случае, так что просто sort -o file file
на каждый файл сделаю). Если файлы не были отсортированы, то join
потребуется прочитать их в память, и, как и многие другие инструменты Unix, это не так, как это было реализовано. Вместо этого это быстрый и эффективный инструмент памяти, который предъявляет лишь немного более строгие требования к своим входным данным. Эти ограничения легко обрабатываются другими инструментами (sort
). Это философия Unix в действии.
РЕДАКТИРОВАТЬ: Пример с несколькими словами категории:
$ cat categories
a category
b nice
c at
d cide
e ffective
f ull of gas
g one
$ cat file1
a category,199
b nice,20
c at,70
e ffective,1
f ull of gas,200
$ join -t "," -a 1 -o 1.1,2.2 -e 0 categories file1
a category,199
b nice,20
c at,70
d cide,0
e ffective,1
f ull of gas,200
g one,0
С помощью file1
В качестве примера:
$ awk '{a[$1]=$2} END{split("abcdefg",b,//); for (i in b) print b[i],a[b[i]]+0}' file1
a 199
b 20
c 70
d 0
e 1
f 200
g 0
Как это устроено
a[$1]=$2
Для каждой прочитанной строки мы сохраняем значение в столбце в ассоциативном массиве
a
с категорией (столбец 1) в качестве ключа.END{split("abcdefg",b,//); for (i in b) print b[i],a[b[i]]+0}
После того, как мы закончили чтение файла, мы создаем массив
b
который имеет в качестве значений все категории. таким образомb[1]
являетсяa
а такжеb[3]
являетсяc
, так далее.Далее для каждого элемента в
b
распечатываем сохраненное в массиве значениеa
, Если значение не было сохранено вa
, затем awk дает нам значение по умолчанию. Чтобы убедиться, что значение по умолчанию равно нулю, в отличие от пустой строки, мы добавляем0
к значению вa
что заставляет awk рассматривать значение вa
как число.
Обработка многобуквенных категорий
Давайте рассмотрим этот тестовый файл:
$ cat file2
category1 1
category2 2
Теперь давайте проанализируем его по трем категориям:
$ awk '{a[$1]=$2} END{split("category1_category2_category3",b,"_"); for (i in b) print b[i],a[b[i]]+0}' file2
category1 1
category2 2
category3 0
Если вы хотите сохранить заказ:
awk 'BEGIN{
split("a b c d e f g",p);
i=1
}
p[i]!=$1{
print p[i],0;
i++
}
{
i++;
print
}
END{
for (j=i;j<=7;j++)print p[j],0
}' file