Как мне использовать grep для строк, содержащих одно из двух слов, но не оба?

Другой вариант - использовать: sudo iotop -aoP

-a    Will show accumulated output
-o    Will only output 
-P    Will only show processes instead of threads

Эта программа сообщит вам, сколько процесс записал на диск и прочитал с диска с момента запуска iotop.

26
31.01.2019, 05:22
6 ответов

Попробуйте сegrep

egrep  'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'
17
27.01.2020, 19:39

С помощью GNU grepвы можете передать оба слова в grep, а затем удалить строки, содержащие оба шаблона.

$ cat testfile.txt
abc
def
abc def
abc 123 def
1234
5678
1234 def abc
def abc

$ grep -w -e 'abc' -e 'def' testfile.txt | grep -v -e 'abc.*def' -e 'def.*abc'
abc
def
32
27.01.2020, 19:39

Подойдет инструмент, отличный от grep.

При использовании perl, например, команда будет:

perl -ne 'print if /pattern1/ xor /pattern2/'

perl -neзапускает команду, заданную для каждой строки стандартного ввода, которая в этом случае печатает строку, если она соответствует /pattern1/ xor /pattern2/или, другими словами, соответствует одному шаблону, но не другому (эксклюзивному или ).

Это работает для шаблона в любом порядке и должно иметь лучшую производительность, чем многократные вызовы grep, а также меньше печатать.

Или, еще короче, с помощью awk:

awk 'xor(/pattern1/,/pattern2/)'

или для версий awk безxor:

awk '/pattern1/+/pattern2/==1`
62
27.01.2020, 19:39

В логических терминах вы ищете A xor B, что может быть записано как

(А, а не Б)

или

(Б, а не А)

Учитывая, что в вашем вопросе не упоминается, что вы заинтересованы в порядке вывода, пока отображаются совпадающие строки, логическое расширение A xor B чертовски просто в grep:

$ cat << EOF > foo
> a b
> a
> b
> c a
> c b
> b a
> b c
> EOF
$ grep -w 'a' foo | grep -vw 'b'; grep -w 'b' foo | grep -vw 'a';
a
c a
b
c b
b c
3
27.01.2020, 19:39

Для следующего примера:

# Patterns:
#    apple
#    pear

# Example line
line="a_apple_apple_pear_a"

Это можно сделать только с помощью grep -E, uniqи wc.

# Grep for regex pattern, sort as unique, and count the number of lines
result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)

Если grepскомпилирован с регулярными выражениями Perl, вы можете сопоставить последнее вхождение вместо того, чтобы переходить кuniq:

# Grep for regex pattern and count the number of lines
result=$(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l)

Вывод результата:

# Only one of the words exists if the result is < 2
((result > 0)) &&
   if (($result < 2)); then
      echo Only one word matched
   else
      echo Both words matched
   fi

Один вкладыш -:

(($(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l) == 1)) && echo Only one word matched

Если вы не хотите жестко -кодировать шаблон, его сборку с переменным набором элементов можно автоматизировать с помощью функции.

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

-2
27.01.2020, 19:39

С grepреализациями, которые поддерживают perl -например, регулярные выражения (например pcregrepили GNU или ast -open grep -P), вы можете сделать это за один grepвызов с помощью:

grep -P '^(?=.*pat1)(?!.*pat2)|^(?=.*pat2)(?!.*pat1)'

То есть найти строки, которые соответствуют pat1, но не pat2, или pat2, но не pat1.

(?=...)и (?!...)— соответственно операторы просмотра вперед и отрицательного просмотра вперед. Таким образом, технически вышеприведенное ищет начало субъекта (^), если за ним следует .*pat1, а не следует .*pat2, или то же самое с pat1и pat2в обратном порядке.

Это неоптимально для строк, содержащих оба шаблона, поскольку тогда их будут искать дважды. Вместо этого вы можете использовать более продвинутые операторы perl, такие как:

grep -P '^(?=.*pat1|())(?(1)(?=.*pat2)|(?!.*pat2))'

(?(1)yespattern|nopattern)совпадает с yespattern, если совпадает1st группа захвата (пустая ()выше ), и nopatternв противном случае. Если это ()совпадает, это означает, что pat1не совпадает, поэтому мы ищемpat2(положительный просмотр вперед ), и мы ищем , а не pat2, иначе (отрицательный просмотр вперед ).

С помощью sedвы можете написать это:

sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'
12
27.01.2020, 19:39

Теги

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