Использование ‘sed’ или ‘awk’ для задачи преобразования данных

#linux #awk #sed

#linux #awk #sed

Вопрос:

У меня есть данные в этом формате (много таких строк):

 TASK : Task 1  
TASK : Task 2  
TASK : Task 3  
OWNER : Emp 1  
OWNER : Emp 2  
OWNER : Emp 3  
Deadline : Monday  
Deadline : Tuesday  
Deadline : Wednesday  
  

Это, я хочу преобразовать в:

 TASK          OWNER           Deadline
Task 1       Emp 1            Monday  
Task 2       Emp 2            Tuesday  
Task 3       Emp 3            Wednesday
  

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

Есть ли способ сделать это с помощью ‘awk’ или ‘sed’?

Комментарии:

1. Я не думаю, что ‘sed’ поможет вам далеко продвинуться, поскольку он анализирует документ построчно. Работа с несколькими строками — настоящая головная боль…

Ответ №1:

один из способов с awk:

  awk -F': *' '{i=NR%3;i=i?i:3;a[i]=a[i]?a[i]"t"$2:$2}
              END{for(x=1;x<=length(a);x  )print a[x]}' file
  

он сохраняет порядок, опускает строку заголовка:

 kent$  cat f
TASK : Task 1  
TASK : Task 2  
TASK : Task 3  
OWNER : Emp 1  
OWNER : Emp 2  
OWNER : Emp 3  
Deadline : Monday  
Deadline : Tuesday  
Deadline : Wednesday 

kent$  awk -F': *' '{i=NR%3;i=i?i:3;a[i]=a[i]?a[i]"t"$2:$2}END{for(x=1;x<=length(a);x  )print a[x]}' f
Task 1          Emp 1   Monday  
Task 2          Emp 2   Tuesday  
Task 3          Emp 3   Wednesday
  

объяснение

  awk -F': *'             #":any <space>" as FS
 '{i=NR%3;i=i?i:3;       #take NR%3 in i, if i=0, set i=3. because
                         #we want the i=0 case at the end of the output
 a[i]=a[i]?a[i]"t"$2:$2}#concatenate the 2nd column to an array
 END{for and print}' file#print the content of the array at the end
  

Заголовок

мы можем сохранить заголовок в переменной h и распечатать его, прежде чем пройти a (array) :

 awk -F': *' '{...h=i==1?(h?h"t"$1:$1):h;a[i]=..} 
END{print h;for...}' file
  

Комментарии:

1. Хорошая идея! Для заголовка вы можете прочитать файл дважды, чтобы при первом запуске вы сохранили все параметры: -F: 'FNR==NR {a[$1]=$1; next} , и т.д.

2. @fedorqui это один из способов. на самом деле достаточно просмотреть файл один раз для head. просто возьмите и объедините $1 when NR%3==certain value(0 or 1 or 2)

3. Это блестяще. Я принял ваш ответ.. Если это не слишком сложно, не могли бы вы также добавить небольшое объяснение того, как это работает? Поможет мне в создании этих команд самостоятельно в будущем!

4. @sanjeevmk добавил объяснение и обработку заголовка. Обратите внимание, что 3 приведенные выше коды были жестко запрограммированы, но это может быть переменная. вы можете получить число с помощью других волшебных инструментов, таких как grep, awk or your eyes ...

5. @Kent: Когда я запускаю это на 3.1.3, он говорит: «FNR = 9) fatal: попытка использовать массив `a’ в скалярном контексте» Есть идеи, почему?

Ответ №2:

Решение на Perl:

 perl -le 'while (<>) {
              chomp;
              ($h, $t) = split / : /;
              $i  , push @{$ar[0]}, $h if $h ne $ar[0][-1];
              push @{$ar[$i]}, $t;
          };
          $" = "t";
          print "@{$ar[0]}";
          print join $", map shift @{$ar[$_]}, 1 .. $#ar while @{$ar[1]}'
  

Если вкладки плохо выравнивают текст, я бы использовал Text:: Table.

Ответ №3:

Вот относительно хорошая awk версия:

 BEGIN {FS=" : ";OFS="t"}

/^TASK/     {task [tpos  ] = $2}
/^OWNER/    {owner[opos  ] = $2}
/^Deadline/ {due  [dpos  ] = $2}

END {
  print "TASK", "OWNER", "DEADLINE"
  for (i in task) {
    print task[i],owner[i],due[i]
  }
}
  

🙂

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

 awk -f test.awk input.txt
  

Обновить:

Приведенная выше команда приводит к невыровненному выводу в оболочке:

 TASK    OWNER    DEADLINE
Task 1      Emp 1      Monday  
Task 2      Emp 2      Tuesday  
Task 3      Emp 3      Wednesday 
  

Вы можете исправить это с помощью column команды:

 awk -f test.awk input.txt | column -t -s $'t'
  

Теперь вывод выглядит чистым:

 TASK      OWNER    DEADLINE
Task 1    Emp 1    Monday  
Task 2    Emp 2    Tuesday  
Task 3    Emp 3    Wednesday 
  

Комментарии:

1. Это намного приятнее, 1.

2. 😉 Имена ваших переменных стали чище, но я не хотел их копировать. Определенно лучше использовать описательные имена переменных при принятии решения о создании скрипта, а не использовать из него одну строку. Ваше и мое сочетание было бы «самым приятным»! 🙂

3. Нет, не беспокойтесь об этом, я бы предпочел удалить свой ответ и просто добавить полные переменные к вашим. Я не то чтобы одержим кодом, который я публикую здесь, он должен быть использован или «украден». Я добавил более длинные переменные и исправил несколько незначительных опечаток. Приветствия.