Разбор каждой строки в текстовом файле с использованием сопоставления с образцом

Я нашел эту статью, которая может помочь вам смонтировать общие ресурсы SMB.

https://askubuntu.com/questions/1026316/cifs-mounts-and-kerberos-permissions-on-access-or-best-practice

Я полагаю, что проблема связана с Kerberos, и Себастьян Старк отлично объясняет, что именно я хотел бы сказать.

2
15.03.2021, 11:46
2 ответа
awk -F':?(Header|Detail)Segment:' '
    { sumPos=10;
      for(i=3; i<=NF; i++) { 
          split($i, tmp, ":")
          for(x in tmp) { 
              sum+=tmp[sumPos]; sumPos+=5
          };
          gsub(/:|:SubSegment.*/, ",", $i)
          gsub(/:/, ",", $2)
          printf("%s,%s%.3d\n", $2, $i, sum)
          sum=0
      };
}' infile
1
18.03.2021, 22:35

Вы упомянули «учиться», так что вот довольно простой awk-скрипт, который, я думаю, решит вашу проблему. он не оптимизирован ни по скорости, ни по длине кода, но, надеюсь, читаем. Я постараюсь объяснить части, чтобы вы могли извлечь из этого уроки.

вот кодез:

BEGIN {
  FS=":"
  OFS=","
}
{
  pos=readheader()
  sanitycheck(pos)
  printresult()
}
func readheader() {
  r["a"]=$2
  r["b"]=$3
  r["c"]=$4
  r["d"]=$5
  r["e"]=$6
  pos=7
  dcount=0
  while($(pos) == "DetailSegment") {
    pos=readdetail(pos+1, dcount)
    dcount++
  }
  return pos
}
func readdetail(pos, dcount) {
  r["detail"][dcount]["a"]=$(pos+0)
  r["detail"][dcount]["b"]=$(pos+1)
  r["detail"][dcount]["c"]=$(pos+2)
  r["detail"][dcount]["d"]=$(pos+3)
  r["detail"][dcount]["e"]=$(pos+4)
  r["detail"][dcount]["f"]=$(pos+5)
  r["detail"][dcount]["g"]=$(pos+6)
  r["detail"][dcount]["h"]=$(pos+7)
  pos=pos+8
  scount=0
  while($(pos) == "SubSegment") {
    pos=readsub(pos+1, dcount, scount)
    scount++
  }
  return pos
}
func readsub(pos, dcount, scount) {
  r["detail"][dcount]["sub"][scount]["a"]=$(pos+0)
  r["detail"][dcount]["sub"][scount]["b"]=$(pos+1)
  r["detail"][dcount]["sub"][scount]["c"]=$(pos+2)
  r["detail"][dcount]["sub"][scount]["d"]=$(pos+3)
  return pos+4
}
func sanitycheck(pos) {
  if (pos <= NF) {
    print "error line "NR" only parsed "pos" of "NF" fields"
  }
}
func printresult() {
  for(d in r["detail"]) {
    subsum=0
    for(s in r["detail"][d]["sub"]) {
      subsum+=r["detail"][d]["sub"][s]["a"]
    }
    print r["a"],r["e"],r["detail"][d]["a"],r["detail"][d]["h"],subsum
  }
}

сохраните это в файле с именем filter.awk.и поместите ввод в файл с именем inputи введите команду

$ awk -f filter.awk input

или направьте вход из источника

$ fromwherecomesinput | awk -f filter.awk

это результат обработки трех строк образцов, которые вы предоставили

1234,29,123467,Purchased,64
1234,29,123468,Refund,0
1234,29,234569,Purchased,0
1234,29,201754,Purchased,0
1234,28,55555,Refund,0
1234,28,123468,Refund,0
1234,28,234569,Purchased,0
1234,28,201754,Purchased,0
1234,28,55555,Sale,56257
1234,28,123468,Refund,0
1234,28,234569,Purchased,0
1234,28,201754,Purchased,0

я не вывел все поля. мне было лень их все печатать.

Я не уверен, правильно ли я понял ваше требование к выводу. возможно, я ошибся. но я пытаюсь объяснить код, поэтому, если вы понимаете остальные коды, вы можете изменить функцию вывода по своему усмотрению.

если строка неправильно сформирована, она выведет что-то вроде этого:

