#linux #bash #sed #grep
#linux #bash #sed #grep
Вопрос:
У меня есть следующий файл Hello.txt с приведенными ниже данными
HEAD 0010 YYYY
A
B
C
D
TAIL 0010 04
HEAD 0001 YYYY
A
B
C
TAIL 0001 03
HEAD 0002 YYYY
A
B
C
D
TAIL 0002 04
HEAD 0001 YYYY
A
B
C
D
E
TAIL 0001 05
HEAD 0003 YYYY
A
B
TAIL 0003 02
** Здесь я хочу извлечь определенные строки, соответствующие формату (HEAD 0001 и TAIL 0001), объединить записи тела вместе, исключив идентичные записи ЗАГОЛОВКА и ХВОСТА и обновив КОНЕЧНУЮ запись с помощью основного количества записей (08).
**
Ожидаемый формат вывода ниже #
HEAD 0010 YYYY
A
B
C
D
TAIL 0010 04
HEAD 0002 YYYY
A
B
C
D
TAIL 0002 04
HEAD 0003 YYYY
A
B
TAIL 0003 02
HEAD 0001 YYYY
A
B
C
A
B
C
D
E
TAIL 0001 08
Комментарии:
1. Вы упомянули, что вам нужны значения
HEAD 0001 to TAIL 0001
, но я тоже мог видеть значения HEAD 200 вместе с TAIL 200, не могли бы вы уточнить больше о том же?2. ХВОСТ 0001 08 находится в ожидаемом выводе, но не на входе.
3. эй, Раман, да, цель состоит в том, чтобы объединить наборы данных для блока 0001 и иметь только один заголовок и трейлер, а также обновить трейлер с помощью объединенного количества записей тела для наборов данных 0001. Здесь первый набор данных 0001 содержит 3 записи, а второй набор данных 0001 содержит 5 записей. Обновление трейлера с комбинированным количеством записей тела = 5 3 = 8
4. Существует ли какой-либо конкретный порядок для выходных разделов?
5. @VPfB Мне очень понравился вопрос о
ansible
; D
Ответ №1:
для многомерных массивов используется GNU awk:
gawk '
$1 == "HEAD" {key = $2; next}
$1 == "TAIL" {next}
{lines[key][ count[key]] = $0}
END {
for (key in count) {
print "HEAD", key
for (i=1; i<=count[key]; i ) print lines[key][i]
printf "TAIL %s dn", key, count[key]
}
}
' file
Ответ №2:
Вот реализация Python с использованием регулярного выражения:
import re
pat=r'^HEAD[ t] (d )([sS] ?)^TAIL[ t] (1)'
with open(ur_file) as f:
data=f.read()
blocks={}
for block in re.finditer(pat, data, flags=re.M):
blocks.setdefault(block.group(1),
[]).extend([r for r in block.group(2).split('n') if r])
for k,v in blocks.items():
cnt=len(v)
data='n'.join(v)
print(f'HEAD {k}n{data}nTAIL {k} {cnt}')
С принтами:
HEAD 0010
A
B
C
D
TAIL 0010 4
HEAD 0001
A
B
C
A
B
C
D
E
TAIL 0001 8
HEAD 0002
A
B
C
D
TAIL 0002 4
HEAD 0003
A
B
TAIL 0003 2
Регулярное выражение r'^HEAD[ t] (d )([sS] ?)^TAIL[ t] (1)'
соответствует блокам записей, как видно из этой демонстрации.
Комментарии:
1. Используете ли вы Python 3.6 для строк f?
Ответ №3:
ОБНОВЛЕНИЕ: OP изменил желаемый вывод, включив в записи требование о выводе фиксированной ширины HEAD/TAIL
; должно быть выполнимо путем изменения printf
форматов; OP может настраивать по мере необходимости… используйте отрицательные числа в формате для выравнивания по левому краю, положительные числа для выравнивания по правому краю, затем отрегулируйте числа для желаемой ширины…
Предположения, основанные на образцах данных:
- OP хочет объединить строки для определенного шаблона (например,
0001
) - консолидированные строки должны быть перемещены в конец вывода
- нет необходимости (повторно) сортировать общий набор данных на основе упорядочения шаблонов (т. Е. Нет необходимости упорядочивать как
0001
,0002
,0003
,0010
)
Одно awk
решение:
pattern='0001'
awk -v ptn="${pattern}" ' # pass pattern in as awk variable "ptn"
$0 ~ "^HEAD "ptn"$" { save=1 ; next } # start of group => set save flag; skip to next line
$0 ~ "^TAIL "ptn" " { save=0 ; next } # end of group => clear save flag; skip to next line
save { i ; line[i]=$0 ; next } # if save=1 then store current line in next position of array line[]; skip to next line
{ print } # otherwise print all other lines to stdout
END { if ( i >= 1 ) # if we found any matches ...
{ printf "%-561s%s", "HEAD", ptn # print the new "HEAD" line
for ( j=1 ; j<=i ; j ) # then loop through our list of indices ...
{ print line[j] } # printing the associated line[] element
fmt="TAIL %s in" # left pad the count with zero ...
printf "%-69s%-491si" "TAIL", i, ptn # print the "TAIL" line
}
}
' hello.txt
ПРИМЕЧАНИЕ: удалите комментарии к коду declutter
Приведенное выше генерирует:
HEAD 0010
A
B
C
D
TAIL 0010 04
HEAD 0002
A
B
C
D
TAIL 0002 04
HEAD 0003
A
B
TAIL 0003 02
HEAD 0001 ('0001' starts in column 562)
A
B
C
A
B
C
D
E
TAIL 08 ('08' starts in column 70) 0001 ('0001' starts in column 562)
Комментарии:
1. Вам не нужен специальный
if
дляi>99
— для больших чисел начальный 0 не печатается:awk 'BEGIN {printf "dn", 100}'
2. извините, я не понимаю, что вы говорите re :
position starts at 68
; не могли бы вы обновить вопрос, обязательно указав, где они68 spaces
отображаются во входных / выходных данных … ?3. теперь у вас есть два набора ожидаемых выходных данных; пожалуйста, объедините их в один набор ожидаемых выходных данных, чтобы у нас было (более) четкое представление о том, что требуется; если входные данные также содержат 68/580 пробелов, то обновите и образец входных данных; вам не обязательно размещать 68/560 пробелов вкаждая строка ЗАГОЛОВКА / ХВОСТА, но, по крайней мере, сделайте что-то вроде
HEAD<560 spaces>0001
иTAIL<68 spaces>0001
; несмотря на это, я обновил ответ некоторыми идеями… по сути, вам просто нужно использовать правильное число в строке формата
Ответ №4:
РЕДАКТИРОВАТЬ: когда я опубликовал это, у вопроса был [Python]
тег. Позже тег был удален.
На простом Python:
import collections
# read into memory
data = collections.defaultdict(list)
with open('datafile') as f:
section = None
for line in f:
if line.startswith('HEAD'):
section = line.split()[1]
elif line.startswith('TAIL'):
# OPTIONAL: check if section id matches
section = None
else:
# OPTIONAL: check if section is not None
data[section].append(line)
# write to output
for section, lines in data.items():
print(f"HEAD {section}")
for line in lines:
print(line, end='')
print(f"TAIL {section} {len(lines):02}")