1

On the command-line *tmp* will match all files with names containing "tmp". Is there a quick way to do the reverse, i.e. match all files with names that don't contain "tmp"?

I figured out how to get ls to do it (ls -I "*tmp*"), but that doesn't help if I want to use some other command rather than ls. Is there a general method?

I forgot to note: I'm using zsh.

weronika
  • 2,561
  • 2
  • 24
  • 30

2 Answers2

2

When using bash, you can enable extglob with:

shopt -s extglob

then you can use:

!(*tmp*)

that will negate your wildcard condition.

you can finally disable extglob with shopt -u extglob.

Giorgi Tsiklauri
  • 9,715
  • 8
  • 45
  • 66
  • 1
    Requires `shopt -s extglob` in bash. – glenn jackman Oct 22 '20 at 16:58
  • I'm not sure I'm aware of that, but generally, this negation should work in both, NIX and Win OSes. – Giorgi Tsiklauri Oct 22 '20 at 16:58
  • 1
    It's documented for bash [in the manual](https://www.gnu.org/software/bash/manual/bash.html#Pattern-Matching) where it says "If the `extglob` shell option is enabled using the `shopt` builtin, several extended pattern matching operators are recognized..." – glenn jackman Oct 22 '20 at 17:02
  • @glennjackman added that part as well. Thank you. – Giorgi Tsiklauri Oct 22 '20 at 17:04
  • @GiorgiTsiklauri It very much matters which shell you are using, regardless of OS. `!(*tmp*)` will *not* work in `dash`, for example. – chepner Oct 22 '20 at 17:06
  • @chepner that's true; however, unfortunately I'm not aware of each implementation OP might want to consider. :) So, simple negation I provided should work in plenty of those.. I guess. If you have a better answer, please propose. – Giorgi Tsiklauri Oct 22 '20 at 17:08
  • `shopt extglob` does *not* enable the extended patterns; it simply reports the current setting of the option. The `-s` is *required*. – chepner Oct 22 '20 at 17:08
  • Stop guessing: make your answer *factual*. Be clear about which shell you are assuming. – chepner Oct 22 '20 at 17:09
2

It depends on your shell.

In ksh, you can use this:

 !(*tmp*)

In bash, the same thing works if you first enable the feature with shopt -s extglob.

In zsh, you can enable the same syntax with setopt ksh_glob, but there's a conflict with another zsh feature that you have to disable with setopt no_bare_glob_qual before the above will actually work. Alternatively, you can just use zsh's native version via setopt extended_glob; the equivalent of the above expression then looks like this:

   ^*tmp*
Mark Reed
  • 91,912
  • 16
  • 138
  • 175
  • Hmm, are you sure about the second option? I use zsh, I did setopt extended_glob, but ^*tmp* doesn't seem to work (it lists everything in all subdirectories instead). Possibly some other setting I have is conflicting with it? Is there a way to figure this out? – weronika Oct 22 '20 at 18:11
  • Could it be that there aren't any files that don't match `*tmp*` and you have `nullglob` set? That would cause `^*tmp*` to expand to nothing at all, and if you run `ls` with no arguments it shows everything in the directory. – Mark Reed Oct 22 '20 at 18:49
  • Ah, I see what happens, I didn't scroll up far enough - it does list all files that don't match `*tmp*`, but then it also lists the contents of all the folders that don't match `*tmp*`, which I suppose is the expected behavior (i.e. it's acting like `ls *` rather than like `ls .`). This is moving off the original question, but don't suppose there's an easy way to exclude folders? – weronika Oct 22 '20 at 19:11
  • Just pass the `-d` option to `ls` so that it lists directory names but not their contents. – Mark Reed Oct 22 '20 at 19:16
  • Right, but I'm often using other commands instead of ls (mostly an image viewer etc) and they don't have that option, so it'd be nice if it was possible to do with shell wildcards somehow. – weronika Oct 22 '20 at 19:20
  • 1
    you might want to do something like `$(find . -maxdepth 1 ! -type d ! -name '*tmp*')` which is not a shell built-in but should give you the desired results. – Mark Reed Oct 22 '20 at 19:25
  • Aha, the easy practical solution is `(^*tmp*).png` whenever the files I want all have the same extension, which is usually the case. Thanks for helping me figure it out! – weronika Oct 22 '20 at 20:19