Sign up ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

How can I tweak zsh's completion system to complete “fake” files in some circumstances?

More precisely, the AVFS filesystem exposes archives as directories by creating a “fake directory” next to every archive. By default, it replicates the whole directory hierarchy under its mount point ~/.avfs. Furthermore, under ~/.avfs, for every archive file such as /tmp/, besides the archive file itself ~/.avfs/tmp/, there is a directory called ~/.avfs/tmp/ which exposes the content of the archive. However this extra directory does not appear in the listing of ~/.avfs/tmp: it only exists when explicitly requested. (This is similar to how some automounters operate.)

cd ~/.avfs/tmp/\#

When I type a command like the one above, doesn't appear as a completion under ~/.avfs/tmp/, since there is no such directory. How can I tell zsh that whenever there is a file whose full path $arc matches ~/.avfs/**/*.(tgz|zip)¹, it should pretend that there is a directory called ${arc}#?

(Note that I want the completion to work with every call to _files or _dirs, not just for the cd command. I'd prefer not to have to duplicate the code of the _files function, if possible.)

¹ and all the rest of the extensions

share|improve this question
Does ~/.avfs contain a "copy" of the whole file-system? – richard Jun 16 '14 at 14:13
@richard Yes, every /path/to/foo is visible as ~/.avfs/path/to/foo. – Gilles Jun 16 '14 at 15:46
This might be easier fixed in avfs. If it presented the default # directory entry for any files that matched the strings it supports, or even magic tested the files in a directory for what needed to have a # entry, then the completion would just work. I guess there would be a performance hit for something like find as it would now trawl through compressed files. – Matt Jul 21 '14 at 14:56

2 Answers 2

A fake answer appears insufficient for this fake question; the zsh builtin support for automount directories operates on directories (fake-files dir:names), and not on file patterns therein. This is handy to add specific named files to a directory, which may suit automount or sshfs or NetApp .snapshot type setups where the directory-to-be-mounted is a known static name, ALAS.

autoload -U compinit
zstyle ':completion:*' fake-files $HOME/.avfs:'ALAS POOR YORICK'

zshall(1) says the "names" are strings, and experiments indicate metacharacters (e.g. # or \# or '*(e:"echo hi":)') "do not work" in various ways, so there is no way I know of to sneak a glob onto the names portion of the fake-files statement. (Delving through Src/Zle/computil.c might reveal exactly what the names can be, but that would be more work.) (Also, using recursive globs in the directory position to name archive files did not fly, but again it will take C diving to see how much fake-files lets you get away with.)

With compdef, one can list the .avfs directory completions:

compdef '_files -g $HOME/.avfs/\*' -p \^
function andthehash {
  reply=($REPLY $REPLY\#)
compdef "_files -g $HOME/.avfs/'*(+andthehash)'" -p \^

Except this fails, as it will only show the # files when it does not matter, and thus cannot complete on them. (There are productive uses for this form, though not for this case.)

Using zstyle gets you closer, with the following .zshrc:

autoload -U compinit

function UnderAVFS {
  local pdir
  [[ -z $1 ]] && return 1
  [[ ! -d $pdir ]] && pdir=$pdir:h
  [[ ! -d $pdir || $pdir == . ]] && return 1
  while [[ $pdir != / ]]; do
    [[ $pdir == $HOME/.avfs ]] && return 0
  return 1

function AVFSExtras {
  if UnderAVFS $REPLY; then
    # TODO embiggen list of archive suffixes
    [[ $REPLY == *.(bz2|tbz2) ]] && reply+=($REPLY\#)

# Try to match avfs, otherwise the default pattern. Probably could greatly
# benefit from caching, to avoid hits on UnderAVFS function.
zstyle ':completion:*' file-patterns '*(+AVFSExtras):avfs-files' '%p:all-files'

However, it can only complete up to the \#/ bit (on a debian squeeze virt with a default avfs setup), but not into the virtual filesystem for an archive, so additional work will be necessary to tab complete into the archives. I'm guessing via _call_program ... ls or something along those lines for completions at the \# bit, unless there's a more elegant way to con zsh into believing that directories that do not exist do.

Bad news! The completion does not make it through _path_files for the fake directories; I suspect this is zsh globbing related. Both zsh 4.something on and zsh 5.0.8 exhibit the problem. So I suspect that a complete fix would require patches to _path_files, or to write something different that otherwise completes on these fake dirs. A completion debugging trace can be generated by typing in ls /root/.avfs/root/sometar.gz\#/ and then at the end of that typing control-x and then ?, assuming bindkey -e and that _complete_debug is bound to said keycombo, if someone wants to delve through why this is failing.

share|improve this answer

In zsh you can use compctl -K to register your own function to generation completion options. For example:

compctl -f -c -u -r -K myAVFScompletion "*" -tn

Then you need to define your own function:

myAVFScompletion () {
    read -c COMMAND ARGS

    # Decide what paths you want to suggest completion for
    # by looking inside the ~/.avfs folder.

    # Place your suggestions into an array:
    reply=( a_path b_path ... )

Obviously the above needs some more work, but it's something to start with.

A similar (but different) custom completion function can be registered for bash.

Here is an example with implementations for bash and zsh. (It offers completion of command-line options by grabbing the options out of the command's man page.)

And here are some quick guesses at things that might work inside your completion function:

if [[ -d "$ARGS#" ]]
then reply=( "$ARGS#" )
else reply=

Or perhaps this:

reply=( $(find ~/.avfs -type f -name "*.tgz" -or -name "*.zip" | sed 's+$+#+') )

Good luck!

share|improve this answer
I know how to write a completion function, thank you very much. The problem is how to modify the existing completion mechanism for files. Note that I use the “new” completion mechanism, not compctl, i.e. I want to change what _files does. Your answer is not helpful at all. – Gilles Jun 5 at 12:52
@Gilles Not to start a flame war, and at the risk of coming off wrong, at least he tried... This has been sitting in the unanswered "attic" 2 years plus... +1 t reset to 0 for the effort. – eyoung100 Jul 24 at 21:59
@eyoung100 Please do not give +1 for effort (not that effort is apparent here). Upvoting unhelpful answers is detrimental to the site. – Gilles Jul 24 at 22:26

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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