Let's say I need to find all files in /var/log/ except btmp, lastlog, wtmp. And I only need depth 1. I tried with the following find command, but that does not work as expected:

find /var/log/ -maxdepth 1 -type f \! -name '(btmp|lastlog|wtmp)'
  • -name arguments are interpreted as shell glob patterns, not a regex. You need -regex if your find supports it: check your manpage. That's a really odd set of find parameters, by the way. You could accomplish the same with bash's -extglob feature, as in /var/log/!(btmp|lastlog|wtmp) (still a glob, not a regex).
    – jw013
    Commented Dec 13, 2013 at 20:47

5 Answers 5


-name doesn't take a regex, it takes a literal name, potentially with globs. You can either use -regex instead (possibly a gnu extension?) or add the three separately

find ... \! -name btmp \! -name lastlog \! -name wtmp

Or you can skip find entirely and use bash's extended globbing.

shopt -s extglob
echo !(btmp|lastlog|wtmp)

This should accomplish what you want:

find /var/log/ -maxdepth 1 -type f \! -regex '/var/log/\(btmp\|lastlog\|wtmp\)' -exec ls {} \;

Here's another way to do it:

ls -1 /var/log/ | grep -v 'btmp\|lastlog\|wtmp'

or to be sure not to include directories:

find /var/log/ -maxdepth 1 -type f | grep -v 'btmp\|lastlog\|wtmp'

This will also work.

find /var/log -maxdepth 1 -regextype posix-extended -type f ! -regex '^.*/(btmp|wtmp|lastlog)$'
find /var/log ! \
      \( -name \[wb]tmp -o \
         -name lastlog  -o \
         -path '*g/*/*/*'  \
         -prune -o -type d \)

That will print only pathnames which are not of type directory, of basenames wtmp, btmp, or lastlog, or are any deeper than a single directory below a directory name ending with a g.

  • 1
    find . -path "*g/*" -a ! -path "*g/*/*"
    – user286944
    Commented Sep 24, 2019 at 20:44

You must log in to answer this question.

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