Как я могу удалить строки из файла, когда строка появляется на нескольких строках? - PullRequest
1 голос
/ 08 июля 2019

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

apple pear
banana pizza
spoon fork
pizza plate
sausage egg

Если слово появляется в нескольких строках, я хочу удалить все строки, в которых появляется повторяющееся слово, поскольку вы можете видеть, что «пицца» появляется дважды, поэтому необходимо удалить 2 строки, следующий вывод является обязательным:

apple pear
spoon fork
sausage egg

Мне известно об использовании:

awk '!seen[$1]++' 

Однако это удаляет строки только тогда, когда строка появляется в одном столбце, мне нужна команда, которая проверит оба столбца. Как мне этого добиться?

Ответы [ 5 ]

5 голосов
/ 08 июля 2019

Вы можете решить проблему за несколько шагов, используя grep и uniq -d.

Сначала создайте список всех слов, используя что-то вроде grep -Eo '[^ ]+'.Затем отфильтруйте этот список, чтобы остались только дублированные слова.Фильтрация может быть выполнена с помощью … | sort | uniq -d.Наконец, выведите все строки, которые не содержат ни одного слова из ранее созданного списка, используя grep -Fwvf listFile inputFile.

В bash. Все эти шаги могут быть выполнены в одной команде.Здесь мы будем использовать переменную $in, чтобы сделать ее легко адаптируемой.

in="path/to/your/input/file"
grep -Fwvf <(grep -Eo '[^ ]+' "$in" | sort | uniq -d) "$in"
2 голосов
/ 08 июля 2019
$ awk '
    NR==FNR {
        for (i=1; i<=NF;i++) {
            if ( firstNr[$i] ) {
                multi[NR]
                multi[firstNr[$i]]
            }
            else {
                firstNr[$i] = NR
            }
        }
        next
    }
    !(FNR in multi)
' file file
apple pear
spoon fork
sausage egg

или, если вы предпочитаете:

$ awk '
    NR==FNR {
        for (i=1; i<=NF;i++) {
            cnt[$i]++
        }
        next
    }
    {
        for (i=1; i<=NF;i++) {
            if ( cnt[$i] > 1 ) {
                next
            }
        }
        print
    }
' file file
apple pear
spoon fork
sausage egg
2 голосов
/ 08 июля 2019

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

  • record: отслеживает каждую строку, которую мы проанализировали
  • seen: отслеживает различные слова, а также первый номер записи, который был замечен на

Это дает нам:

awk '{ record[NR]=$0 }
     { for(i=1;i<=NF;++i) {
         if ($i in seen) { delete record[NR]; delete record[seen[$i]] }
         else { seen[$i]=NR }
       }
     }
     END { for(i=1;i<=NR;++i) if (i in record) print record[i] }' file 

Как это работает?

  • record[NR]=$0: сохранить запись $0в массиве record, проиндексированном по номеру записи NR
  • , для каждого поля / слова записи проверяется, было ли слово замечено ранее.Если это было замечено, удалите исходную запись из массива record, а также текущую запись.Если оно не было просмотрено, сохраните слово и номер текущей записи в массиве seen.
  • Когда полный файл будет обработан, проверьте все возможные номера записей, которые мы видели, если он все ещеиндекс массива record, распечатать эту запись.
0 голосов
/ 09 июля 2019

Это может работать для вас (GNU grep, sort, uniq, sed):

sed 's/ /\n/g' file | sort |uniq -d | grep -vFf - file

Или игрушечный раствор GNU sed:

cat <<\! | sed -Ef - file
H         # copy file into hold space
$!d       # delete each line of the original file
g         # at EOF replace pattern space with entire file
y/ /\n/;  # put each word on a separate line
# make a list of duplicate words, space separated
:a;s/^(.*\n)(\S+)(\n.*\b\2\b)/\2 \1\3/;ta
s/\n.*//  # remove adulterated file leaving list of duplicates
G         # append original file to list
# remove lines with duplicate words
:b;s/^((\S+) .*)\n[^\n]*\2[^\n]*/\1/;tb
s/^\S+ //;tb # reduce duplicate word list
s/..//    # remove newline artefacts
!
0 голосов
/ 08 июля 2019

Это работает с вашим образцом:

#!/usr/bin/env sh
filename='x.txt'
for dupe in $(xargs -n1 -a "${filename}" | sort | uniq -d); do
  sed -i.bak -e "/\\<${dupe}\\>/d" "${filename}"
done

Создает список слов, которые встречаются в файле более одного раза:

  • xargs -n1 -a "${filename}" Выводит список всех слов
    содержится в файле (одно слово в строке)
  • | sort Сортировка списка
  • | uniq -d Выводит только слова, которые встречаются более одного раза в последовательных строках

Затем использует sed, чтобы выбрать и удалить все строки, содержащие дублированное слово.

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