Это кажется простой задачей для SQL.
csv
+ sql
== csvsql
изcsvkit
!
Вы можете установить его через pip.
Прежде чем вы сможете начать, вам может потребоваться исправить файл перед выполнением этого :Удалите пустую строку и пробелы в конце каждой строки:
sed -i.bak '2d;s/[[:blank:]]*$//' file
Вы можете пройти половину пути с помощью:
csvsql \
-d' ' -S \
--query 'select cable,grnd_sta || "-" || chan_pdu,group_concat(fldr) from file group by cable' \
file \
| csvformat -D' '
-t
вместо -d' '
, если ваш файл разделен табуляцией. csvformat -D' '
для получения вывода с разделителями -, так как csvsql
выводит только разделители с запятыми -. С awk
работать проще, но csvsql заключит последний столбец в кавычки, а нам это не нужно! Выход:
cable "grnd_sta || ""-"" || chan_pdu" group_concat(fldr)
1 97-108 79,817,946
3 145-156 64,66,117,240,255,263,291
Отсюда вы можете продолжить использованиеawk
:
... \
| awk 'NR>1{printf "Cable %s\n\n%s\n%s\n\n",$1,$2,$3}' \
| head -n-1
Выход:
Cable 1
97-108
79,817,946
Cable 3
145-156
64,66,117,240,255,263,291
Существует несколько способов извлечения необходимых данных.
Если вы видите каждую строку как ряд полей, разделенных пробелом, то самый простой инструмент, который вы можете использовать для решения вашей проблемы, будетcut
:
$ cut -d ' ' -f 1,2,4,6 file
file.txt 4 11 102
file_1.txt 5 10 100
file_3_2.txt 0 10 100
Это извлекает из файла 1-е, 2-е, 4-е и 6-е поля с разделителями -пробелами.
Аналогично, сawk
:
$ awk '{ print $1,$2,$4,$6 }' file
file.txt 4 11 102
file_1.txt 5 10 100
file_3_2.txt 0 10 100
Это то же самое, но немного больше. Инструмент awk
обрабатывает свой ввод как поток записей , состоящих из полей . По умолчанию запись — это просто строка, а поле — любой непустой -текст, разделенный пробелами (пробелами или символами табуляции, возможно более одного ). Вывод создается по одной записи за раз (по умолчанию, по строке за раз ), а поля разделены одним пробелом.
Чуть более сложным подходом было бы вырезать первое поле из остальных данных, удалить все, что является буквой в остальных данных, а затем снова вставить их вместе.
$ paste -d ' ' \
<( cut -d ' ' -f 1 file ) \
<( cut -d ' ' -f 2- file | tr -s '[:alpha:]' '[ *]' )
file.txt 4 11 102
file_1.txt 5 10 100
file_3_2.txt 0 10 100
Это несколько неэлегантно, потому что ввод считывается дважды. Используемая команда tr
изменит все буквенные символы на пробелы, а затем «сожмет» (с помощью-s
)любой набор из нескольких последовательных пробелов в одиночные пробелы.
Команда paste
получает два входных потока и объединяет их построчно с пробелом в -между ними. Первый поток обеспечивается заменой процесса (<( ... )
), запускающей команду cut
, которая извлекает из данных только первый столбец. Второй поток предоставляется другим подстановочным процессом, выполняющим cut
для извлечения всех столбцов, начиная со столбца 2, и который затем заменяет буквенные символы пробелами в этих столбцах, используя tr
, как описано выше.
Неясно, насколько конкретно ваш пример относится к вашей проблеме. Предполагая, что это буквально то, о чем вы спрашиваете (, что заставляет меня беспокоиться, что это может быть классное упражнение ), тогда вы можете воспользоваться тем фактом, что вам нужно удалить только 3 известные строки и что вы хотите получить только одиночные пробелы между символами, чтобы создать довольно короткую команду sed. В этом примере используется BSD sed, поставляемый с MacOS, ввод осуществляется в файл input.txt, а вывод — в стандартный вывод.
sed -E -e 's/ (minutes|seconds|msec)//g' < input.txt
Это будет соответствовать одному пробелу, за которым следует одна из ваших целевых строк, а затем ничего не заменит. Буква «g» в конце говорит о многократном применении к одной и той же строке.
Одним из решений является удаление всех «одного пробела», за которым следуют «не-числовые слова»:
$ sed -E 's/ \<[^0-9]*\>//g' file
file.txt 4 11 102
file_1.txt 5 10 100
file_3_2.txt 0 10 100