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.

























    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.























      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 20 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.






















          5 Answers
          5






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








          • 2





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

            – wjandrea
            4 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
            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



















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






          • 1





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

            – Jim L.
            3 hours ago



















          0














          awk solution -- for completion's sake:



          awk 'gsub("foo|bar", "&")==1'


          exactly the same trick works in perl:



          perl -ne 'print if s/(foo|bar)/1/g==1'




          share























            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









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








            • 2





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

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








            • 2





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

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








            • 2





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

              – wjandrea
              4 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
              4 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
            4 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
            4 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















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

























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
















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














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

























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














            • 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











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






            • 1





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

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






            • 1





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

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






            • 1





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

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






            • 1





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

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





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

            – Sparhawk
            3 hours ago




            1




            1





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

            – Jim L.
            3 hours ago





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

            – Jim L.
            3 hours ago











            0














            awk solution -- for completion's sake:



            awk 'gsub("foo|bar", "&")==1'


            exactly the same trick works in perl:



            perl -ne 'print if s/(foo|bar)/1/g==1'




            share




























              0














              awk solution -- for completion's sake:



              awk 'gsub("foo|bar", "&")==1'


              exactly the same trick works in perl:



              perl -ne 'print if s/(foo|bar)/1/g==1'




              share


























                0












                0








                0







                awk solution -- for completion's sake:



                awk 'gsub("foo|bar", "&")==1'


                exactly the same trick works in perl:



                perl -ne 'print if s/(foo|bar)/1/g==1'




                share













                awk solution -- for completion's sake:



                awk 'gsub("foo|bar", "&")==1'


                exactly the same trick works in perl:



                perl -ne 'print if s/(foo|bar)/1/g==1'





                share











                share


                share










                answered 7 secs ago









                pizdelectpizdelect

                44916




                44916






















                    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

                    CARDNET

                    Boot-repair Failure: Unable to locate package grub-common:i386

                    濃尾地震