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
    58 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
    58 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 1 hour 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 17 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
    58 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
    58 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
58 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
58 mins ago










5 Answers
5






active

oldest

votes


















17














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
    15 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
    12 hours ago











  • Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".

    – Jim L.
    6 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
    6 hours ago








  • 2





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

    – wjandrea
    6 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
    15 hours ago






  • 5





    Also, note from the grep man page: Direct invocation as either egrep or fgrep is deprecated -- prefer grep -E

    – glenn jackman
    15 hours ago











  • That isn't in my OS @glennjackman

    – Grump
    13 hours ago











  • I'm on linux, so GNU coreutils

    – glenn jackman
    12 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
    17 hours ago






  • 1





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

    – Chris
    16 hours ago











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

    – Haxiel
    15 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
    5 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.
    5 hours ago













  • Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.

    – Sparhawk
    5 hours ago






  • 1





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

    – Jim L.
    5 hours ago



















0














This can be done purely with grep, uniq, and wc.



# Patterns:
# apple
# pear

# Example line
line="a_apple_apple_pear_a"

# Grep for regex pattern, sort as unique, and count the number of lines
result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)

# Onle 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





share|improve this answer























    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

























    5 Answers
    5






    active

    oldest

    votes








    5 Answers
    5






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    17














    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
      15 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
      12 hours ago











    • Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".

      – Jim L.
      6 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
      6 hours ago








    • 2





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

      – wjandrea
      6 hours ago


















    17














    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
      15 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
      12 hours ago











    • Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".

      – Jim L.
      6 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
      6 hours ago








    • 2





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

      – wjandrea
      6 hours ago
















    17












    17








    17







    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 12 hours ago

























    answered 17 hours ago









    ChrisChris

    582314




    582314








    • 1





      Nice - is the Awk xor available in GNU Awk only?

      – steeldriver
      15 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
      12 hours ago











    • Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".

      – Jim L.
      6 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
      6 hours ago








    • 2





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

      – wjandrea
      6 hours ago
















    • 1





      Nice - is the Awk xor available in GNU Awk only?

      – steeldriver
      15 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
      12 hours ago











    • Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".

      – Jim L.
      6 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
      6 hours ago








    • 2





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

      – wjandrea
      6 hours ago










    1




    1





    Nice - is the Awk xor available in GNU Awk only?

    – steeldriver
    15 hours ago





    Nice - is the Awk xor available in GNU Awk only?

    – steeldriver
    15 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
    12 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
    12 hours ago













    Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".

    – Jim L.
    6 hours ago





    Just curious, how could those methods be modified to be word-senstive? The OP uses the phrase "two words".

    – Jim L.
    6 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
    6 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
    6 hours ago






    2




    2





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

    – wjandrea
    6 hours ago







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

    – wjandrea
    6 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
      15 hours ago






    • 5





      Also, note from the grep man page: Direct invocation as either egrep or fgrep is deprecated -- prefer grep -E

      – glenn jackman
      15 hours ago











    • That isn't in my OS @glennjackman

      – Grump
      13 hours ago











    • I'm on linux, so GNU coreutils

      – glenn jackman
      12 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
      15 hours ago






    • 5





      Also, note from the grep man page: Direct invocation as either egrep or fgrep is deprecated -- prefer grep -E

      – glenn jackman
      15 hours ago











    • That isn't in my OS @glennjackman

      – Grump
      13 hours ago











    • I'm on linux, so GNU coreutils

      – glenn jackman
      12 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 14 hours ago

























    answered 17 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
      15 hours ago






    • 5





      Also, note from the grep man page: Direct invocation as either egrep or fgrep is deprecated -- prefer grep -E

      – glenn jackman
      15 hours ago











    • That isn't in my OS @glennjackman

      – Grump
      13 hours ago











    • I'm on linux, so GNU coreutils

      – glenn jackman
      12 hours ago














    • 1





      can also be written as grep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'

      – glenn jackman
      15 hours ago






    • 5





      Also, note from the grep man page: Direct invocation as either egrep or fgrep is deprecated -- prefer grep -E

      – glenn jackman
      15 hours ago











    • That isn't in my OS @glennjackman

      – Grump
      13 hours ago











    • I'm on linux, so GNU coreutils

      – glenn jackman
      12 hours ago








    1




    1





    can also be written as grep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'

    – glenn jackman
    15 hours ago





    can also be written as grep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'

    – glenn jackman
    15 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
    15 hours ago





    Also, note from the grep man page: Direct invocation as either egrep or fgrep is deprecated -- prefer grep -E

    – glenn jackman
    15 hours ago













    That isn't in my OS @glennjackman

    – Grump
    13 hours ago





    That isn't in my OS @glennjackman

    – Grump
    13 hours ago













    I'm on linux, so GNU coreutils

    – glenn jackman
    12 hours ago





    I'm on linux, so GNU coreutils

    – glenn jackman
    12 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
      17 hours ago






    • 1





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

      – Chris
      16 hours ago











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

      – Haxiel
      15 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
      17 hours ago






    • 1





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

      – Chris
      16 hours ago











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

      – Haxiel
      15 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 15 hours ago

























    answered 17 hours ago









    HaxielHaxiel

    1,9451710




    1,9451710








    • 2





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

      – Haxiel
      17 hours ago






    • 1





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

      – Chris
      16 hours ago











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

      – Haxiel
      15 hours ago














    • 2





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

      – Haxiel
      17 hours ago






    • 1





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

      – Chris
      16 hours ago











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

      – Haxiel
      15 hours ago








    2




    2





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

    – Haxiel
    17 hours ago





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

    – Haxiel
    17 hours ago




    1




    1





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

    – Chris
    16 hours ago





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

    – Chris
    16 hours ago













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

    – Haxiel
    15 hours ago





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

    – Haxiel
    15 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
      5 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.
      5 hours ago













    • Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.

      – Sparhawk
      5 hours ago






    • 1





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

      – Jim L.
      5 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
      5 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.
      5 hours ago













    • Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.

      – Sparhawk
      5 hours ago






    • 1





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

      – Jim L.
      5 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 5 hours ago

























    answered 8 hours ago









    Jim L.Jim L.

    1112




    1112








    • 1





      This works, but it will scramble the order of the file.

      – Sparhawk
      5 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.
      5 hours ago













    • Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.

      – Sparhawk
      5 hours ago






    • 1





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

      – Jim L.
      5 hours ago














    • 1





      This works, but it will scramble the order of the file.

      – Sparhawk
      5 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.
      5 hours ago













    • Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.

      – Sparhawk
      5 hours ago






    • 1





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

      – Jim L.
      5 hours ago








    1




    1





    This works, but it will scramble the order of the file.

    – Sparhawk
    5 hours ago





    This works, but it will scramble the order of the file.

    – Sparhawk
    5 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.
    5 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.
    5 hours ago















    Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.

    – Sparhawk
    5 hours ago





    Fair call; I agree my language was inaccurate. I meant to imply that the original order would be changed.

    – Sparhawk
    5 hours ago




    1




    1





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

    – Jim L.
    5 hours ago





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

    – Jim L.
    5 hours ago











    0














    This can be done purely with grep, uniq, and wc.



    # Patterns:
    # apple
    # pear

    # Example line
    line="a_apple_apple_pear_a"

    # Grep for regex pattern, sort as unique, and count the number of lines
    result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)

    # Onle 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





    share|improve this answer




























      0














      This can be done purely with grep, uniq, and wc.



      # Patterns:
      # apple
      # pear

      # Example line
      line="a_apple_apple_pear_a"

      # Grep for regex pattern, sort as unique, and count the number of lines
      result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)

      # Onle 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





      share|improve this answer


























        0












        0








        0







        This can be done purely with grep, uniq, and wc.



        # Patterns:
        # apple
        # pear

        # Example line
        line="a_apple_apple_pear_a"

        # Grep for regex pattern, sort as unique, and count the number of lines
        result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)

        # Onle 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





        share|improve this answer













        This can be done purely with grep, uniq, and wc.



        # Patterns:
        # apple
        # pear

        # Example line
        line="a_apple_apple_pear_a"

        # Grep for regex pattern, sort as unique, and count the number of lines
        result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)

        # Onle 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






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered 46 mins ago









        ZhroZhro

        342413




        342413






















            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

            A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

            Calculate evaluation metrics using cross_val_predict sklearn

            Insert data from modal to MySQL (multiple modal on website)