How to match the nondirectory part of a filename with a regular expression?

Whats the correct way to match a filename from a full path using string-match-p.

Say I want to check if the file is a git commit message COMMIT_EDITMSG or svn-commit.tmp or svn-commit.3.tmp to pass to 3rd party code that only takes regular expressions (something like auto-mode-alist).

eg: (string-match my-clever-regex "/path/to/svn-commit.3.tmp")

Using the COMMIT_EDITMSG as an example:

• "COMMIT_EDITMSG\\'" also matches PREFIX_COMMIT_EDITMSG.
• "/COMMIT_EDITMSG\\'" fails to match COMMIT_EDITMSG or \COMMIT_EDITMSG.

How can I match a filename which may or may not contain a path?

• Match how/where? Isearch? grep, re-search-forward? Emacs regexp or regexp for find or something else? Define "correct". Please clarify what you're trying to do. – Drew Jan 28 '20 at 1:36
• Emacs lisp provides specific functions to extract parts of filenames: no regex needed. – NickD Jan 28 '20 at 4:31
• If you're matching buffer-file-name, that will have a directory component to it, in which case "/COMMIT_EDITMSG\\'" may be all you need for a regexp. – phils Jan 28 '20 at 6:37
• I don't know whether it could be \  on WIN32 because I've no idea where this value is coming from; but if it's coming from Emacs then you may find that it's / even on WIN32. I suggest either testing this yourself, or else giving details so that someone else would be able to do so. – phils Jan 28 '20 at 8:07
• \' because that's the correct way to say "end of text". Whilst not recommended, newlines are valid in filenames on most systems, so \$ is potentially insufficient. For more examples see virtually every entry in auto-mode-alist. – phils Jan 28 '20 at 8:12

2 Answers

I'd do

(equal "COMMIT_EDITMSG"
(file-name-nondirectory <file>))

• This question asked about using regular expressions which is significant, I've updated my question to explain why I can't use some elisp to check the string. – ideasman42 Jan 28 '20 at 14:44

This regular expression matches both COMMIT_EDITMSG and /path/to/COMMIT_EDITMSG.

(string-match-p "\\\$$?:.*[\\/]\$$?COMMIT_EDITMSG\\'" "/path/to/COMMIT_EDITMSG")


If the filename is from buffer-file-name there is no need to check for \ separators as Emacs converts them to /, there is also no need to account for no need to account for only the filename case as the filename will either be the full path or ./.

Since this isn't part of the original question, adding this as a second example.

(string-match-p "/COMMIT_EDITMSG\\'" "/path/to/COMMIT_EDITMSG")


Thanks to suggestions from @phils which helped improve this answer.

• \$$\\|foo\$$ is a bit simpler as \$$foo\$$?. – phils Jan 28 '20 at 6:16
• Where are you receiving paths with backslash separators from? AFAIK it's uncommon to have to deal with DOS separators explicitly -- see (elisp)File Names – phils Jan 28 '20 at 6:16
• Assuming you don't need to capture that sub-group, I'd suggest "\$$?:\\\\|[\\/]\$$COMMIT_EDITMSG\\'" (n.b. newlines are valid in filenames). And if you don't need to support backslashes then just / instead of [\\/] of course. And maybe go with string-match-p if you aren't capturing anything. – phils Jan 28 '20 at 6:20
• The filenames are from buffer-file-name, which might be called on a windows system. – ideasman42 Jan 28 '20 at 15:13
• In which case I believe you can forget about \  as Emacs stores file names using / internally so that elisp programs work consistently across systems. The aforementioned note in (elisp)File Names will certainly be applicable to buffer-file-name. – phils Jan 28 '20 at 20:30