2

My zshrc includes the following function to create a directory and then enter it:

function mcd () {
    mkdir -p "$*" && cd "$*"
}

The function itself works fine but I get odd behavior with completion. If I start typing e.g. mcd ~/ and then press Tab, the message

_mtools_drives:3: command not found: mtoolstest

is inserted at the insertion point and nothing is completed. What I want is for the command to be completed just as mkdir would be: zsh should offer me the names of existing directories.

How do I tell zsh that for completion purposes, it should treat mcd the same as mkdir?

2

The following snippet causes mcd to be completed like mkdir:

compdefas () {
  if (($+_comps[$1])); then
    compdef $_comps[$1] ${^@[2,-1]}=$1
  fi
}
compdefas mkdir mcd

The way it works is to look up the current completion setting for mkdir. The completion code for a function (generally the name of a completion function) is stored in the associative array _comps. Thus compdef $_comps[mkdir] mcd declares that mcd should be completed in the same way that mkdir is completed right now.

The function above adds a few niceties:

  • The test for (($+_comps[$1])) ensures that if $1 doesn't have a specified completion method then no completion method is set for the other arguments.
  • ${@[2,-1]} is the list of arguments to the function starting with the second one, so you can specify more than one command name to define completions for. It's actually ${^@[a,-1]} so that the text around the array expansion is replicated for each array element.
  • =$1 sets the service name to use. This matters only for a few commands whose completion function handles several closely-related commands. For example the completion function _gzip handles both gzip and gunzip as well as pigz and unpigz; compdef _gzip foo makes foo use the default behavior of _gzip while compdef _gzip foo=pigz makes foo use the behavior of _gzip when it completes for pigz.

Turning to your specific case, the default completion for mkdir not only offers directories, but also options, which your function does not support. So you'd actually be better off defining mcd as just completing existing directories. Zsh comes with a helper function for that (an undocumented wrapper around _files).

compdef _directories mcd

The reason you were getting these bizarre-looking completions for mcd is that it's the name of a command from a once moderately widespread suite of commands mtools.

  • If I issue command mcd without any argument, then it echos mkdir: cannot create directory ‘’: No such file or directory. mkdir will give mkdir: missing operand which look nicer than our mcd, how to have the same behavior as mkdir? – Tuyen Pham Jan 27 at 6:31
  • 1
    @TuyenPham See if my function inspires you. – Gilles Jan 27 at 18:54
  • Thanks, that's truly mkdir wrapper, but should we still need to have compdefas? As I test your mkcd function, it works well with completion. – Tuyen Pham Jan 28 at 2:03
1

compdef uses the following form:

compdef [ -ane ] function name

so one idea would be to point the name of mcd at the _mkdir function via the following command:

compdef _mkdir mcd

however this is slightly incorrect as mkdir completes various flags that should probably not also be given to cd. More direct would be to complete on directories:

compdef _directories mcd

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.