3

For a list of files, I'd like to match the ones not ending with .txt. I am currently using this expression:

.*(txt$)|(html\.txt$)

This expression will match everything ending in .txt, but I'd like it to do the opposite.


Should match:

happiness.html
joy.png
fear.src

Should not match:

madness.html.txt
excitement.txt

I'd like to get this so I can use it in pair with fswatch:

fswatch -0 -e 'regex here' . | xargs -0 -n 1 -I {} echo "{} has been changed"

The problem is it doesn't seem to work.

PS: I use the tag bash instead of fswatch because I don't have enough reputation points to create it. Sorry!

reevesy
  • 3,452
  • 1
  • 26
  • 23
stephane
  • 33
  • 1
  • 4

4 Answers4

3

Try using a lookbehind, like this:

.*$(?<!\.txt)

Demonstration

Basically, this matches any line of text so long as the last 4 characters are not ".txt".

p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
3

You can use Negative Lookahead for this purpose.

^(?!.*\.txt).+$

Live Demo

You can use this expression with grep using option -P:

grep -Po '^(?!.*\.txt).+$' file
hwnd
  • 69,796
  • 4
  • 95
  • 132
1

Since question has been tagged as bash, lookaheads may not be supported (except grep -P), here is one grep solution that doesn't need lookaheads:

grep -v '\.txt$' file
happiness.html
joy.png
fear.src

EDIT: You can use this xargs command to avoid matching *.txt files:

xargs -0 -n 1 -I {} bash -c '[[ "{}" == *".txt" ]] && echo "{} has been changed"'
anubhava
  • 761,203
  • 64
  • 569
  • 643
0

It really depends what regular expression tool you are using. Many tools provide a way to invert the sense of a regex. For example:

bash

# succeeds if filename ends with .txt
[[ $filename =~ "."txt$ ]]
# succeeds if filename does not end with .txt
! [[ $filename =~ "."txt$ ]]
# another way of writing the negative
[[ ! $filename =~ "."txt$ ]]

grep

# succeeds if filename ends with .txt
egrep -q "\.txt$" <<<"$filename"
# succeeds if filename does not end with .txt
egrep -qv "\.txt$" <<<"$filename"

awk

/\.txt$/ { print "line ends with .txt" }
! /\.txt$/ { print "line doesn't end with .txt" }
$1 ~ /\.txt$/ { print "first field ends with .txt" }
$1 !~ /\.txt$/ { print "first field doesn't end with .txt" }

For the adventurous, a posix ERE which will work in any posix compatible regex engine

/[^t]$|[^x]t$|[^t]xt$|[^.]txt$/

rici
  • 234,347
  • 28
  • 237
  • 341