Сохранение строк, повторяющихся заданное количество раз [duplicate]

Вот версия, ориентированная на bash; выглядит очень похоже на версию awk oliv

unset fileheads fields
declare -A fileheads
declare -A fields
for f in *.csv
do 
  IFS=, fileheads[$f]=$(head -n1 "$f");
  set -f
  for field in ${fileheads[$f]}
  do
    fields[$field]+=x
  done
  set +f
done

for field in ${!fields[*]}
do
  [[ ${#fields[$field]} -gt 1 ]] || continue 
  for file in ${!fileheads[*]}
  do
    [[ ${fileheads[$file]} =~ $field ]] && echo "$file has $field"
  done
  echo
done

. При этом поля (строка 1) каждого файла собираются в ассоциативный массив fileheads , индексированный по имени файла. Он также собирает список того, сколько раз он видел каждое имя поля. Здесь мы предполагаем, что запятые не появляются в самих названиях полей.

Затем мы перебираем все известные поля; если какой-либо из них был замечен более одного раза, мы просматриваем файлы (индексы в массиве fileheads ), чтобы увидеть, содержит ли какой-либо из них это поле. По крайней мере два файла должны соответствовать этому критерию; их имена файлов и связанное поле выводятся эхом, за ними следует пустая строка для удобства чтения.

Пример выполнения:

вход

$ head -n1 *.csv
==> EVOP_IMAGE.csv <==
"evop_image_id","evop_id","evo_ang_id","evo_collection","file_format","image_name","image_path","image_type"

==> EVO_ANGLE.csv <==
"evo_ang_id","angle_description"

==> IMAGE_TYPE.csv <==
"id","image_type","group","description"

выход

EVOP_IMAGE.csv has "evo_ang_id"
EVO_ANGLE.csv has "evo_ang_id"

EVOP_IMAGE.csv has "image_type"
IMAGE_TYPE.csv has "image_type"

1
27.09.2017, 18:41
4 ответа

awk решение:

--для вывода только тех записей, которые встречаютсяне менее3 раза:

awk '++a[$3]==3{ print $3 }' file
  • ++a[$3]-последовательно увеличивающееся количество уникальных значений 3-го поля

--для вывода только тех записей, которые встречаютсяровно3 раза:

awk '{++a[$3]}END{ for(i in a) if(a[i]==3) print i }' file

Выход:

APPLE
1
27.01.2020, 23:18

Пропустите выходные данные вашего конвейера через awk '$1 == 3 { print $2 }', чтобы получить только APPLE.

В качестве альтернативы, считайте с помощью awkс самого начала:

awk '{ c[$3]++; r[$3] = r[$3] ? r[$3] ORS $0 : $0 } END { for (i in c) { if (c[i] == 3) print r[i] } }' file
1 2 APPLE
8 2 APPLE
3 4 APPLE

Скрипт awkбудет подсчитывать в c, сколько раз встречаются значения в столбце 3. Каждая входная строка добавляется к r, который представляет собой ассоциативный массив, подобный c, с ключом в третьем столбце.

В конце выводятся только те строки, в которых 3-й столбец встречается ровно три раза.

0
27.01.2020, 23:18

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

$ awk 'NR==FNR{a[$3]++; next} a[$3]==3' ip.txt ip.txt 
1 2 APPLE
8 2 APPLE
3 4 APPLE

$ awk 'NR==FNR{a[$3]++; next} a[$3]==2' ip.txt ip.txt 
3 4 PEAR
9 3 LEMON
9 3 LEMON
8 3 PEAR

$ awk 'NR==FNR{a[$3]++; next} a[$3]<2' ip.txt ip.txt 
A B C
8 3 ORANGE
3
27.01.2020, 23:18

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

awk '{print $3}' yourInputFile | sort | uniq -c | while read -r line
   do 
      echo $line | [ `awk '{print $1}'` -gt 2 ] && echo $line | awk '{print $2}'
   done

А если вы захотите ограничить отображение строками, число вхождений которых строго равно 3 , это еще проще. grepможно использовать как @wvxvw, предложенный в комментариях. В этом случае вам даже не нужно выполнять итерацию, но вы должны экранировать grep, чтобы не получать значения, которые начинаются с 3, но больше, например 30:

awk '{print $3}' yourInputFile | sort | uniq -c | grep '^\s*3\s' | awk '{print $2}'

В обоих случаях вывод:

APPLE
0
27.01.2020, 23:18

Теги

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