I am using sshpass command like this:

sshpass -p 'my_password' ssh user@server

The full syntax, according to the man page is

sshpass [-ffilename|-dnum|-ppassword|-e] [options] command arguments

At the moment, zsh does not have any completion rules for sshpass. How can I create simple completion rules, so that as soon as I reach the "command" and "arguments", ssh is completed for command and user@host is completed same as it would be for standard ssh ?

1 Answer 1


You can take inspiration from commands with similar syntax, such as sudo which also takes options followed by a command name and its options. See also Dynamic zsh autocomplete for custom commands for an introduction on writing zsh completions.

Put the following code in a file called _sshpass on your $fpath. (See How to properly make custom zsh completions "just work"? for more details on how zsh picks up completion functions.)

#compdef sshpass

_sshpass () {
  local context state line
  typeset -A opt_args

  _arguments \
    '(-d -e -f -p)-d+[read password from file descriptor]:file descriptor:_file_descriptors' \
    '(-d -e -f -p)-e[take password from $SSHPASS]' \
    '(-d -e -f -p)-f+[read password from file]:file:_files' \
    '(-d -e -f -p)-p+[actual password]::' \
    '(-)1:command:->command' \
    '*::arguments: _normal' \
    && return

  case $state in
      if ((CURRENT == 2)); then
        # Insist that the first argument must be an option
        compadd -- -d -e -f -p
        compadd ssh ||
        _command_names -e

_sshpass "$@"

Brief explanation:

  • _arguments arranges completion for a command with options.
  • The options -d, -e, -f and -p are mutually exclusive. (-d -e -f -p)-d… specifies completions for -d and states that this should not be offered if one of the options -d, -e, -f or -p is already present.
  • -d+ means that a space is optional between -d and its argument.
  • ->command for the first parameter causes _arguments to set $state to command and the execution continues below.
  • _normal for arguments after the first non-option argument causes them to be completed as if the command line started with the first non-option argument. The :: after * instructs zsh to strip options up the first non-option argument.
  • For the command name, try completing the name passed to compadd (you can add more names if you want). If this fails, complete any command name instead.
  • works great. Thank you. Commented Jul 12, 2020 at 18:43
  • why do we define opt_args, context and line> They don't seem to be used. Are they just leftovers form the template we used, or do they have any function here? Commented Jul 14, 2020 at 5:00
  • when I do sshpass <TAB>, it completes ssh. I think, it should first require one of the arguments -p, -e, -f, as sshpass does not work otherwise. How do I tell zsh to only complete the command, after one options has been provided? Commented Jul 14, 2020 at 6:33
  • @400theCat opt_args and the others are necessary to use the ->state feature of _arguments. See the documentation of _arguments. I've edited the function to insist that the first argument is an option. It isn't very clean, I'm sure there's a better way, but it does the job here. Commented Jul 14, 2020 at 11:01

You must log in to answer this question.

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