I am trying to tweak some completion. Suppose I have:

$ ls
bar.baz  foo_bar.baz  foo.bar

If I want completion to match all files that don't end in baz:

$ shopt|grep extglob
extglob         on
$ compgen -G "*.!(baz)"

The exclude glob works as expected. But if I want all files that don't start with foo:

$ compgen -G "!(foo)*"

All the files still match. Presumably this is because the * can still expand to match foo. Is there a glob trick to use exclusion followed by *?

As Janis pointed out, excluding foo needs to be !(foo*), but the actual use case is to also match on an extension, so the glob needs to be generally of the form !(foo*)*.bar (which still fails to exclude "foo" prefixes).


You have to include the * in the negation, e.g.:

ls !(foo*)

Edit (to clarify): The requested [extended] globbing pattern is !(foo*). If used with the command compgen the call would be, e.g.:

compgen -G "!(foo*)"

You need quotes to pass the pattern to the command compgen unexpanded.

Edit (after question change): For your complete pattern in your edited question try one of @(!(foo*)|*bar) or @(!(bar*)|*foo). (See my comment below if that isn't what you expect.)

  • This seems super obvious when I read it, but man bash for complete says "The arguments to the -G ... options ... should be quoted to protect them from expansion before the complete builtin is invoked." IIUC when used unquoted this expands and sets completion based only on current matches to the pattern (rather than setting the glob itself as a completion pattern). If I use compgen -G "!(foo)*" it still matches everything. – jake Mar 17 '15 at 2:18
  • @jake, have you tried compgen -G "!(foo*)" instead of the sample you posted? (The star should be inside the paranthesis not outside!) – Janis Mar 17 '15 at 3:13
  • Yes, sorry, that was a typo in my comment. compgen -G "!(foo*)*" returns all of the files in the above example, rather than just bar.baz, as expected. – jake Mar 17 '15 at 10:39
  • It works for just "!(foo*)" as shown in your answer, but my actual use case includes the extension ("match all files with extension .foo unless they start with bar") – jake Mar 17 '15 at 10:41
  • It may be something like @(!(foo*)|*bar) or @(!(bar*)|*foo). Though you confused me; in your edited question you write !(foo*)*.bar, but in your comment here you seem to say something that appears like the reverse. Please clarify what you mean; best supported with a set of significant test samples (like fo.bar, foo, fooo.bar, foooo, go.bar), and shown which ones shall be matched and which ones not. – Janis Mar 17 '15 at 16:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.