error line 3 only parsed 7 of 20 fields

кроме этой строки вывода отладки, я не делал никакого другого кода для обработки ошибок и создания отчетов.

расшифровка кода:

сначала небольшой обзор того, что делает код :awk считывает ввод построчно. каждая строка разбивается на двоеточие. затем мы перебираем поля и ищем сегменты. затем мы собираем данные в древовидную структуру. наконец, мы перебираем дерево и вычисляем желаемый результат.

К счастью, все сегменты имеют фиксированное количество полей. это делает поиск сегментов очень простым.

древовидная структура примерно такая :в корне есть пять переменных заголовка и список деталей. в каждой детали есть восемь переменных деталей и список подпрограмм. в каждом подразделе есть четыре подварианта.

в конце я связал кучу соответствующих страниц из руководства Fine awk. поэтому, если вы хотите узнать больше о какой-то теме, посмотрите в конце.

приступим к подробному объяснению

BEGIN {
  FS=":"
  OFS=","
}

блок BEGINвыполняется awk перед чтением первой строки из ввода. в основном он используется для инициализации переменных.

также есть блок END, который будет выполняться после прочтения последней строки. он обычно используется для печати конечных результатов.в нашем случае у нас есть результат на строку, но нет кумулятивного конечного результата, поэтому у нас нет блока END.

FS— разделитель полей. это говорит awk, как разделить каждую строку ввода на так называемые поля. это одна из основных сильных сторон awk. часто подходящее значение разделителя полей составляет половину решения. в этом случае мы устанавливаем разделитель полей на двоеточие(:).

OFS— разделитель выходных полей. это будет символ между полями в операторе печати. в этом случае мы устанавливаем запятую(,).

Эти переменные называются управляющими, потому что они изменяют работу awk.

Далее следует кодовый блок "для каждой строки"

{
  pos=readheader()
  sanitycheck(pos)
  printresult()
}

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

(обратите внимание, что вы также можете изменить разделитель записей на что-то другое, кроме новой строки, и тогда запись может быть больше или меньше строки ввода.)

обратите внимание, что в awk все переменные являются глобальными (даже над блоками кода ), поэтому функции в основном предназначены только для структуры для людей. вот почему printresult()может распечатать результат без передачи каких-либо данных. он просто печатает результат из глобальных переменных. возвращение posиз readheader()также не является строго обязательным, поскольку оно является глобальным, но мне понравилось, поэтому я оставил его.

обратите внимание также, что в awk есть только два типа переменных :строки и числа (и массивы ). преобразование является неявным. неинициализированные переменные всегда равны нулю или пустой строке. это чрезмерное упрощение. прочитайте руководства, связанные в конце.

блоки кода часто имеют префикс. например

/foo/ {... }

или

NR > 1 {... }

это условия. это означает, что блок будет выполняться только в том случае, если текущая запись удовлетворяет условию.

наш блок кода не имеет такого условия, поэтому он выполняется для каждой строки.

На жаргоне awk условие называется шаблоном, а блоки кода — действиями.

следующее объяснение функций:

func readheader() {
  r["a"]=$2
  r["b"]=$3
  r["c"]=$4
  r["d"]=$5
  r["e"]=$6
  pos=7
  dcount=0
  while($(pos) == "DetailSegment") {
    pos=readdetail(pos+1, dcount)
    dcount++
  }
  return pos
}

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

$2и другие цифры доллара являются ссылками на поля. поля - это части строки после того, как awk выполнил разбиение. в нашем случае поля — это значения между двоеточиями.

($в awk используется только для переменных поля. и в регулярных выражениях, где они имеют совершенно другое значение, но это совсем другая история. у нас нет регулярных выражений в этом коде, поэтому ничего из этого здесь)

$0— это всегда вся строка.

$1в нашем случае всегда будет HeaderSegment, поэтому мы просто пропускаем это (без проверки ошибок ). От $2до $6— пять значений HeaderSegment.

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

Массивы в awk — это не массивы, как в c или java, а скорее карты или словари. сопоставление ключевых значений. ключи могут быть любой строкой или числом. если повторяться, то порядок в основном случайный. значения могут быть любыми, включая другие массивы. этот массив массивов - это то, что мы используем для построения дерева.

