With GNU awk
:
$ printf '%s\n' {foo,bar}{bar,foo} neither | gawk 'xor(/foo/,/bar/)'
foofoo
barbar
Or portably:
awk '((/foo/) + (/bar/)) % 2'
With a grep
with support for -P
(PCRE):
grep -P '^((?=.*foo)(?!.*bar)|(?=.*bar)(?!.*foo))'
With sed
:
sed '
/foo/{
/bar/d
b
}
/bar/!d'
If you want to consider whole words only (that there is neither foo
nor bar
in foobar
or barbar
for instance), you'd need to decide how those words are delimited. If it's by any character other than letters, digits and underscore like the -w
option of many grep
implementation does, then you'd change those to:
gawk 'xor(/\<foo\>/,/\<bar\>/)'
awk '((/(^|[^[:alnum:]_)foo([^[:alnum:]_]|$)/) + \
(/(^|[^[:alnum:]_)bar([^[:alnum:]_]|$)/)) % 2'
grep -P '^((?=.*\bfoo\b)(?!.*\bbar\b)|(?=.*\bbar\b)(?!.*\bfoo\b))'
For sed
that becomes a bit complicated unless you have a sed
implementation like GNU sed
that supports \<
/\>
as word boundaries like GNU awk
does.