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






















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








          • 2





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

            – wjandrea
            4 hours ago










          1




          1





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

          – steeldriver
          14 hours ago





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

          – steeldriver
          14 hours ago




          1




          1





          @steeldriver I think it's GNU only, yes. Or at least it's missing on older versions. You can replace it with /pattern1/+/pattern2/==1 ir xor is missing.

          – Chris
          11 hours ago





          @steeldriver I think it's GNU only, yes. Or at least it's missing on older versions. You can replace it with /pattern1/+/pattern2/==1 ir xor is missing.

          – Chris
          11 hours ago













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

          – Jim L.
          5 hours ago





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

          – Jim L.
          5 hours ago













          @JimL. You can use negative lookaround to make sure that the word you are searching for does not come before or after another word character (i.e. alphanumeric or underscore). In perl, if you want to match the word "peach," it would be /(?<![w])peach(?![w])/. The syntax for this varies quite a bit between perl and other regex implementations, though, and not all implementations have [w] as a pre-defined character class, so you may have to do something longer like [A-Za-z0-9_] in some implementations.

          – Chris
          5 hours ago







          @JimL. You can use negative lookaround to make sure that the word you are searching for does not come before or after another word character (i.e. alphanumeric or underscore). In perl, if you want to match the word "peach," it would be /(?<![w])peach(?![w])/. The syntax for this varies quite a bit between perl and other regex implementations, though, and not all implementations have [w] as a pre-defined character class, so you may have to do something longer like [A-Za-z0-9_] in some implementations.

          – Chris
          5 hours ago






          2




          2





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

          – wjandrea
          4 hours ago







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

          – wjandrea
          4 hours ago















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









          HaxielHaxiel

          1,9451710




          1,9451710








          • 2





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

            – Haxiel
            15 hours ago






          • 1





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

            – Chris
            14 hours ago











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

            – Haxiel
            13 hours ago














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

          Polycentropodidae

          Magento 2 Error message: Invalid state change requested

          Paulmy