Я использовал клавиши с "a"по "e", потому что у меня нет лучших имен для значений. вы знаете семантическое значение значений и можете дать им более осмысленные имена, такие как "customerID"или "froobazzaloopaCount".

$(pos)— вычисляемая или косвенная полевая переменная. сначала оценивается часть в скобках. то поле ссылается. поэтому, если posравно 7, тогда $(pos)равно $7и $(pos+1)равно $8.это особенно интересно, если зацикливаться на поле, как мы делаем здесь, ищем следующий DetailSegment.

На жаргоне awk это называется непостоянными номерами полей.

остальные функции(readdetailиreadsub)работают аналогично.

функция sanitycheck:

func sanitycheck(pos) {
  if (pos <= NF) {
    print "error line "NR" only parsed "pos" of "NF" fields"
  }
}

NF— количество полей в этой записи. pos— это переменная, которую мы использовали, чтобы отслеживать, какое поле мы рассмотрим следующим. поэтому, если posменьше NF, что-то не так. тогда мы сообщаем об ошибке.

NR— номер текущей записи. поскольку в нашем случае запись в основном представляет собой строку, это номер строки.

Эти переменные называются информационными, потому что они дают информацию о текущем состоянии awk.

функция результатов печати:

func printresult() {
  for(d in r["detail"]) {
    subsum=0
    for(s in r["detail"][d]["sub"]) {
      subsum+=r["detail"][d]["sub"][s]["a"]
    }
    print r["a"],r["e"],r["detail"][d]["a"],r["detail"][d]["h"],subsum
  }
}

Никаких больших сюрпризов. это работает, как и большинство современных языков. for(d in r["detail"])перебирает ключи в массиве r["detail"]. первый цикл перебирает детали. второй цикл перебирает сабвуферы в детали.

для каждой детали выведите числа и сумму первого значения в подпунктах.

одно замечание по поводу printутверждения:

у нас здесьprint 1,2,3(разделены запятой )и вывод1,2,3(разделены запятой ). это потому, что у насOFS(разделитель полей вывода )установлен в запятую. если OFSбыло, например, #, тогда print 1,2,3выведет 1#2#3.

примечаниеprint "1,2,3"(в кавычках )всегда будет 1,2,3независимо от OFS, потому что на этот раз запятая является буквальной запятой.

Надеюсь, это помогло вам понять, как решить вашу проблему с помощью awk. надеюсь, я также смог объяснить вещи достаточно хорошо, чтобы вы могли адаптировать код к своим дальнейшим потребностям.


ссылки на связанные темы в руководстве Fine awk

подробнее о блоках BEGINи END:https://www.gnu.org/software/gawk/manual/html_node/Using-BEGIN_002fEND.html

подробнее о разделителе полей(FS):https://www.gnu.org/software/gawk/manual/html_node/Field-Separators.html

подробнее об «управляющих» переменных (FS, OFSи др.):https://www.gnu.org/software/gawk/manual/html_node/User_002dmodified.html

подробнее о "информационных" переменных(NRиNF):https://www.gnu.org/software/gawk/manual/html_node/Auto_002dset.html

подробнее о записях (, которые чаще всего являются строками):https://www.gnu.org/software/gawk/manual/html_node/Records.html

подробнее о видимости переменных (все глобально ):https://www.gnu.org/software/gawk/manual/html_node/Global-Namespace.html, но некоторые могут быть локальнымиhttps://www.gnu.org/software/gawk/manual/html_node/Variable-Scope.html

подробнее о типах переменных (строки и числа):https://www.gnu.org/software/gawk/manual/html_node/Variable-Typing.html

подробнее о шаблонах и действиях (условия и блоки кода):https://www.gnu.org/software/gawk/manual/html_node/Patterns-and-Actions.html

подробнее о массивах (карты ключ-значение или словари):https://www.gnu.org/software/gawk/manual/html_node/Arrays.html

подробнее о номерах полей (числовое значение в долларах или переменные поля):https://www.gnu.org/software/gawk/manual/html_node/Fields.html

подробнее о непостоянных номерах полей (вычисляемые или косвенные переменные поля):https://www.gnu.org/software/gawk/manual/html_node/Nonconstant-Fields.html

0
18.03.2021, 22:35

Теги

Похожие вопросы