13

When I type

unzip <tab>

I get a list of all files ending in .zip, which is the standard behavior of tab completion for the unzip command.

However, I also want bash completion for unzip list files which end in .udp, in addition to .zip files. How can I achieve this behavior in my local .bashrc?

4 Answers 4

25

Instead of keeping on commenting …


complete -f -X '!*.@(zip|udp)' unzip

should add completion for

-f -X '!*.@(zip|udp)'
 |  |  | |||________|
 |  |  | ||     |
 |  |  | ||     +- filterpat: zip or udp
 |  |  | |+-------------  @ : Matches one of the given patterns
 |  |  | +-------------- *. : Anything+<dot>
 |  |  +----------------  ! : Negate
 |  +------------------- -X : Filter out matches in "filterpat"
 +---------------------- -f : files

In other words: Complete files and remove all not ending in .zip or .udp.


Extras

If you add -o default completion will complete/match all files and directories if there is no files ending in .zip or .udp.

If you add -o plusdirs completion will add any directories in addition to any matches of files ending in .zip or .udp.

Current

When you use complete -p unzip you get current pattern.

From your comments it sounds mostly like you are missing either + or @ in pattern, as in:

# Err:
complete -f -X '!*.(zip|udp)' unzip
                   |
                   +---- Missing + or @

which would mean match any file literally ending in .(zip|udp). E.g.

touch 'file_test.(zip|udp)'

Also have a look at this section of the manual:

it is possible e.g. extglob is not enabled. Enable by:

shopt -s extglob

Check current status of all shopt settings by entering:

shopt

Function

If that is -F something it means it uses a function named something to generate the list of completions.

The _filedir_xspec is typically a Debian function. You might have something like this:

$ cat /etc/bash_completion
. /usr/share/bash-completion/bash_completion

Which means /usr/share/bash-completion/bash_completion get sourced. Here you'll find the function in question. A few lines further down you see that this function / completion is added by a function named _install_xspec by e.g.:

_install_xspec '!*.@(zip|[ejsw]ar|exe|pk3|wsz|zargo|xpi|s[tx][cdiw]|sx[gm]|o[dt][tspgfc]|od[bm]|oxt|epub|apk|do[ct][xm]|p[op]t[mx]|xl[st][xm])' unzip zipinfo

Manual entries:


Update to comments 1:

– No match due to invalid archives or file permissions.

This should not affect the result. complete only matches files given by the rules and do no processing of the files. If you can list them with ls, they should match.

As a side note one could add such a functionality by using a complete function:

function _unzip_validated() {
    # 1. Generate list of files ending in e.g. .zip and .udp
    # 2. Validate each file and remove invalid ones from file list.
}

complete -F _unzip_validated unzip

– Colors. Why does ls distinguish test.zip from test.upd?

This does not affect complete. Colors by ls are provided by $LS_COLORS. Try:

echo "$LS_COLORS" | tr : '\n' | sort

You should see something like *.zip=01;31 which means:

*.zip=01;31
|____| |  |
   |   |  +--- Red
   |   +------ Bold
   +---------- Files with .zip extension

*.udp on the other hand has no entry associated with it, so no coloring.


Next step (should have been first)

  1. Open terminal and go to a directory with test files. Both .zip and .udp
  2. Enter bash --norc
  3. Enter complete -f -X '!*.@(zip|udp)' unzip
  4. Enter shopt -s extglob
  5. Enter unzip <tab><tab>

Result?

Also, click edit below your question and add output of:

bind -V
shopt
env

might be helpful.

