С 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
, если совпадает1
st группа захвата (пустая ()
выше ), и nopattern
в противном случае. Если это ()
совпадает, это означает, что pat1
не совпадает, поэтому мы ищемpat2
(положительный просмотр вперед ), и мы ищем , а не pat2
, иначе (отрицательный просмотр вперед ).
С помощью sed
вы можете написать это:
sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'