Con GNU awk:
$ printf '%s\n' {foo,bar}{bar,foo} neither | gawk 'xor(/foo/,/bar/)'
foofoo
barbar
O portablemente:
awk '((/foo/) + (/bar/)) % 2'
Con un grepsoporte para -P(PCRE):
grep -P '^((?=.*foo)(?!.*bar)|(?=.*bar)(?!.*foo))'
Con sed:
sed '
/foo/{
/bar/d
b
}
/bar/!d'
Si desea considerar solo palabras completas (que no hay fooni baren foobarni barbarpor ejemplo), deberá decidir cómo se delimitan esas palabras. Si es por cualquier carácter que no sean letras, dígitos y guiones bajos, como lo hace la -wopción de muchas grepimplementaciones, entonces los cambiaría a:
gawk 'xor(/\<foo\>/,/\<bar\>/)'
awk '((/(^|[^[:alnum:]_)foo([^[:alnum:]_]|$)/) + \
(/(^|[^[:alnum:]_)bar([^[:alnum:]_]|$)/)) % 2'
grep -P '^((?=.*\bfoo\b)(?!.*\bbar\b)|(?=.*\bbar\b)(?!.*\bfoo\b))'
Para sedeso se vuelve un poco complicado a menos que tenga una sedimplementación como GNU sed que admita \</ \>como límites de palabras como lo awkhace GNU .