awk: перечисление полей на основе другого общего поля

#arrays #awk

Вопрос:

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

Ввод:

 uid1 ip1 tag1
uid1 ip1 tag2
uid2 ip2 tag3
uid2 ip2 tag4
uid2 ip2 tag5
 

Желаемый результат:

 uid1 ip1 tag1,tag2
uid2 ip2 tag3,tag4,tag5
 

Я думаю, что , возможно , этого можно достичь, сохранив все элементы в array1 , затем uid ip поля в array2 , а затем выполнив поиск array1 путем повторения элементов из array2 .

Ответ №1:

 awk -v OFS=, '{
         k=$1 SUBSEP $2; 
         arr[k] = k in arr ? arr[k] OFS $3 : $0;
      }
      END{
         for(i in arr)
            print arr[i]
      }' infile
 

Результаты испытаний:

 $ cat f1
uid1 ip1 tag1
uid1 ip1 tag2
uid2 ip2 tag3
uid2 ip2 tag4
uid2 ip2 tag5

$ awk -v OFS=, '{k=$1 SUBSEP $2; arr[k] = k in arr ? arr[k] OFS $3 : $0;}END{for(i in arr)print arr[i]}' f1
uid1 ip1 tag1,tag2
uid2 ip2 tag3,tag4,tag5
 

Объяснение:

 awk -v OFS=, '{                                   # output field separator
         # variable k contains field1 value 
         # and  SUBSEP - Multi-dimensional array separator 
         # and column 2 value

         k=$1 SUBSEP $2; 

         # arr -> array
         # if array key that is variable k already exists in array arr
         # then arr[k] will be existing content of arr[k]  plus
         # field separator (comma) and then field 3 contents
         # else entire row/record which is when array does not have index already

         arr[k] = k in arr ? arr[k] OFS $3 : $0;

      }
      END{                      # END block

         # iterate through array arr
         # and print array value

         for(i in arr)
            print arr[i]

      }' infile
 

Троичный оператор ниже

 arr[k] = k in arr ? arr[k] OFS $3 : $0;
 

Это то же самое, что

 if(k in arr){
  arr[k] = arr[k] OFS $3
}else{
  arr[k] = $0
}
 

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

1. Спасибо, все сработало так, как задумывалось. Я не могу сказать, что могу расшифровать весь код, чтобы лучше понять, как он работает. В частности, эти части: k=$1 SUBSEP $2 и arr[k] = k in arr ? arr[k] OFS $3 : $0

2. @однострочное добавленное объяснение

3. @Akshay, Спасибо, что поделился приятным ответом «Ура».

4. Действительно очень хорошо объяснено

Ответ №2:

С помощью GNU datamash

 $ datamash -W -t' ' -g1,2 collapse 3 <ip.txt
uid1 ip1 tag1,tag2
uid2 ip2 tag3,tag4,tag5
 
  • -W чтобы использовать пробел/вкладку в качестве разделителя полей ввода
  • -t' ' пробел в качестве разделителя выходных полей
  • -g1,2 группа на основе полей 1 и 2
  • collapse 3 операция, которая будет выполнена на поле 3

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

1. Я не знал об datamash этом , синтаксис выглядит намного проще и чище, к сожалению, он не установлен по умолчанию в моей целевой среде. Будем иметь это в виду, однако, для использования в будущем.