How do I grep for lines containing either of two words, but not both?
I'm trying to use grep
to show only lines containing either of the two words, if only one of them appears in the line, but not if they are in the same line.
So far I've tried grep pattern1 | grep pattern2 | ...
but didn't get the result I expected.
grep
New contributor
add a comment |
I'm trying to use grep
to show only lines containing either of the two words, if only one of them appears in the line, but not if they are in the same line.
So far I've tried grep pattern1 | grep pattern2 | ...
but didn't get the result I expected.
grep
New contributor
add a comment |
I'm trying to use grep
to show only lines containing either of the two words, if only one of them appears in the line, but not if they are in the same line.
So far I've tried grep pattern1 | grep pattern2 | ...
but didn't get the result I expected.
grep
New contributor
I'm trying to use grep
to show only lines containing either of the two words, if only one of them appears in the line, but not if they are in the same line.
So far I've tried grep pattern1 | grep pattern2 | ...
but didn't get the result I expected.
grep
grep
New contributor
New contributor
edited 23 mins ago
Olorin
3,2441417
3,2441417
New contributor
asked 16 hours ago
TrasmosTrasmos
361
361
New contributor
New contributor
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
A tool other than grep
is the way to go.
Using perl, for instance, the command would be:
perl -ne 'print if /pattern1/ xor /pattern2/'
perl -ne
runs the command given over each line of stdin, which in this case prints the line if it matches /pattern1/ xor /pattern2/
, or in other words matches one pattern but not the other (exclusive or).
This works for the pattern in either order, and should have better performance than multiple invocations of grep
, and is less typing as well.
Or, even shorter, with awk:
awk 'xor(/pattern1/,/pattern2/)'
or for versions of awk that don't have xor
:
awk '/pattern1/+/pattern2/==1`
1
Nice - is the Awkxor
available in GNU Awk only?
– steeldriver
14 hours ago
1
@steeldriver I think it's GNU only, yes. Or at least it's missing on older versions. You can replace it with/pattern1/+/pattern2/==1
irxor
is missing.
– Chris
11 hours ago
Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".
– Jim L.
5 hours ago
@JimL. You can use negative lookaround to make sure that the word you are searching for does not come before or after another word character (i.e. alphanumeric or underscore). In perl, if you want to match the word "peach," it would be/(?<![w])peach(?![w])/
. The syntax for this varies quite a bit between perl and other regex implementations, though, and not all implementations have[w]
as a pre-defined character class, so you may have to do something longer like[A-Za-z0-9_]
in some implementations.
– Chris
5 hours ago
2
@JimL. You could put word boundaries (b
) in the patterns themselves, i.e.bwordb
.
– wjandrea
4 hours ago
|
show 1 more comment
Try with egrep
egrep 'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'
1
can also be written asgrep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
– glenn jackman
14 hours ago
5
Also, note from the grep man page:Direct invocation as either egrep or fgrep is deprecated
-- prefergrep -E
– glenn jackman
14 hours ago
That isn't in my OS @glennjackman
– Grump
12 hours ago
I'm on linux, so GNU coreutils
– glenn jackman
11 hours ago
add a comment |
With GNU grep
, you could pass both words to grep
and then remove the lines containing both the patterns.
$ 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
2
@msp9011 I didn't think of that possibility at first. It's fixed now, thanks!
– Haxiel
15 hours ago
1
The^.*
and.*$
are unnecessary and harmful to performance.
– Chris
14 hours ago
@Chris Thanks for the feedback, I've edited my answer.
– Haxiel
13 hours ago
add a comment |
In Boolean terms, you're looking for A xor B, which can be written as
(A and not B)
or
(B and not A)
Given that your question doesn't mention that you are concerned with the order of the output so long as the matching lines are shown, the Boolean expansion of A xor B is pretty darn simple in 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
1
This works, but it will scramble the order of the file.
– Sparhawk
4 hours ago
@Sparhawk True, although "scramble" is a harsh word. ;) it lists all the 'a' matches first, in order, then all the 'b' matches next, in order. The OP didn't express any interest in maintaining the order, just show the lines. FAWK, the next step could besort | uniq
.
– Jim L.
4 hours ago
Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.
– Sparhawk
4 hours ago
1
@Sparhawk ... And I edited in your observation for full disclosure.
– Jim L.
4 hours ago
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Trasmos is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f497674%2fhow-do-i-grep-for-lines-containing-either-of-two-words-but-not-both%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
A tool other than grep
is the way to go.
Using perl, for instance, the command would be:
perl -ne 'print if /pattern1/ xor /pattern2/'
perl -ne
runs the command given over each line of stdin, which in this case prints the line if it matches /pattern1/ xor /pattern2/
, or in other words matches one pattern but not the other (exclusive or).
This works for the pattern in either order, and should have better performance than multiple invocations of grep
, and is less typing as well.
Or, even shorter, with awk:
awk 'xor(/pattern1/,/pattern2/)'
or for versions of awk that don't have xor
:
awk '/pattern1/+/pattern2/==1`
1
Nice - is the Awkxor
available in GNU Awk only?
– steeldriver
14 hours ago
1
@steeldriver I think it's GNU only, yes. Or at least it's missing on older versions. You can replace it with/pattern1/+/pattern2/==1
irxor
is missing.
– Chris
11 hours ago
Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".
– Jim L.
5 hours ago
@JimL. You can use negative lookaround to make sure that the word you are searching for does not come before or after another word character (i.e. alphanumeric or underscore). In perl, if you want to match the word "peach," it would be/(?<![w])peach(?![w])/
. The syntax for this varies quite a bit between perl and other regex implementations, though, and not all implementations have[w]
as a pre-defined character class, so you may have to do something longer like[A-Za-z0-9_]
in some implementations.
– Chris
5 hours ago
2
@JimL. You could put word boundaries (b
) in the patterns themselves, i.e.bwordb
.
– wjandrea
4 hours ago
|
show 1 more comment
A tool other than grep
is the way to go.
Using perl, for instance, the command would be:
perl -ne 'print if /pattern1/ xor /pattern2/'
perl -ne
runs the command given over each line of stdin, which in this case prints the line if it matches /pattern1/ xor /pattern2/
, or in other words matches one pattern but not the other (exclusive or).
This works for the pattern in either order, and should have better performance than multiple invocations of grep
, and is less typing as well.
Or, even shorter, with awk:
awk 'xor(/pattern1/,/pattern2/)'
or for versions of awk that don't have xor
:
awk '/pattern1/+/pattern2/==1`
1
Nice - is the Awkxor
available in GNU Awk only?
– steeldriver
14 hours ago
1
@steeldriver I think it's GNU only, yes. Or at least it's missing on older versions. You can replace it with/pattern1/+/pattern2/==1
irxor
is missing.
– Chris
11 hours ago
Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".
– Jim L.
5 hours ago
@JimL. You can use negative lookaround to make sure that the word you are searching for does not come before or after another word character (i.e. alphanumeric or underscore). In perl, if you want to match the word "peach," it would be/(?<![w])peach(?![w])/
. The syntax for this varies quite a bit between perl and other regex implementations, though, and not all implementations have[w]
as a pre-defined character class, so you may have to do something longer like[A-Za-z0-9_]
in some implementations.
– Chris
5 hours ago
2
@JimL. You could put word boundaries (b
) in the patterns themselves, i.e.bwordb
.
– wjandrea
4 hours ago
|
show 1 more comment
A tool other than grep
is the way to go.
Using perl, for instance, the command would be:
perl -ne 'print if /pattern1/ xor /pattern2/'
perl -ne
runs the command given over each line of stdin, which in this case prints the line if it matches /pattern1/ xor /pattern2/
, or in other words matches one pattern but not the other (exclusive or).
This works for the pattern in either order, and should have better performance than multiple invocations of grep
, and is less typing as well.
Or, even shorter, with awk:
awk 'xor(/pattern1/,/pattern2/)'
or for versions of awk that don't have xor
:
awk '/pattern1/+/pattern2/==1`
A tool other than grep
is the way to go.
Using perl, for instance, the command would be:
perl -ne 'print if /pattern1/ xor /pattern2/'
perl -ne
runs the command given over each line of stdin, which in this case prints the line if it matches /pattern1/ xor /pattern2/
, or in other words matches one pattern but not the other (exclusive or).
This works for the pattern in either order, and should have better performance than multiple invocations of grep
, and is less typing as well.
Or, even shorter, with awk:
awk 'xor(/pattern1/,/pattern2/)'
or for versions of awk that don't have xor
:
awk '/pattern1/+/pattern2/==1`
edited 11 hours ago
answered 15 hours ago
ChrisChris
572314
572314
1
Nice - is the Awkxor
available in GNU Awk only?
– steeldriver
14 hours ago
1
@steeldriver I think it's GNU only, yes. Or at least it's missing on older versions. You can replace it with/pattern1/+/pattern2/==1
irxor
is missing.
– Chris
11 hours ago
Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".
– Jim L.
5 hours ago
@JimL. You can use negative lookaround to make sure that the word you are searching for does not come before or after another word character (i.e. alphanumeric or underscore). In perl, if you want to match the word "peach," it would be/(?<![w])peach(?![w])/
. The syntax for this varies quite a bit between perl and other regex implementations, though, and not all implementations have[w]
as a pre-defined character class, so you may have to do something longer like[A-Za-z0-9_]
in some implementations.
– Chris
5 hours ago
2
@JimL. You could put word boundaries (b
) in the patterns themselves, i.e.bwordb
.
– wjandrea
4 hours ago
|
show 1 more comment
1
Nice - is the Awkxor
available in GNU Awk only?
– steeldriver
14 hours ago
1
@steeldriver I think it's GNU only, yes. Or at least it's missing on older versions. You can replace it with/pattern1/+/pattern2/==1
irxor
is missing.
– Chris
11 hours ago
Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".
– Jim L.
5 hours ago
@JimL. You can use negative lookaround to make sure that the word you are searching for does not come before or after another word character (i.e. alphanumeric or underscore). In perl, if you want to match the word "peach," it would be/(?<![w])peach(?![w])/
. The syntax for this varies quite a bit between perl and other regex implementations, though, and not all implementations have[w]
as a pre-defined character class, so you may have to do something longer like[A-Za-z0-9_]
in some implementations.
– Chris
5 hours ago
2
@JimL. You could put word boundaries (b
) in the patterns themselves, i.e.bwordb
.
– wjandrea
4 hours ago
1
1
Nice - is the Awk
xor
available in GNU Awk only?– steeldriver
14 hours ago
Nice - is the Awk
xor
available in GNU Awk only?– steeldriver
14 hours ago
1
1
@steeldriver I think it's GNU only, yes. Or at least it's missing on older versions. You can replace it with
/pattern1/+/pattern2/==1
ir xor
is missing.– Chris
11 hours ago
@steeldriver I think it's GNU only, yes. Or at least it's missing on older versions. You can replace it with
/pattern1/+/pattern2/==1
ir xor
is missing.– Chris
11 hours ago
Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".
– Jim L.
5 hours ago
Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".
– Jim L.
5 hours ago
@JimL. You can use negative lookaround to make sure that the word you are searching for does not come before or after another word character (i.e. alphanumeric or underscore). In perl, if you want to match the word "peach," it would be
/(?<![w])peach(?![w])/
. The syntax for this varies quite a bit between perl and other regex implementations, though, and not all implementations have [w]
as a pre-defined character class, so you may have to do something longer like [A-Za-z0-9_]
in some implementations.– Chris
5 hours ago
@JimL. You can use negative lookaround to make sure that the word you are searching for does not come before or after another word character (i.e. alphanumeric or underscore). In perl, if you want to match the word "peach," it would be
/(?<![w])peach(?![w])/
. The syntax for this varies quite a bit between perl and other regex implementations, though, and not all implementations have [w]
as a pre-defined character class, so you may have to do something longer like [A-Za-z0-9_]
in some implementations.– Chris
5 hours ago
2
2
@JimL. You could put word boundaries (
b
) in the patterns themselves, i.e. bwordb
.– wjandrea
4 hours ago
@JimL. You could put word boundaries (
b
) in the patterns themselves, i.e. bwordb
.– wjandrea
4 hours ago
|
show 1 more comment
Try with egrep
egrep 'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'
1
can also be written asgrep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
– glenn jackman
14 hours ago
5
Also, note from the grep man page:Direct invocation as either egrep or fgrep is deprecated
-- prefergrep -E
– glenn jackman
14 hours ago
That isn't in my OS @glennjackman
– Grump
12 hours ago
I'm on linux, so GNU coreutils
– glenn jackman
11 hours ago
add a comment |
Try with egrep
egrep 'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'
1
can also be written asgrep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
– glenn jackman
14 hours ago
5
Also, note from the grep man page:Direct invocation as either egrep or fgrep is deprecated
-- prefergrep -E
– glenn jackman
14 hours ago
That isn't in my OS @glennjackman
– Grump
12 hours ago
I'm on linux, so GNU coreutils
– glenn jackman
11 hours ago
add a comment |
Try with egrep
egrep 'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'
Try with egrep
egrep 'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'
edited 12 hours ago
answered 16 hours ago
msp9011msp9011
4,17544064
4,17544064
1
can also be written asgrep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
– glenn jackman
14 hours ago
5
Also, note from the grep man page:Direct invocation as either egrep or fgrep is deprecated
-- prefergrep -E
– glenn jackman
14 hours ago
That isn't in my OS @glennjackman
– Grump
12 hours ago
I'm on linux, so GNU coreutils
– glenn jackman
11 hours ago
add a comment |
1
can also be written asgrep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
– glenn jackman
14 hours ago
5
Also, note from the grep man page:Direct invocation as either egrep or fgrep is deprecated
-- prefergrep -E
– glenn jackman
14 hours ago
That isn't in my OS @glennjackman
– Grump
12 hours ago
I'm on linux, so GNU coreutils
– glenn jackman
11 hours ago
1
1
can also be written as
grep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
– glenn jackman
14 hours ago
can also be written as
grep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
– glenn jackman
14 hours ago
5
5
Also, note from the grep man page:
Direct invocation as either egrep or fgrep is deprecated
-- prefer grep -E
– glenn jackman
14 hours ago
Also, note from the grep man page:
Direct invocation as either egrep or fgrep is deprecated
-- prefer grep -E
– glenn jackman
14 hours ago
That isn't in my OS @glennjackman
– Grump
12 hours ago
That isn't in my OS @glennjackman
– Grump
12 hours ago
I'm on linux, so GNU coreutils
– glenn jackman
11 hours ago
I'm on linux, so GNU coreutils
– glenn jackman
11 hours ago
add a comment |
With GNU grep
, you could pass both words to grep
and then remove the lines containing both the patterns.
$ 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
2
@msp9011 I didn't think of that possibility at first. It's fixed now, thanks!
– Haxiel
15 hours ago
1
The^.*
and.*$
are unnecessary and harmful to performance.
– Chris
14 hours ago
@Chris Thanks for the feedback, I've edited my answer.
– Haxiel
13 hours ago
add a comment |
With GNU grep
, you could pass both words to grep
and then remove the lines containing both the patterns.
$ 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
2
@msp9011 I didn't think of that possibility at first. It's fixed now, thanks!
– Haxiel
15 hours ago
1
The^.*
and.*$
are unnecessary and harmful to performance.
– Chris
14 hours ago
@Chris Thanks for the feedback, I've edited my answer.
– Haxiel
13 hours ago
add a comment |
With GNU grep
, you could pass both words to grep
and then remove the lines containing both the patterns.
$ 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
With GNU grep
, you could pass both words to grep
and then remove the lines containing both the patterns.
$ 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
edited 13 hours ago
answered 16 hours ago
HaxielHaxiel
1,9451710
1,9451710
2
@msp9011 I didn't think of that possibility at first. It's fixed now, thanks!
– Haxiel
15 hours ago
1
The^.*
and.*$
are unnecessary and harmful to performance.
– Chris
14 hours ago
@Chris Thanks for the feedback, I've edited my answer.
– Haxiel
13 hours ago
add a comment |
2
@msp9011 I didn't think of that possibility at first. It's fixed now, thanks!
– Haxiel
15 hours ago
1
The^.*
and.*$
are unnecessary and harmful to performance.
– Chris
14 hours ago
@Chris Thanks for the feedback, I've edited my answer.
– Haxiel
13 hours ago
2
2
@msp9011 I didn't think of that possibility at first. It's fixed now, thanks!
– Haxiel
15 hours ago
@msp9011 I didn't think of that possibility at first. It's fixed now, thanks!
– Haxiel
15 hours ago
1
1
The
^.*
and .*$
are unnecessary and harmful to performance.– Chris
14 hours ago
The
^.*
and .*$
are unnecessary and harmful to performance.– Chris
14 hours ago
@Chris Thanks for the feedback, I've edited my answer.
– Haxiel
13 hours ago
@Chris Thanks for the feedback, I've edited my answer.
– Haxiel
13 hours ago
add a comment |
In Boolean terms, you're looking for A xor B, which can be written as
(A and not B)
or
(B and not A)
Given that your question doesn't mention that you are concerned with the order of the output so long as the matching lines are shown, the Boolean expansion of A xor B is pretty darn simple in 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
1
This works, but it will scramble the order of the file.
– Sparhawk
4 hours ago
@Sparhawk True, although "scramble" is a harsh word. ;) it lists all the 'a' matches first, in order, then all the 'b' matches next, in order. The OP didn't express any interest in maintaining the order, just show the lines. FAWK, the next step could besort | uniq
.
– Jim L.
4 hours ago
Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.
– Sparhawk
4 hours ago
1
@Sparhawk ... And I edited in your observation for full disclosure.
– Jim L.
4 hours ago
add a comment |
In Boolean terms, you're looking for A xor B, which can be written as
(A and not B)
or
(B and not A)
Given that your question doesn't mention that you are concerned with the order of the output so long as the matching lines are shown, the Boolean expansion of A xor B is pretty darn simple in 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
1
This works, but it will scramble the order of the file.
– Sparhawk
4 hours ago
@Sparhawk True, although "scramble" is a harsh word. ;) it lists all the 'a' matches first, in order, then all the 'b' matches next, in order. The OP didn't express any interest in maintaining the order, just show the lines. FAWK, the next step could besort | uniq
.
– Jim L.
4 hours ago
Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.
– Sparhawk
4 hours ago
1
@Sparhawk ... And I edited in your observation for full disclosure.
– Jim L.
4 hours ago
add a comment |
In Boolean terms, you're looking for A xor B, which can be written as
(A and not B)
or
(B and not A)
Given that your question doesn't mention that you are concerned with the order of the output so long as the matching lines are shown, the Boolean expansion of A xor B is pretty darn simple in 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
In Boolean terms, you're looking for A xor B, which can be written as
(A and not B)
or
(B and not A)
Given that your question doesn't mention that you are concerned with the order of the output so long as the matching lines are shown, the Boolean expansion of A xor B is pretty darn simple in 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
edited 4 hours ago
answered 6 hours ago
Jim L.Jim L.
1112
1112
1
This works, but it will scramble the order of the file.
– Sparhawk
4 hours ago
@Sparhawk True, although "scramble" is a harsh word. ;) it lists all the 'a' matches first, in order, then all the 'b' matches next, in order. The OP didn't express any interest in maintaining the order, just show the lines. FAWK, the next step could besort | uniq
.
– Jim L.
4 hours ago
Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.
– Sparhawk
4 hours ago
1
@Sparhawk ... And I edited in your observation for full disclosure.
– Jim L.
4 hours ago
add a comment |
1
This works, but it will scramble the order of the file.
– Sparhawk
4 hours ago
@Sparhawk True, although "scramble" is a harsh word. ;) it lists all the 'a' matches first, in order, then all the 'b' matches next, in order. The OP didn't express any interest in maintaining the order, just show the lines. FAWK, the next step could besort | uniq
.
– Jim L.
4 hours ago
Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.
– Sparhawk
4 hours ago
1
@Sparhawk ... And I edited in your observation for full disclosure.
– Jim L.
4 hours ago
1
1
This works, but it will scramble the order of the file.
– Sparhawk
4 hours ago
This works, but it will scramble the order of the file.
– Sparhawk
4 hours ago
@Sparhawk True, although "scramble" is a harsh word. ;) it lists all the 'a' matches first, in order, then all the 'b' matches next, in order. The OP didn't express any interest in maintaining the order, just show the lines. FAWK, the next step could be
sort | uniq
.– Jim L.
4 hours ago
@Sparhawk True, although "scramble" is a harsh word. ;) it lists all the 'a' matches first, in order, then all the 'b' matches next, in order. The OP didn't express any interest in maintaining the order, just show the lines. FAWK, the next step could be
sort | uniq
.– Jim L.
4 hours ago
Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.
– Sparhawk
4 hours ago
Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.
– Sparhawk
4 hours ago
1
1
@Sparhawk ... And I edited in your observation for full disclosure.
– Jim L.
4 hours ago
@Sparhawk ... And I edited in your observation for full disclosure.
– Jim L.
4 hours ago
add a comment |
Trasmos is a new contributor. Be nice, and check out our Code of Conduct.
Trasmos is a new contributor. Be nice, and check out our Code of Conduct.
Trasmos is a new contributor. Be nice, and check out our Code of Conduct.
Trasmos is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f497674%2fhow-do-i-grep-for-lines-containing-either-of-two-words-but-not-both%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown