294

I have not been able to find a proper regex to match any string not ending with some condition. For example, I don't want to match anything ending with an a.

This matches

b
ab
1

This doesn't match

a
ba

I know the regex should be ending with $ to mark the end, though I don't know what should preceed it.

Edit: The original question doesn't seem to be a legit example for my case. So: how to handle more than one character? Say anything not ending with ab?

I've been able to fix this, using this thread:

.*(?:(?!ab).).$

Though the downside with this is, it doesn't match a string of one character.

Sled
  • 18,541
  • 27
  • 119
  • 168
Menno
  • 12,175
  • 14
  • 56
  • 88

9 Answers9

408

You don't give us the language, but if your regex flavour support look behind assertion, this is what you need:

.*(?<!a)$

(?<!a) is a negated lookbehind assertion that ensures, that before the end of the string (or row with m modifier), there is not the character "a".

See it here on Regexr

You can also easily extend this with other characters, since this checking for the string and isn't a character class.

.*(?<!ab)$

This would match anything that does not end with "ab", see it on Regexr

stema
  • 90,351
  • 20
  • 107
  • 135
  • 3
    I don't know RegexPAL, but regexes are different in all languages and lookbehind assertions are an advanced feature that is not supported by all. – stema May 06 '13 at 12:47
  • 9
    regexpal is a javascript based regex tester and javascript doesn't support lookbehind assertions which is sad – HamZa May 06 '13 at 12:58
  • Lookbehinds are not supported on regexr (javascript) – Stealth Rabbi Jan 19 '16 at 12:42
  • 1
    Lack of lookbehinds in JS make me cry. If you're doing server-side though you can probably use the PCRE module on NPM or similar to use them directly (it's a set of bindings so I don't think you can use it front-end) – Eirik Birkeland Mar 18 '16 at 13:16
  • More types of lookahead / lookbehind assertions: http://stackoverflow.com/q/2973436/12484 – Jon Schneider Jun 29 '16 at 14:46
  • For those who can't use lookarounds, I've provided an answer that works without them. – MatthewRock Feb 08 '17 at 11:56
  • am using regex to validate email, i need my regex do not accept those caracter at the end of the email "(-|_=*+/!:;*<>,%}]{[ " , so how can i except those caracters – Imen Sep 06 '19 at 13:01
  • Thanks, following worked for me when selecting all the lines that don't end with pdf|exe|zip|dmg: /.*(?<!pdf|exe|zip|dmg)$/gmi – AbstractVoid Dec 06 '19 at 10:27
110

Use the not (^) symbol:

.*[^a]$

If you put the ^ symbol at the beginning of brackets, it means "everything except the things in the brackets." $ is simply an anchor to the end.

For multiple characters, just put them all in their own character set:

.*[^a][^b]$
Community
  • 1
  • 1
tckmn
  • 57,719
  • 27
  • 114
  • 156
  • 1
    +1, with the caveat that this does not match the empty string (which may or may not be as intended), so the meaning is rather "any character that is not in the brackets". – Fred Foo May 06 '13 at 12:12
  • @larsmans: It does match the empty string though... ab will match the last only. –  May 06 '13 at 12:14
  • Ah great, was so close (had the `^` outside of the brackets, which marks the beginning of a document). Thanks! Couldn't find any documentation saying that it should be in `[]`. – Menno May 06 '13 at 12:14
  • @Doorknob: not in grep, it doesn't. And it shouldn't, either. – Fred Foo May 06 '13 at 12:15
  • @larsmans: Try it here.. http://regexpal.com/ that's the javascript regex engine. –  May 06 '13 at 12:16
  • @0A0D: and how does that tool show me that an empty string was matched? – Fred Foo May 06 '13 at 12:17
  • @0A0D: yes, the RE in the answer. Matching text is colored yellow, but nothing happens to an empty string (a completely empty input field). – Fred Foo May 06 '13 at 12:18
  • 4
    @0A0D: a string containing whitespace is not an empty string. – Fred Foo May 06 '13 at 12:19
  • @larsmans: That's up for debate.. https://en.wikipedia.org/wiki/Line_%28text_file%29 and http://vim.wikia.com/wiki/Remove_unwanted_empty_lines .. grep can differentiate between whitespace and nothing, yes. –  May 06 '13 at 12:22
  • 9
    @0A0D Actually, that's not up for debate, that's a fact – tckmn May 06 '13 at 12:22
  • @Doorknob: It's a difference between how different frameworks, tools and toolsets handle empty lines. –  May 06 '13 at 12:23
  • @Doorknob: How would the expression look when using more characters than just `a`? Say anything but ending with `ab`? – Menno May 06 '13 at 12:24
  • 12
    @Doorknob: that doesn't match `ae` or `cb`. – Fred Foo May 06 '13 at 12:25
  • 2
    Nop, this wouldn't allow "acb" either. – Menno May 06 '13 at 12:26
  • Since I've extended the question I will temporarily remvoe the `accepted` mark so other people will give it a try too. Thanks for you help so far +1! – Menno May 06 '13 at 12:33
  • upvoted this one as well as th accepted answer bc it shows an easy, explicit, working variation, which might b easier for noobs to grasp n impl, eg: [^a][^b][^c]$ – Nick Humphrey Sep 22 '16 at 11:49
  • I'm pretty confused. You all seem to really know what you are doing--way more than me. However, the method in this answer does not work at all in my tests with both `grep` and [Regex101](https://regex101.com). See my [answer below](https://stackoverflow.com/a/54794803/188963) and tell me where I'm going wrong. – abalter Feb 20 '19 at 20:37
  • For multiple characters this answer does not work, if not wanting to match `.tmp` then `/[^.][^t][^m][^p]$/.test('file.txt')` returns false even though it doesn't end in .tmp. – Michael May 24 '23 at 16:17
