0

How can I match some string enclosed in spaces or coming from the beginning or end?

I need match for -someword in the following sentences: word1 -someword word2, -someword word1, word1 -someword, -someword. And don't need match in the sentences: s-someword, -somewordd

I tried grep the above with regex grep -r [^ ]-someword[$ ] (i.e before -someword must be a space or -someword must start the sentence, after -someword must be a space or -someword must end the sentence), but it finds nothing.

1
  • 1
    When you say "space" in your question, do you mean just the blank character generated by pressing the space bar on your keyboard or do you mean any white space character including tabs?
    – Ed Morton
    Mar 30, 2023 at 17:00

3 Answers 3

3

Try:

grep -w -e -someword

From man grep:

-w, --word-regexp

          Select only those lines containing matches that form whole
          words.  The test is that the matching substring must
          either be at the beginning of the line, or preceded by a
          non-word constituent character.  Similarly, it must be
          either at the end of the line or followed by a non-word
          constituent character.  Word-constituent characters are
          letters, digits, and the underscore.  This option has no
          effect if -x is also

Please note that it would also match -someword if it's surrounded by some other non alphanumeric character except for white spaces, such as # or ,. If you want to ensure it's surrounded only by spaces or beginning/end of line, you can use:

egrep '(^|[[:space:]])-someword([[:space:]]|$)'

# Which is equivalent to:

grep -E '(^|[[:space:]])-someword([[:space:]]|$)'

# Or without extended regex:

grep '\(^\|[[:space:]]\)-someword\([[:space:]]\|$\)'
7
  • You may also want to use -F to avoid using the query string as a regular expression.
    – Kusalananda
    Mar 30, 2023 at 16:23
  • @Kusalananda if they did that then they couldn't add boundaries to avoid matching -someword with non-blank, non-word-constituent chars on either side, e.g. -someword-
    – Ed Morton
    Mar 30, 2023 at 16:34
  • 1
    @EdMorton Yes, you might well be correct. To explicitly conform to that specification, -wF would not be enough.
    – Kusalananda
    Mar 30, 2023 at 16:42
  • 2
    @aviro egrep has been deprecated for about 15+ years (since 2007 IIRC) in favor of grep -E, no reason to include it in any answer.
    – Ed Morton
    Mar 30, 2023 at 16:44
  • 1
    There's no alternation in standard BRE, so that last one with \| will only work in some systems. Also since grep -E is standard, there's no reason to use nonstandard variants like that.
    – ilkkachu
    Mar 30, 2023 at 17:03
1

You may use the following to avoid complicated regular expressions. It matches any line that has -someword at the start with a space after it, -someword somewhere in the middle with spaces on either side, or -someword at the end with a space before it:

grep -e '^-someword ' -e ' -someword ' -e ' -someword$'

This all assumes that -someword is a string that does not contain any characters that are special in regular expressions. If it does, the string must be rewritten to match these literal characters, e.g., by escaping them in the pattern.

If you also want to match lines containing nothing but the string -someword, add -e '^-someword$'.

If you by "space" mean "a blank character", which would include both tabs and spaces, then change the literal spaces in the pattern with [[:blank:]]. Would you need to match a wider variety of space-like characters, such as vertical tabs and carriage returns, use [[:space:]] instead.

4
  • FWIW I find grep -E '(^| )-someword( |$)' less complicated than grep -e '^-someword ' -e ' -someword ' -e ' -someword$'. Neither would match a tab before/after -somespace though.
    – Ed Morton
    Mar 30, 2023 at 16:41
  • @EdMorton Depends on who you are, I suppose. Matching three plain patterns seems less complicated to me than a single patterns with two alternations containing anchors in it. Also, was tab supposed to be allowed?
    – Kusalananda
    Mar 30, 2023 at 16:44
  • Agreed and understood. The OP said space which is ambiguous but I don't see anything to tell me for sure that it can't include tabs or any other chars in the [:space:] POSIX character class (or at least [:blank:]).
    – Ed Morton
    Mar 30, 2023 at 16:48
  • 1
    I've now asked the OP what they mean by "space".
    – Ed Morton
    Mar 30, 2023 at 17:00
1

The Regex pattern for this is (https://regexr.com/7b8g0):

(\s|^)-someword(\s|$)

A lot of shells freak out about these ()\| so you'll have to fiddle with quotes and escapes a bit. For example fish seems to like \\s but zsh likes \s.

With ripgrep and fish, it's pretty easy to implement:

$ bat word.txt --style=numbers
   1 word1 -someword word2
   2 -someword word1
   3 word1 -someword
   4 -someword
   5 s-someword
   6 -somewordd
   7 \s-someword

$ bat word.txt | rg '(\\s|^)-someword(\\s|$)' --only-matching --line-number
1: -someword
2:-someword
3: -someword
4:-someword

$ bat word.txt | rg '(\\s|^)-someword(\\s|$)' --line-number -v
5:s-someword
6:-somewordd
7:\s-someword

(I added \s-someword and -v to address some concerns in comments.)

Note that the spaces become part of the match. To fix that in the regex, you'd have to add a capturing group around -someword (easy) and then then tell rg to return the first group (pffft...).

Because grep is ancient it won't process this "advanced" regex syntax by default. You need to add -E.

$ bat word.txt | grep -E '(\\s|^)(-someword)(\\s|$)' -n
1:word1 -someword word2
2:-someword word1
3:word1 -someword
4:-someword

$ bat word.txt | grep -v -E '(\\s|^)(-someword)(\\s|$)' -n
5:s-someword
6:-somewordd
7:\s-someword

Frankly, if you live in a year starting with 2, you should probably alias grep to grep -E anyway. Or just use rg.

11
  • Note that the pattern \\s would match the literal string \s (maybe it's a feature of thefish shell I don't understand that makes it work for you, if so you should point this out), and that \s is a pattern similar to [[:space:]], but only if you use a non-standard grep implementation that understands Perl-compatible regular expressions (or a subset thereof). The regular expression that grep uses with -E are called POSIX extended regular expressions.
    – Kusalananda
    Mar 30, 2023 at 18:10
  • @Kusalananda "the pattern \\s would match the literal string \s" - just tried it, it doesn't. Mar 30, 2023 at 18:13
  • "The regular expression that grep uses with -E are called POSIX extended regular expressions." - indeed, the curious would also discover this tidbit in man grep. I try to avoid cluttering my brain with unnecessary trivia. Mar 30, 2023 at 18:14
  • @Kusalananda Also, I can't comment on your answer because of the stupid restrictions on new accounts, but your version has a bug and won't match a line with only -someword. I think you need another -e '^-someword$'. Mar 30, 2023 at 18:20
  • I can't get \\s to match a space with grep -E using FreeBSD grep nor with GNU grep. Not even pcregrep recognises \\s as the same as \s. It only ever matches the literal string \s.
    – Kusalananda
    Mar 30, 2023 at 18:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .