How do I grep for lines containing either of two words, but not both?












7















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.










share|improve this question









New contributor




Trasmos is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





















  • (1) You talk about “words” and “patterns”.  Which is it?  Ordinary words like “quick”, “brown” and “fox”, or regular expressions like [a-z][a-z0-9](,7}(.[a-z0-9]{,3})+? (2) What if one of the words / patterns appears more than once in a line (and the other one doesn’t appear)?  Is that equivalent to the word appearing once, or does it count as multiple occurrences?

    – G-Man
    4 mins ago
















7















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.










share|improve this question









New contributor




Trasmos is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





















  • (1) You talk about “words” and “patterns”.  Which is it?  Ordinary words like “quick”, “brown” and “fox”, or regular expressions like [a-z][a-z0-9](,7}(.[a-z0-9]{,3})+? (2) What if one of the words / patterns appears more than once in a line (and the other one doesn’t appear)?  Is that equivalent to the word appearing once, or does it count as multiple occurrences?

    – G-Man
    4 mins ago














7












7








7








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.










share|improve this question









New contributor




Trasmos is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.












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






share|improve this question









New contributor




Trasmos is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




Trasmos is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 46 mins ago









Olorin

3,2441417




3,2441417






New contributor




Trasmos is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 16 hours ago









TrasmosTrasmos

361




361




New contributor




Trasmos is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





Trasmos is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






Trasmos is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.













  • (1) You talk about “words” and “patterns”.  Which is it?  Ordinary words like “quick”, “brown” and “fox”, or regular expressions like [a-z][a-z0-9](,7}(.[a-z0-9]{,3})+? (2) What if one of the words / patterns appears more than once in a line (and the other one doesn’t appear)?  Is that equivalent to the word appearing once, or does it count as multiple occurrences?

    – G-Man
    4 mins ago



















  • (1) You talk about “words” and “patterns”.  Which is it?  Ordinary words like “quick”, “brown” and “fox”, or regular expressions like [a-z][a-z0-9](,7}(.[a-z0-9]{,3})+? (2) What if one of the words / patterns appears more than once in a line (and the other one doesn’t appear)?  Is that equivalent to the word appearing once, or does it count as multiple occurrences?

    – G-Man
    4 mins ago

















(1) You talk about “words” and “patterns”.  Which is it?  Ordinary words like “quick”, “brown” and “fox”, or regular expressions like [a-z][a-z0-9](,7}(.[a-z0-9]{,3})+? (2) What if one of the words / patterns appears more than once in a line (and the other one doesn’t appear)?  Is that equivalent to the word appearing once, or does it count as multiple occurrences?

– G-Man
4 mins ago





(1) You talk about “words” and “patterns”.  Which is it?  Ordinary words like “quick”, “brown” and “fox”, or regular expressions like [a-z][a-z0-9](,7}(.[a-z0-9]{,3})+? (2) What if one of the words / patterns appears more than once in a line (and the other one doesn’t appear)?  Is that equivalent to the word appearing once, or does it count as multiple occurrences?

– G-Man
4 mins ago










4 Answers
4






active

oldest

votes


















16














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`





share|improve this answer





















  • 1





    Nice - is the Awk xor 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 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











  • @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
    5 hours ago





















7














Try with egrep



egrep  'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'





share|improve this answer





















  • 1





    can also be written as grep -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 -- prefer grep -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



















5














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





share|improve this answer





















  • 2





    @msp9011 I didn't think of that possibility at first. It's fixed now, thanks!

    – Haxiel
    16 hours ago






  • 1





    The ^.* and .*$ are unnecessary and harmful to performance.

    – Chris
    15 hours ago











  • @Chris Thanks for the feedback, I've edited my answer.

    – Haxiel
    14 hours ago



















1














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





share|improve this answer





















  • 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 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






  • 1





    @Sparhawk ... And I edited in your observation for full disclosure.

    – Jim L.
    4 hours ago











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.










draft saved

draft discarded


















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









16














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`





share|improve this answer





















  • 1





    Nice - is the Awk xor 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 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











  • @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
    5 hours ago


















16














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`





share|improve this answer





















  • 1





    Nice - is the Awk xor 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 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











  • @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
    5 hours ago
















16












16








16







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`