5
  • Thank you very much for your excellent and lengthy explanation, but still it does not work. complete -p unzip yields complete -f -X '!*.@(zip|udp)' unzip, and unzip <tab> completes the file named test.zip. However, the directory contains a file named test.upd as well which is not found by tab completion of unzip. Do the fact of file permissions or a zero length of the file maybe explain this weird behavior?
    – Alex
    Jan 10, 2014 at 6:01
  • @Alex: Should get completion on both empty and valid files, as well as on files where one do not have permission. Is it the same behavior if you add it to a test script? E.g. 1. some test script with execution set: ~/bin/testing, 2. complete -f -X '!*.@(zip|udp)' testing, 3. testing <tab><tab> ?
    – Runium
    Jan 10, 2014 at 6:22
  • @Alex: You could also update Q with output of shopt
    – Runium
    Jan 10, 2014 at 9:27
  • extglob is activated. The example with 'testing' selects the zip file only again. I notice that test.zip is shown in red color, while test.upd is shown in white color with ls -l, both files empty with same permissions, ownershift and group association. Why does ls distinguish test.zip from test.upd? Maybe this is a hint towards the problem I have?
    – Alex
    Jan 10, 2014 at 12:19
  • @Alex: Added an update.
    – Runium
    Jan 11, 2014 at 0:09
3

In the file /etc/bash_completion, which you are presumably sourcing somewhere, the default completion, at least for my version of bash (v4.2.25) is

complete -f -X '!*.@(zip|[ejw]ar|exe|pk3|wsz|zargo|xpi|sxw|o[tx]t|od[fgpst]|epub|apk)' unzip zipinfo

Try changing the entire expression to

complete -f -X '!*.@(zip|[ejw]ar|exe|pk3|wsz|zargo|xpi|sxw|o[tx]t|od[fgpst]|epub|apk|udp)' unzip zipinfo

And add to your .bash_profile

5
  • This does not solve my problem. If I do so and source it, unzip <tab> neither finds .zip nor .upd files nor other files.
    – Alex
    Jan 9, 2014 at 6:08
  • Try hitting tab twice, see if that's any different. Do other completions work as expected? For example gzip with .gz files?
    – bsd
    Jan 9, 2014 at 11:54
  • Hitting tab twice does not change anything. Using gunzip with .gz files select only files ending in .gz, but the definition is also different: complete -F _filedir_xspec gunzip.
    – Alex
    Jan 9, 2014 at 12:35
  • Do you source the file /etc/bash_completion in your .bashrc ?
    – bsd
    Jan 9, 2014 at 18:27
  • Try shopt -s extglob Jun 24, 2016 at 1:07
2

Try adding this line in your .bashrc:

complete -f -o default -X '!*.+(zip|udp)' unzip
8
  • It seems with this suggestion I will find .udp files, but only if no .zip files are present. If a test.zip and test.udp file is present in the current directory, unzip <tab> only lists the .zip file. I would expect to see both files (i.e. the bash completion would give test.).
    – Alex
    Jan 8, 2014 at 16:36
  • Are you sure? I checked on my bash and I seem to find both udp and zip files. Did you source .bashrc after adding the line? Jan 8, 2014 at 16:43
  • I repeated in a new terminal, and now unzip <tab> list all files, even ones ending in a different phrase. Now quite what I need.
    – Alex
    Jan 8, 2014 at 16:51
  • Strange.. I am running GNU bash version 4.2.25 and works as expected. Which version of bash are you on? Jan 8, 2014 at 17:02
  • Could you try adding two separate lines: complete -f -o default -X '!*.zip' unzip and complete -f -o default -X '!*.udp' unzip Jan 8, 2014 at 17:06
1

Better use _filedir instead of "raw" complete.

Justification

  • grep _filedir -r $(pkg-config --variable=completionsdir bash-completion) | wc -l
  • tilde (~) paths are being expanded
  • Prefixes are removed for directories, i.e. for /home/tux/Do<TAB><TAB>, the list you get as a reply removes '/home/tux' and is thus much more compact
  • Easier to implement and more failsafe

MWE

_unzip_completor()
{
    # init bash-completion's stuff
    _init_completion || return

    # fill COMPREPLY using bash-completion's routine
    _filedir '@(zip|udp)'
}
complete -F _unzip_completor unzip

You must log in to answer this question.

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