Ответы:
Инструмент, отличный 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`
xor
доступен только в GNU Awk?
/pattern1/+/pattern2/==1
ir xor
отсутствует.
\b
) в самих моделях, то есть \bword\b
.
Попробуй с egrep
egrep 'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'
grep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
Direct invocation as either egrep or fgrep is deprecated
- предпочитаюgrep -E
-f
и -e
опции, хотя старые egrep
и fgrep
будут поддерживаться некоторое время.
grep
(который поддерживает -F
, -E
, -e
, , -f
как POSIX требует) в /usr/xpg4/bin
. Утилиты в /bin
устарели.
С 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 st (пустой ()
сверху), и в nopattern
противном случае. Если это ()
соответствует, это означает, что pat1
не соответствует, поэтому мы ищем pat2
(позитивный взгляд вперед), и мы ищем не pat2
иначе (негативный взгляд вперед).
С помощью sed
вы можете написать это:
sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'
grep: the -P option only supports a single pattern
, по крайней мере, в каждой системе, к которой у меня есть доступ. +1 за ваше второе решение, хотя.
grep
. pcregrep
и у ast-open grep такой проблемы нет. Я заменил -e
множитель оператором чередования RE, поэтому grep
теперь он должен работать и с GNU .
В логических терминах вы ищете 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
sort | uniq
.
Для следующего примера:
# 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 как функция без конвейеров или дополнительных процессов, но будет более сложным и, вероятно, выходит за рамки вашего вопроса.
Big apple\n
и pear-shaped\n
, то выход должен содержать обе эти строки. Ваше решение получит счет 2; длинная версия будет сообщать «оба слова совпадают» (что является ответом на неправильный вопрос), а короткая версия вообще ничего не говорит. (3) Предложение: использование -o
здесь - очень плохая идея, потому что оно скрывает строки, содержащие совпадения, поэтому вы не можете видеть, когда оба слова появляются на одной строке. … (Продолжение)
uniq
/ sort -u
и необычного регулярного выражения Perl для сопоставления только с последним вхождением в каждой строке на самом деле не дает полезного ответа на этот вопрос. Но даже если бы они это сделали, это все равно было бы плохим ответом, потому что вы не объясняете, как они способствуют ответу на вопрос. (См . Ответ Стефана Шазеласа для примера хорошего объяснения.)
[a-z][a-z0-9]\(,7\}\(\.[a-z0-9]\{,3\}\)+
? (2) Что, если одно из слов / шаблонов появляется более одного раза в строке (а другое не появляется)? Это эквивалентно тому, что слово появляется один раз, или оно считается как несколько вхождений?