share|improve this answer















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`






share|improve this answer














share|improve this answer



share|improve this answer








edited 11 hours ago

























answered 16 hours ago









ChrisChris

572314




572314








  • 1





    Nice - is the Awk xor 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 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











  • @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
    5 hours ago
















  • 1





    Nice - is the Awk xor 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 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











  • @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
    5 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
5 hours ago







@JimL. You could put word boundaries (b) in the patterns themselves, i.e. bwordb.

– wjandrea
5 hours ago















7














Try with egrep



egrep  'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'





share|improve this answer





















  • 1





    can also be written as grep -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 -- prefer grep -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
















7














Try with egrep



egrep  'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'





share|improve this answer





















  • 1





    can also be written as grep -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 -- prefer grep -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














7












7








7







Try with egrep



egrep  'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'





share|improve this answer















Try with egrep



egrep  'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'






share|improve this answer














share|improve this answer



share|improve this answer








edited 13 hours ago

























answered 16 hours ago









msp9011msp9011

4,17544064




4,17544064








  • 1





    can also be written as grep -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 -- prefer grep -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





    can also be written as grep -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 -- prefer grep -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











5














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





share|improve this answer





















  • 2





    @msp9011 I didn't think of that possibility at first. It's fixed now, thanks!

    – Haxiel
    16 hours ago






  • 1





    The ^.* and .*$ are unnecessary and harmful to performance.

    – Chris
    15 hours ago











  • @Chris Thanks for the feedback, I've edited my answer.

    – Haxiel
    14 hours ago
















5














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





share|improve this answer





















  • 2





    @msp9011 I didn't think of that possibility at first. It's fixed now, thanks!

    – Haxiel
    16 hours ago






  • 1





    The ^.* and .*$ are unnecessary and harmful to performance.

    – Chris
    15 hours ago











  • @Chris Thanks for the feedback, I've edited my answer.

    – Haxiel
    14 hours ago














5












5








5







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





share|improve this answer















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






share|improve this answer














share|improve this answer



share|improve this answer








edited 14 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
    16 hours ago






  • 1





    The ^.* and .*$ are unnecessary and harmful to performance.

    – Chris
    15 hours ago











  • @Chris Thanks for the feedback, I've edited my answer.

    – Haxiel
    14 hours ago














  • 2





    @msp9011 I didn't think of that possibility at first. It's fixed now, thanks!

    – Haxiel
    16 hours ago






  • 1





    The ^.* and .*$ are unnecessary and harmful to performance.

    – Chris
    15 hours ago











  • @Chris Thanks for the feedback, I've edited my answer.

    – Haxiel
    14 hours ago








2




2





@msp9011 I didn't think of that possibility at first. It's fixed now, thanks!

– Haxiel
16 hours ago





@msp9011 I didn't think of that possibility at first. It's fixed now, thanks!

– Haxiel
16 hours ago




1




1





The ^.* and .*$ are unnecessary and harmful to performance.

– Chris
15 hours ago





The ^.* and .*$ are unnecessary and harmful to performance.

– Chris
15 hours ago













@Chris Thanks for the feedback, I've edited my answer.

– Haxiel
14 hours ago





@Chris Thanks for the feedback, I've edited my answer.

– Haxiel
14 hours ago











1














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





share|improve this answer





















  • 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 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






  • 1





    @Sparhawk ... And I edited in your observation for full disclosure.

    – Jim L.
    4 hours ago
















1














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





share|improve this answer





















  • 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 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






  • 1





    @Sparhawk ... And I edited in your observation for full disclosure.

    – Jim L.
    4 hours ago














1












1








1







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





share|improve this answer















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






share|improve this answer














share|improve this answer



share|improve this answer








edited 4 hours ago

























answered 7 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 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






  • 1





    @Sparhawk ... And I edited in your observation for full disclosure.

    – Jim L.
    4 hours ago














  • 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 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






  • 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










Trasmos is a new contributor. Be nice, and check out our Code of Conduct.










draft saved

draft discarded


















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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

404 Error Contact Form 7 ajax form submitting

How to know if a Active Directory user can login interactively

TypeError: fit_transform() missing 1 required positional argument: 'X'