Толик Панков
hex_laden
............ .................. ................
Толик Панков [userpic]
Определение номера колонки таблицы по содержимому (заголовку), для вывода ее awk.

Преамбула


awk, удобный инструмент для работы с табличными данными (текстовыми файлами с разделителями), однако, бывают ситуации, когда необходимо определить нужную колонку в таблице не по номеру, а по содержимому.
Например, имеется такая таблица (в виде текстового файла с разделителями табуляцией), скажем, абстрактная зарплатная ведомость:



Задача - вывести колонку с номерами карт (CardID). В простейшем случае, читаем файл с помощью cat, вывод передаем awk и выводим на экран колонку 3.

cat salary1.txt|awk '{print $3}'

Результат:
CardID
6666-6666-6666-0001
6666-6666-6666-0002
6666-6666-6666-0003
6666-6666-6666-0004
6666-6666-6666-0005
6666-6666-6666-0006
6666-6666-6666-0007
6666-6666-6666-0008
6666-6666-6666-0009
6666-6666-6666-0010
6666-6666-6666-0011
6666-6666-6666-0012
6666-6666-6666-0013


Сложнее будет, если место колонки в таблице поменяется, например, в таблицу добавили информацию о банках и воинских званиях сотрудников:



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

Нашел вопрос по этому поводу на toster.ru, заданный примерно год назад, но без ответов, что удивительно. А ведь там такие акулы и киты программирования с Хабра плавают...

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

Решение


1. Вытаскиваем строчку с заголовками grep'ом:

cat salary2.txt|grep -w "CardID"

параметр -w, указывает grep'у вытащить только строки, содержащие слово целиком.

Вывод:
Family Name MilRank BankID CardID Sum

2. Заменяем символы табуляции (\t) на символы перевода строк (\n):

cat salary2.txt|grep -w "CardID"|sed 's/\t/\n/g'

Вывод:
Family
Name
MilRank
BankID
CardID
Sum


Далее, удобства для, буду приводить только следующую команду, без предыдущих.

3. Нумеруем строки с помощью утилиты nl

nl

Вывод:
     1  Family
     2  Name
     3  MilRank
     4  BankID
     5  CardID
     6  Sum


4. grep'ом вытаскиваем строку с номером нужной колонки:

grep -w "CardID"

Вывод:
     5  CardID


5. Получаем номер колонки с помощью awk:

awk '{print $1}'

Вывод:
5
Пункты 3 и 4 можно оптимизировать, заставив grep нумеровать строки с помощью ключа -n:

grep -w -n "CardID"

Вывод:
5:CardID

Тогда для окончательного определения номера, нужно будет задать awk разделитель двоеточие:

awk -F":" '{print $1}'

Вывод:
5

6. Помещаем результат работы всей команды целиком в переменную:

COLNUM=`cat salary2.txt|grep -w "CardID"|sed 's/\t/\n/g'|nl|grep -w "CardID"|awk '{print $1}'`

или

COLNUM=`cat salary2.txt|grep -w "CardID"|sed 's/\t/\n/g'|grep -w -n "CardID"|awk -F":" '{print $1}'`

7. Теперь в awk нужно передать значение переменной из bash скрипта:

Делается это с помощью ключа -v имя=значение
имя - имя переменной, которое будет использовано внутри скрипта awk
значение - значение переменной

cat salary2.txt|awk -v cnum="${COLNUM}" '{print $cnum}'

Вывод:
CardID
6666-6666-6666-0001
6666-6666-6666-0002
6666-6666-6666-0003
6666-6666-6666-0004
6666-6666-6666-0005
6666-6666-6666-0006
6666-6666-6666-0007
6666-6666-6666-0008
6666-6666-6666-0009
6666-6666-6666-0010
6666-6666-6666-0011
6666-6666-6666-0012
6666-6666-6666-0013


Чтобы не выводить сам заголовок, можно добавить sed '1d':

cat salary2.txt|sed '1d'|awk -v cnum="${COLNUM}" '{print $cnum}'

Немного усложним задачу


Предположим, что в файле две таблицы:



Тогда в переменной $COLNUM после выполнения первой команды, окажется значение
5 11, что на самом деле не есть хорошо. Думаю, понятно, из-за чего этот эффект происходит. Нужно добавить команду head -n1, чтобы оставить только одну строчку после первого grep "CardID".

COLNUM=`cat salary3.txt|grep "CardID"|head -n1|sed 's/\t/\n/g'|grep -w -n "CardID"|awk -F":" '{print $1}'`

В следующей команде так же желательно установить awk разделитель "только символ табуляции", чтоб не отреагировал на пятое слово не в таблице (по умолчанию у awk разделитель полей - табуляция и пробел).

cat salary3.txt|awk -v cnum="${COLNUM}" -F "\t" '{print $cnum}'

Ну и можно удалить sed'ом пустые строки и строки, содержащие заголовок CardID:

cat salary3.txt|awk -v cnum="${COLNUM}" -F "\t" '{print $cnum}'|sed '/^$/d'|sed '/CardID/d'

Скрипты целиком


На GitHub
Скачать одним архивом с Mega.nz

UPD:
Нашел обсуждение, наведшее на идею

Это репост с сайта http://tolik-punkoff.com
Оригинал: http://tolik-punkoff.com/2019/03/18/opredelenie-nomera-kolonki-tablitsy-po-soderzhimomu-zagolovku-dlya-vyvoda-ee-awk/

Tags: ,
Comments
(Anonymous)

Освой Excell и не майся дурью.

1. >Excell
Excel пишется не так.

2. Табличка по "типу Экселевской" приведена ради простоты объяснения.
3. Если это шутка такая, спасибо, поржал.