Как я могу перекодировать строки в текстовом файле на основе шаблона? - PullRequest
1 голос
/ 17 января 2020

У меня есть текстовый файл, который содержит партии из 4 строк, первая строка каждой партии находится в правильном положении, однако следующие 3 строки не всегда находятся в правильном порядке.

name cat
label 4
total 5
value 4

name dog
total 4
label 3
value 6

name cow
value 6
total 1
label 4

name fish
total 3
label 5
value 6

Я бы как каждая 4-строчная партия должна быть в следующем формате:

name cat
value 4
total 5
label 4

, поэтому вывод будет:

name cat
value 4
total 5
label 4

name dog
value 6
total 4
label 3

name cow
value 6
total 1
label 4

name fish
value 6
total 3
label 5

Файл содержит тысячи строк в общей сложности, поэтому я хотел бы создайте команду, которая может обрабатывать все потенциальные порядки трех строк и переупорядочить их, если не в правильном формате.

Я знаю, что могу использовать awk для поиска строк, начинающихся с определенной строки, и их переупорядочить их:

awk '$1 == "value" { print $3, $4, $1, $2; next; } 1' 

Однако я не могу понять, как добиться чего-то похожего, что обрабатывается в несколько строк.

Как я могу добиться этого?

Ответы [ 4 ]

3 голосов
/ 17 января 2020

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

BEGIN {RS=""}
{
    for (i=1; i<=NF; i+=2) a[$i] = $(i+1)
    print "name", a["name"] ORS \
          "value", a["value"] ORS \
          "total", a["total"] ORS \
          "label", a["label"] ORS
}


$ awk -f a.awk file
name cat
value 4
total 5
label 4

name dog
value 6
total 4
label 3

name cow
value 6
total 1
label 4

name fish
value 6
total 3
label 5
2 голосов
/ 17 января 2020

Не могли бы вы попробовать следующее.

awk '
/^name/{
  if(name){
    print name ORS array["value"] ORS array["total"] ORS array["label"] ORS
    delete array
  }
  name=$0
  next
}
{
  array[$1]=$0
}
END{
  print name ORS array["value"] ORS array["total"] ORS array["label"]
}
'  Input_file


РЕДАКТИРОВАТЬ: Добавление уточненного решения выше, предложенного сэр Квантур.

awk -v OFS="\n" '
(!NF) && ("name" in a){
  print a["name"],a["value"],a["total"],a["label"] ORS
  delete a
  next
}
{
  a[$1]=$0
}
END{
  print a["name"],a["value"],a["total"],a["label"]
}
'  Input_file
1 голос
/ 17 января 2020

В Vim вы можете отсортировать файл по разделам в обратном порядке sort!:

for i in range(1,line("$"))
  /^name/+1,/^name/+3sort!
endfor

Та же команда, выданная из оболочки:

$ ex -s '+for i in range(1,line("$"))|/^name/+1,/^name/+3sort!|endfor' '+%p' '+q!' inputfile
1 голос
/ 17 января 2020

Самый простой способ заключается в следующем:

awk 'BEGIN{RS=""; ORS="\n\n"; FS=OFS="\n"}
     { for(i=1;i<=NF;++i) { k=substr($i,1,index($i," ")-1); a[k]=$i } }
     { print a["name"],a["value"],a["total"],a["label"] }' file

Как это работает?

Awk знает концепцию records и поля . Файлы разделяются на записи , где последовательные записи разделяются разделителем записей RS. Каждая запись разделяется на поля, где последовательные поля разделяются разделителем полей FS. По умолчанию разделитель записей RS установлен на символ (\n), поэтому каждая запись представляет собой строку. Разделитель записей имеет следующее определение:

RS: Первый символ строкового значения RS должен быть разделителем входных записей; по умолчанию. Если RS содержит более одного символа, результаты не указываются. Если RS равно нулю, то записи разделяются последовательностями, состоящими из плюс одна или несколько пустых строк, начальные или конечные пустые строки не должны приводить к пустым записям в начале или конце ввода , а всегда должен быть разделителем полей, независимо от значения FS.

Таким образом, с указанным вами форматом файла мы можем определять записи на основе RS="" и разделитель полей `FS =" \ n ".

Каждая запись выглядит упрощенно как:

key1 string1      << field $1
key2 string2      << field $2
key3 string3      << field $3
key4 string4      << field $4
...
keyNF stringNF    << field $NF

Когда awk читает запись, мы сначала анализируем ее, сохраняя все key-value пары в массиве a. После этого мы просим напечатать значения, которые нам интересны. Для этого нам нужно определить разделители выходных полей OFS и разделитель выходных записей ORS.

...