Bash – Trouble w/ bash programmable completion of filenames matching patterns

autocompletebash

I've got a simple command named th. I've got a bash-completion file for it that goes as follows:

_th()  {
  COMPREPLY=()
  local curr=${COMP_WORDS[COMP_CWORD]}
  COMPREPLY=( $(compgen -f -X '!*.@(tar|tar.xz|tar.bz2|tar.gz|tbz|tgz)' -- "$curr") )
}                
complete -F _th -o filenames th

This works as intended, for the most part. For arguments to my th function, BASH only tab-completes files ending in .tar, .tar.xz, etc. Two problems:

  1. The matching is case-sensitive. Not all tar files have lower-case extensions. (Using shopt to set nocaseglob inside the bash-completion file doesn't work.)

  2. The matching chokes on multi-word filenames, e.g. files with spaces in their names.

    [rsaw:blahb]$ ls
    bam.tgz  boom.TGZ  sad.TAR  spaces in this-file.tbz
    [rsaw:blahb]$ th<tabtab>
    bam.tgz        in             spaces         this-file.tbz  
    [rsaw:blahb]$ th spa<tabtab>
    in             spaces         this-file.tbz  
    [rsaw:blahb]$ th spaces<tabtab>   # At this point I added a backslash and pressed tab until 4 backslashes appeared
    in             spaces         this-file.tbz  
    [rsaw:blahb]$ th spaces\\\\<tabtab>   # At this point, nothing else happened 
    

Any help appreciated!

Best Answer

Not sure if this is still an issue for you but I ran into something similar while trying to get mid-word filename completion and have some ideas for possible solutions.

Changing the IFS variable by adding local IFS=$'\n' fixed the problem with spaces in filenames for me, might be worth a try.

As for making things case-insensitive, you could force $curr to lowercase for compgen by using ${cur,,}. Full function would look like this after both changes:

_th()  {
  local IFS=$'\n'
  local curr=${COMP_WORDS[COMP_CWORD]}
  COMPREPLY=( $(compgen -f -X '!*.@(tar|tar.xz|tar.bz2|tar.gz|tbz|tgz)' -- "${curr,,}") )
}                
complete -F _th -o filenames th

That would make the whole filename matching case-insensitive. If you only want the extension case insensitive you could do some string manipulation to $cur to make only the extension lowercase.

Related Question