Вот мой набор данных:
col1,col2,col3
a,b,c
a,d,f
d,u,v
f,g,h
d,u,g
x,t,k
Ожидаемый результат:
f,g,h
x,t,k
Критерий выбора:
Если что-либо повторяется col1
несколько раз, то все связанные строки будут удалены.
Могу ли я решить эту проблему с помощью Linux sort
или uniq
чего-то еще?
решение1
Вот «небуферизованный» (1) двухпроходный подход awk
(будет работать только с обычными файлами).
awk -F',' 'NR==FNR{cnt[$1]++;next} FNR>1&&cnt[$1]==1' input.csv input.csv
Это приведет к обработке файла дважды, поэтому он дважды указывается в качестве аргумента в командной строке.
- Аргумент
-F','
устанавливает разделитель полей,
. - На первом проходе, когда
NR
глобальный счетчик строк равенFNR
счетчику строк для каждого файла, мы регистрируем, как часто каждое значение в столбце 1 встречается в массивеcnt
(который принимает значение как «индекс массива»), но сразу же пропускаем обработку до следующей строки. - Во втором проходе мы проверяем, равен ли счетчик вхождений для текущего значения первого столбца точно 1, а номер строки в файле больше 1 (чтобы пропустить заголовок). Только если это так, текущая строка будет напечатана. Это использует синтаксис,
awk
который выражение вне правил блокирует, что вычисляется какtrue
инструкцияawk
для печати текущей строки.
(1) В ответ на комментарий, который я разместилнебуферизованныйв кавычках, потому что, поскольку решение будет временно хранить некоторые данные из файла в оперативной памяти, оноделаетидут с использованием оперативной памяти. Однако он не будет хранить содержимое файла дословнокроме тогок любым другим данным, сохраняющим прокрутку в оперативной памяти (которыеябудет рассматривать «буферизацию» в прямом смысле).
решение2
Предполагая, что файл есть, /tmp/data
вы можете сделать это с помощью однострочника Perl:
perl -e 'while(<STDIN>) { /(^\S+?),/; $show->{$1}=$_; $count->{$1}++;}; foreach(keys %$show) {print $show->{$_} if($count->{$_} == 1);}' < /tmp/data
Или более читабельно... :
while(<STDIN>) { #loop through all lines in the input and put the lines in "$_"
/(^\S+?),/; #Everything before the first "," now ends up in "$1"
$show->{$1} = $_; #a hash will be created with as keys the "$1" and as values the "$_"
$count->{$1}++; #In the hash $count the number of occurrences will be increased everytime the same $1 appears
}
foreach(keys %$show) { #loop trough all lines
print $show->{$_} if($count->{$_} == 1); #only print them if they occur once
}
решение3
awk
единственное решение
не соблюдает порядок
awk -F, 'NR>1 { count[$1]++ ; line[$1]=$0 ;} END { for ( c in count) if (count[c] ==1) print line[c]}' data
поддержание порядка
awk -F, 'NR>1 { row[a]=$0; col[a]=$1; count[$1]++; ++a; } END { for (i=0; i<a; ++i) if (count[col[i]]==1) print row[i]; }' data
где
-F,
указать awk использовать,
в качестве разделителяNR>1
после первой строкиcount[$1]++
количество элементов первого столбцаline[$1]=$0
линия магазинаEND
после конца файлаfor ( c in count)
цикл через элементif (count[c] ==1)
если только одинprint line[c]
линия печатиa
иcol[]
используются для хранения порядка строк в варианте, сохраняющем порядок.
это можно сделать в одну строку, я сворачиваю для удобства чтения
решение4
декорировать/сортировать/использовать/отменять декорирование, используя любую версию обязательных инструментов POSIX и любые символы во входных данных (если только ваши входные данные не являются CSV-файлом с полями в кавычках, которые могут содержать запятые и/или символы новой строки, но тогда все остальные ответы также будут неверными) и сохраняя порядок входных строк для выходных данных и открывая входные данные только один раз, чтобы все работало, если входные данные поступают из канала или файла, и не сохраняя все входные данные в памяти:
$ awk 'BEGIN{FS=OFS=","} NR>1{print ++cnt[$1], NR, $0}' file |
sort -nt, -k1,1r -k2,2 |
awk -F, '(!seen[$3]++) && ($1==1)' |
cut -d, -f3-
f,g,h
x,t,k