
У меня есть файл, который выглядит следующим образом:
1
4 5 6 7 19
20
22
24 26 27
29
30
31
32
34
40
50
56
58
100
234 235 270 500
1234 1235 1236 1237
2300
2303
2304
2307
2309
Как видно, есть некоторые строки с более чем 1 столбцом, а есть и с одним столбцом. Я бы хотел объединить строки с одним столбцом так, чтобы в каждой объединенной строке было не более 4 столбцов. Поэтому вывод должен выглядеть следующим образом:
1
4 5 6 7 19
20 22
24 26 27
29 30 31 32
34 40 50 56
58 100
234 235 270 500
1234 1235 1236 1237
2300 2303 2304 2307
2309
Есть ли какие-нибудь предложения, как это сделать, учитывая, что реальных данных много?
решение1
Немного идиоматично, но работает с gnu awk:
awk '{printf "%s",(NF==1?$0 FS:(c==0?"":RS) $0 RS)} \
{(NF==1?++c:c=0)} \
c==4{printf "\n";c=0} \
END{printf "\n"}' file
#Output
1
4 5 6 7 19
20 22
24 26 27
29 30 31 32
34 40 50 56
58 100
234 235 270 500
1234 1235 1236 1237
2300 2303 2304 2307
2309
Объяснение:
Переменные awk:
NF=Количество полей
FS=Разделитель полей = пробел по умолчанию
RS=Разделитель записей = новая строка по умолчанию.
c=счетчик
Линия 1: {printf "%s",(NF==1?$0 FS:(c==0?"":RS) $0 RS)}
: вложенные тернарные операции if
#Single ternary if operation:
condition?true action:false action
#Nested if operations:
condition1?true action 1:(condition2:true action2:false action2) #nested ternary if operations
-------------------------[ ^ false action1 ^ ]
Это можно объяснить следующим псевдокодом:
if NF==1 then print $0 and print FS
else (if c==0 then print "" else print RS) and print $0 and print RS again
Строка 2: {(NF==1?++c:c=0)}
: Еще одна троичная операция if, которая может быть выражена как:
If NF==1 (line has one field)
then increase counter c by one
else reset counter c.
Строка 3: c==4{printf "\n";c=0}
Классический синтаксис awk:condition{action}
If counter c==4 then print a new line and reset counter c
Строка 4: END{printf "\n"}' file
: Это просто печатает новую строку в конце скрипта.
решение2
sed
Чтобы получить желаемое, вы можете использовать :
sed -e '
/./!b
/[^[:space:]]/!b
/[^[:space:]][[:blank:]]\{1,\}[^[:space:]]/b
:loop
$q;N
/\n.*\S[[:blank:]]\+\S/b
s/\n/ /;tdummy
:dummy
s/[[:space:]]\{1,\}/&/3;t
bloop
' yourfile
Пояснения
- Пропустить пустые, незаполненные строки и строки с NF > 1.
- Установите цикл do-while в точке, где пространство шаблона содержит строку с одним полем.
- Мы берем следующую строку и проверяем, имеет ли она NF > 1, после чего печатаем все пространство шаблонов и возвращаемся к чтению следующей строки.
- Теперь мы знаем, что следующая строка также является однополевой, поэтому мы идем дальше и обрезаем новую строку, соединяющую эти две части в пространстве шаблона.
- Есть ли в пространстве шаблона фрагменты из 3 пробелов? Если да, то мы печатаем все пространство шаблона и начинаем читать следующую строку.
- В противном случае мы возвращаемся к циклу, который, в свою очередь, считывает следующую строку, но присоединяет ее к существующему пространству шаблонов.
Результат
1
4 5 6 7 19
20 22
24 26 27
29 30 31 32
34 40 50 56
58 100
234 235 270 500
1234 1235 1236 1237
2300 2303 2304 2307
2309
решение3
Использование: ./join_rows.awk input.txt
Проверьте shebang #!/usr/bin/awk -f
, так как awk
местоположение в вашей системе может отличаться.
#!/usr/bin/awk -f
BEGIN {
count = 1;
}
{
if (NF == 1) {
if (count > 1 && count <= 4) printf " ";
printf "%s", $1;
count++;
if (count > 4) {
printf "\n";
count = 1;
}
} else {
if (count > 1) printf "\n";
print;
count = 1;
}
}
END {
if(count > 1) printf "\n";
}
Выход:
1
4 5 6 7 19
20 22
24 26 27
29 30 31 32
34 40 50 56
58 100
234 235 270 500
1234 1235 1236 1237
2300 2303 2304 2307
2309
решение4
Расширенныйтаращитьсяподход:
переставить_столбцы.awkсценарий:
#!/bin/awk -f
function printRow(a, i, v)
{
for (i in a) {
printf "%s ", a[i]
}
print ""
delete a
}
NF <= 2{
for (i=1; i<=NF; i++) {
a[++c] = $i
if (length(a) == 4) {
c = 0
printRow(a)
}
}
}
NF > 2{
if (length(a) > 0) {
c = 0
printRow(a)
}
print $0
}
END{ print }
Применение:
awk -f rearrange_columns.awk yourfile
Выход:
1
4 5 6 7 19
20 22
24 26 27
29 30 31 32
34 40 50 56
58 100
234 235 270 500
1234 1235 1236 1237
2300 2303 2304 2307
2309