5

If ^ matches the beginning of a line and $ matches the end of a line, then why doesn't ^$ match the beginning and end of a string like the one in the title?

How do you make such a regex that matches a string like this:

"Starts with s and ends with s."

3
  • 8
    If the string is "exactly one" s", then it matches you're golden!
    – Ian W
    Oct 9, 2020 at 1:59
  • 9
    The title says ^s$ but the question body says ^$ without the s. Which are you asking about?
    – shoover
    Oct 9, 2020 at 3:38
  • 1
    Concluding from the remaining text, I think the OP meant ^$ in that first line in the same sense that they stated "If ^ matches the beginning" instead of "If ^s matches the beginning", so the question would be about ^s$ for the concrete example, but in general about ^$ as a "combined anchor" at begin and end for whatever is inbetween.
    – AdminBee
    Oct 9, 2020 at 8:35

1 Answer 1

46

The reason lies in the way RegEx matches are processed (see here, e.g.): The string is evaluated from left to right, and - except for backreferences - every single symbol in the string must be matched by a token in the regular expression (which in the simplest case is the literal symbol itself), although the token can be implicit thanks to repetition operators.

The key point is that regular expressions do not describe "general properties of the entire string" (such as "starts and ends with s"), but impose rules on the character level of the string. So, your regular expression

^s$

means: "start-of-string", followed by one s, and immediately followed by "end-of-string". This would therefore only match the string that consists of the single letter s. While technically this is a string starting and ending with s, it is not what you are looking for.

If you want to match a string that starts with s, has arbitrary characters in between, and ends with s, you have to specify all of these explicitly.

To make it very explicit:

  • A first iteration could look like this:
    ^s.*s$
    
    This would match "string starting with s, followed by zero or more characters of any kind (.*), followed by s at the end-of-string.
  • Of course, this would not match the string that consists of only the character s. So, in the second iteration, we would make the part after the initial s optional (see comment by @user414777):
    ^s(.*s)?$
    
    This would then match "string starting with s, followed by zero or one times (the ?) 'a sub-string of zero or more characters of any kind, followed by an s' (the content of the (...)), and ending immediately after that".

Now, as you tagged your question with awk, it could mean that you want to create a condition for an awk rule that only applies if the line or a string starts and ends with s. This could look like:

awk '/^s/ && /s$/ { ... }'

With grep, such an AND-construction is not possible natively (the -e regex1 -e regex2 syntax would amount to an OR), but you can achieve it by piping:

grep "^s" sourcefile.txt | grep "s$"
4
  • 5
    This is a good example where it would be easier (as in easier to understand and easier to maintain) to use two separate regular expressions than one single expression. Your example at the end with awk illustrates this nicely. With grep, you'd do grep -e '^s' -e 's$'.
    – Kusalananda
    Oct 8, 2020 at 12:22
  • 4
    @Kusalananda you are right, but are you sure with the grep example? In my GNU grep, specifying two patterns would amount to OR, not AND as the OP requires ...
    – AdminBee
    Oct 8, 2020 at 12:26
  • 6
    @AdminBee You're correct. It would need to be two grep's with a pipe.
    – Kusalananda
    Oct 8, 2020 at 12:34
  • I'm still not clear on whether the OP intends to match lines with only an s, but ... grep -Ex 's(|.*s)'
    – Jim L.
    Sep 16, 2021 at 7:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.