77

To search for files not ending with ".tmp" we use the following regex:

^(?!.*[.]tmp$).*$

Tested with the Regex Tester gives following result:

enter image description here

Philipp
  • 4,645
  • 3
  • 47
  • 80
  • 3
    This is interesting, any idea why this works and why `^.*(?![.]tmp$)` doesn't? – Łukasz Zaroda Aug 13 '17 at 19:48
  • 9
    Your early `.*` does already match the whole string, so the remaining exclusion does not work anymore. – Philipp Aug 15 '17 at 19:25
  • For my purposes, this worked and the other answers didn't. Thanks! – David Moritz Dec 14 '17 at 15:09
  • 1
    A bit late to reply, I know, but for anyone else wondering, as I was, regarding @ŁukaszZaroda question.. it could be because of end of line characters ("\n" and "\r") – The Student Soul Oct 09 '20 at 16:59
  • @ŁukaszZaroda What bamboozled me is that we use `lookahead` BEFORE the thing we are actually searching for, which makes 0 sense. But then I noticed `^` start of the line token. And its actually the thing we are looking ahead from. Now this pattern makes much more sense. – ScienceDiscoverer Jul 10 '23 at 13:31
9
.*[^a]$

the regex above will match strings which is not ending with a.

Kent
  • 189,393
  • 32
  • 233
  • 301
  • I've extended my question since the original example didn't seem to fully match my case. Can you solve it? – Menno May 06 '13 at 12:30
6

Try this

/.*[^a]$/

The [] denotes a character class, and the ^ inverts the character class to match everything but an a.

JesperE
  • 63,317
  • 21
  • 138
  • 197
4

The accepted answer is fine if you can use lookarounds. However, there is also another approach to solve this problem.

If we look at the widely proposed regex for this question:

.*[^a]$

We will find that it almost works. It does not accept an empty string, which might be a little inconvinient. However, this is a minor issue when dealing with just a one character. However, if we want to exclude whole string, e.g. "abc", then:

.*[^a][^b][^c]$

won't do. It won't accept ac, for example.

There is an easy solution for this problem though. We can simply say:

.{,2}$|.*[^a][^b][^c]$

or more generalized version:

.{,n-1}$|.*[^firstchar][^secondchar]$ where n is length of the string you want forbid (for abc it's 3), and firstchar, secondchar, ... are first, second ... nth characters of your string (for abc it would be a, then b, then c).

This comes from a simple observation that a string that is shorter than the text we won't forbid can not contain this text by definition. So we can either accept anything that is shorter("ab" isn't "abc"), or anything long enough for us to accept but without the ending.

Here's an example of find that will delete all files that are not .jpg:

find . -regex '.{,3}$|.*[^.][^j][^p][^g]$' -delete

MatthewRock
  • 1,071
  • 1
  • 14
  • 30
1

The question is old but I could not find a better solution I post mine here. Find all USB drives but not listing the partitions, thus removing the "part[0-9]" from the results. I ended up doing two grep, the last negates the result:

ls -1 /dev/disk/by-path/* | grep -P "\-usb\-" | grep -vE "part[0-9]*$"

This results on my system:

pci-0000:00:0b.0-usb-0:1:1.0-scsi-0:0:0:0

If I only want the partitions I could do:

ls -1 /dev/disk/by-path/* | grep -P "\-usb\-" | grep -E "part[0-9]*$"

Where I get:

pci-0000:00:0b.0-usb-0:1:1.0-scsi-0:0:0:0-part1
pci-0000:00:0b.0-usb-0:1:1.0-scsi-0:0:0:0-part2

And when I do:

readlink -f /dev/disk/by-path/pci-0000:00:0b.0-usb-0:1:1.0-scsi-0:0:0:0

I get:

/dev/sdb
tombert
  • 111
  • 3
0

Anything that matches something ending with a --- .*a$ So when you match the regex, negate the condition or alternatively you can also do .*[^a]$ where [^a] means anything which is not a

Bill
  • 5,263
  • 6
  • 35
  • 50
0

If you are using grep or sed the syntax will be a little different. Notice that the sequential [^a][^b] method does not work here:

balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n'
jd8a
8$fb
q(c
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^a]$"
8$fb
q(c
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^b]$"
jd8a
q(c
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^c]$"
jd8a
8$fb
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^a][^b]$"
jd8a
q(c
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^a][^c]$"
jd8a
8$fb
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^a^b]$"
q(c
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^a^c]$"
8$fb
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^b^c]$"
jd8a
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^b^c^a]$"

FWIW, I'm finding the same results in Regex101, which I think is JavaScript syntax.

Bad: https://regex101.com/r/MJGAmX/2
Good: https://regex101.com/r/LzrIBu/2

abalter
  • 9,663
  • 17
  • 90
  • 145