Я новичок в bash и имею следующие требования:

У меня есть файл, как показано ниже:

col1,col2,col3....col25
s1,s2,s2..........s1
col1,col2,col3....col25
s3,s2,s2..........s2

Если вы заметили, что значения этих столбцов могут быть только трех типов: s1, s2, s3

Я могу извлечь последние 2 строки из данного файла, который дает мне:

col1,col2,col3....col25
s3,s1,s2..........s2

Я хочу дополнительно проанализировать вышеупомянутые строки, чтобы получить только столбцы с допустимым значением s1.

Желаемый результат . скажем, col3, col25 - единственные столбцы со значением s2, затем скажите, что разделенное запятыми значение также отлично, например:

col3,col25

Может кто-нибудь, пожалуйста, помогите?

Постскриптум Я нашел много примеров, когда файл анализировался на основе значения, скажем, 2-го (фиксированного) столбца, но как мы можем это сделать, если номер столбца не является фиксированным? Проверенные URL: awk одним вкладышем выбрать только строки на основе значение столбца

1
learner 4 Сен 2017 в 15:15

5 ответов

Лучший ответ

Предположения:

  • Есть 2 строки ввода
  • каждая строка ввода имеет одинаковое количество элементов, разделенных запятыми

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

$ cat col.awk
  /col1/ { for (i=1; i<=NF; i++) { arr_c[i]=$i } ; n=NF }
! /col1/ { for (i=1; i<=NF; i++) { arr_s[i]=$i }        }
END {
sep=""
for (i=1; i<=n; i++)
    { if (arr_s[i]==smatch)
         { printf "%s%s" ,sep,arr_c[i]
           sep=", "
         }
    }
}
  • /col1/: для строки, содержащей col1, сохраните поля в массиве arr_c
  • n=NF: захватить наше максимальное значение индекса массива (NF = количество полей)
  • ! /col1/: для строки, которая не содержит col1, сохраните поля в массиве arr_s
  • END ...: выполняется после загрузки массивов
  • sep="": установите наш начальный разделитель вывода на пустую строку
  • for (...): цикл по индексам нашего массива (от 1 до n)
  • if (arr_s[i]==smatch): если значение массива s соответствует нашему входному параметру (smatch - см. Пример ниже), то ...
  • printf "%s%s",sep,arr_c[i]: напечатайте наш sep и соответствующий элемент массива c, затем ...
  • sep=", ": установите наш разделитель для следующего соответствия в цикле

Мы используем printf, потому что без указания '\ n' (новая строка) весь вывод идет в одну строку.

Примере:

$ cat col.out
col1,col2,col3,col4,col5
s3,s1,s2,s1,s3
$ awk -F, -f col.awk smatch=s1 col.out                                                                                           
col2, col4
  • -F,: определите разделитель поля ввода как запятую
  • здесь мы передаем наш шаблон поиска s1 в переменную массива с именем smatch, на которую есть ссылка в коде awk (см. col.awk - выше)

Если вы хотите сделать все это из командной строки:

$ awk -F, '
  /col1/ { for (i=1; i<=NF; i++) { arr_c[i]=$i } ; n=NF }
! /col1/ { for (i=1; i<=NF; i++) { arr_s[i]=$i }        }
END {
sep=""
for (i=1; i<=n; i++)
    { if (arr_s[i]==smatch)
         { printf "%s%s" ,sep,arr_c[i]
           sep=", "
         }
    }
}
' smatch=s1 col.out
col2, col4

Или свертывание блока END в одну строку:

awk -F, '
  /col1/ { for (i=1; i<=NF; i++) { arr_c[i]=$i } ; n=NF }
! /col1/ { for (i=1; i<=NF; i++) { arr_s[i]=$i }        }
END { sep="" ; for (i=1; i<=n; i++) { if (arr_s[i]==smatch) { printf "%s%s" ,sep,arr_c[i] ; sep=", " } } }
' smatch=s1 col.out
col2, col4
2
markp-fuso 4 Сен 2017 в 13:58

Я не очень хорош с awk, но вот кое-что, что, кажется, работает, выводя только имена столбцов, соответствующие значения которых s1:

#<yourTwoLines> | 
  tac | 
  awk -F ',' 'NR == 1 { for (f=1; f<=NF; f++) { relevant[f]= ($f == "s1") } };
              NR == 2 { for (f=1; f<=NF; f++) { if(relevant[f]) print($f) } }'

Это работает следующим образом:

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

  2. при обработке первой строки (теперь значений) с помощью awk, сохраните в массиве s1

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

1
Aaron 4 Сен 2017 в 13:47

Решение в awk , которое печатает результирующую строку после анализа каждого набора из 2 строк.

$ cat tst.awk
BEGIN {FS=","; p=0}
/s1|s2|s3/ {
   for (i=1; i<NF; i++) {
      if ($i=="s2") str = sprintf("%s%s", str?str ", ":str, c[i])
   };
   p=1
}
!p { for (i=1; i<NF; i++) { c[i] = $i } }
p { print str; p=0; str="" }

Обоснование: создайте свою строку результатов str, когда вы просматриваете строку значений.

  • всякий раз, когда ваш ввод содержит s1, s2 или s3, циклически перебирайте элементы и - если value == s2 - добавляйте столбец с индексом i к строке результатов str; установите переменную печати p в 1.
  • if p = 0 создать массив столбцов
  • if p = 1 вывести строку результатов str

С входом:

$ cat input.txt
col1,col2,col3,col4,col5
s1,s2,s2,s3,s1
col1,col2,col3,col4,col5
s1,s1,s2,s3,s3
col1,col2,col3,col4,col5
s1,s1,s1,s3,s3
col1,col2,col3,col4,col5
s1,s1,s2,s3,s3

Результат:

$ awk -f tst.awk input.txt
col2, col3
col3

col3

Обратите внимание на пустую 3-ю строку: нет s2 для этого.

1
Marc Lambrichs 4 Сен 2017 в 14:24

Допустим, у вас есть это:

cat file
col1,col2,col3,..,col25
s3,s1,s2,........,s2

Тогда вы можете использовать это awk:

awk -F, -v val='s2' '{
   s="";
  for (i=1; i<=NF; i++)
     if (NR==1)
        hdr[i]=$i
     else if ($i==val)
        s=s hdr[i] FS;
  if (s) {
     sub(/,$/, "", s);
     print s
  }
}' file

col3,col25
0
anubhava 4 Сен 2017 в 14:14

Если порядок возвращаемых столбцов не имеет значения

awk -F"," 'NR==1{for(i=1;i<=NF;i++){a[i]=$i};next}{for(i=1;i<=NF;i++){if($i=="s2")b[i]=$i}}END{for( i in b) m=m a[i]",";  gsub(/,$/,"", m); print m }'
0
Vicky 8 Сен 2017 в 12:35