#awk
#awk
Вопрос:
У меня есть файл с разделителями табуляции с > 10 000 строк и переменным количеством столбцов в строке (33-35).
Для строк с 34 столбцами я хотел бы объединить столбцы 3-4 в один:
col1 col2 col3 col4 ... col34
index1 tool kit math new
чтобы —>
col1 col2 col3 ... col33
index1 tool kit;math new
Аналогично, для 35 столбцов я хотел бы объединить столбцы 3-5 в один. Я предполагаю, что для этого может быть способ использовать AWK и NF. Какие-либо советы или помощь?
Комментарии:
1. Давайте пока забудем о 35 столбцах и создадим простой пример с меньшим количеством столбцов, показывающий, как именно выглядят входные и желаемые выходные данные.
Ответ №1:
В awk:
NF>5 { # if more than 5 (33) fields
for(i=(NF-5)-1; i>=0; i--) # execute next for once or twice
for(j=3 i; j<=NF; j )
$j=( j<4 i ? $j ";" : "" ) $(j 1) # catenate once or twice on i
NF=5
} $1=$1 # is this a problem?
Запустите его:
$ awk -v OFS='t' -f program.awk karakfa's.txt
col1 col2 col3 col4 col5
1 2 3;4 5 6
1 2 3 4 5
1 2 3;4;5 6 7
Ответ №2:
Вы могли бы сделать это короче, используя цикл for для перебора полей с учетом ваших двух или трех условий, но длинная рука (и более понятная для новичка в awk):
awk 'NF==35{print $1,$2,$3$4$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34,$35} NF==34{print $1,$2,$3$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34} NF==33{$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33}' yourfile.txt
Это полный рот, но это просто распечатка различных комбинаций ваших полей в зависимости от количества полей, присутствующих в записи.
Комментарии:
1. Спасибо! Это очень полезно и имеет смысл. Должен ли я в этом формате предварительно указывать все возможности? Например, исключит ли это строки, где NF < 33? Я хотел бы только специально изменить строки на NF == 34 и NF == 35 и оставить все остальное как есть.
2. Это исключило бы записи, длина которых не составляла 33, 34 или 35 полей. Вы можете изменить это последнее условие на
NF<=33
илиNF!=34 amp;amp; NF!=35
илиNF<34 || NF>35
или что угодно.
Ответ №3:
вот еще awk
один вариант с большей гибкостью. Вместо этого я использовал 5-7 столбцов.
$ cat file
col1 col2 col3 col4 col5
1 2 3 4 5 6
1 2 3 4 5
1 2 3 4 5 6 7
$ awk -v OFS='t' 'NR==1{print; next}
NF>5{$3=$3 $4; t=1}
NF>6{$3=$3 $5; t=2}
t{for(i=4;i<=NF;i ) $i=$(i t);
NF=NF-t; t=0}1' file
col1 col2 col3 col4 col5
1 2 34 5 6
1 2 3 4 5
1 2 345 6 7
Ответ №4:
TXR-реализация парадигмы awk примерно в 270 строках TXR Lisp:
Разминка: объединение базовых столбцов по всему файлу:
$ txr -e '(awk ((>= nf 4) (set [f 2..4] (list `@[f 2];@[f 3]`)))
(t))'
1
1
1 2
1 2
1 2 3
1 2 3
1 2 3 4
1 2 3;4
1 2 3 4 5
1 2 3;4 5
Поля представляют собой список f
, а не специальный синтаксический гаджет, включающий знаки доллара, поэтому они подвержены назначению фрагмента: (set [target-sequence n..m] source-sequence)
заменяет n..m
фрагмент (от n
до m
, исключая m
) target-sequence
на source-sequence
. Индексация начинается с нуля, поэтому для замены третьего и четвертого полей мы обозначаем срез 2..4
: т. е. индексы 2 и 3, не включая 4.
Это (t)
предложение условия-действия без условий, аналогичное Awk 1
: оно запускает неявную (prn)
форму, которая выводит rec
(эквивалент $0
); t
это самооценочный символ, который канонически обозначает логическое значение true, хотя любое значение, отличное от nil
true . Когда f
обрабатывается определенным образом, например, с помощью приведенного выше назначения фрагмента, rec
автоматически воссоздается из полей путем вставки ofs
между ними, точно так же, как перемещение полей в Awk Classic $0
reconstitutions .
Теперь, как насчет обработки первой строки в качестве заголовков для печати, а затем игнорирования:
$ txr -e '(awk ((= nr 1) (prn) (next))
((>= nf 4) (set [f 2..4] (list `@[f 2];@[f 3]`)))
(t))'
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 3 3;4 5
Далее у нас есть требование об изменении диапазона для 34 против 35. Как насчет того, чтобы использовать условие «34 или более столбцов». Диапазон сдвига может быть обработан путем изменения выражения:
[f 2..4]
Для
[f (if (> nf 34) 2..5 2..4))]
но мы можем сделать это унифицированным способом, если мы привяжем условный диапазон к переменной, а затем используем его в нескольких местах. Нам нужно только один раз проверить условие. Результатом является:
$ txr -e '(awk ((= nr 1) (prn) (next))
((>= nf 4) (let ((r (if (> nf 34) 2..5 2..4)))
(set [f r] (list `@{[f r] ";"}`))))
(t))'
col1 col2 ...
col1 col2 ...
1 2 3 4 5 6 7 8 9 0 A B C D E F G H I J K L M N O P Q R S T U V W X Y
1 2 3;4;5 6 7 8 9 0 A B C D E F G H I J K L M N O P Q R S T U V W X Y
1 2 3 4 5 6 7 8 9 0 A B C D E F G H I J K L M N O P Q R S T U V W X
1 2 3;4 5 6 7 8 9 0 A B C D E F G H I J K L M N O P Q R S T U V W X
Внутри квазистрочного литерала, разделенного обратным символом, синтаксис @{[seq range] string}
будет интерполировать фрагмент последовательности, используя строку в качестве разделителя. Мы просто интерполируем диапазон r
, который является тем же диапазоном, который мы удаляем и заменяем результирующей строкой; мы переключаемся r
условно на то, есть ли у нас более 34 столбцов, поэтому тест выполняется только в одном месте и r
используется в двух местах.
Комментарии:
1. Искренне благодарен за объяснение и помощь!!