I have a Bash tab-completion script for Apache's Hadoop. Normally, I use zsh as my day-to-day shell. It tends to be pretty bash-like when I need it to be, but it looks like the tab-completion systems are radically different between them. Is there a simple way to "convert" the existing bash-tab-completion definitions to work in zsh? I don't want to invest a ton of time in this, but if it's easy I'd save a moderate amount of effort.
From this page (dated 2010/01/05):
Zsh can handle bash completions functions. The latest development version of zsh has a function bashcompinit, that when run will allow zsh to read bash completion specifications and functions. This is documented in the zshcompsys man page. To use it all you need to do is run bashcompinit at any time after compinit. It will define complete and compgen functions corresponding to the bash builtins.
/etc/zsh/zshrc
. Also, check the following files in reverse order (listed in the order they are sourced during zsh startup): /etc/zsh/zshenv
, $ZDOTDIR/.zshenv
, /etc/zsh/zprofile
and $ZDOTDIR/.zprofile
(probably ~/.zprofile
) to see if the RCS
variable is unset. If it is, that would prevent the subsequent files after the file in which it's unset from being sourced. Finally, check the shell for the user in /etc/passwd
and make sure it's zsh
and make sure it doesn't have a -f
argument. –
Bock autoload bashcompinit
bashcompinit
source /path/to/your/bash_completion_file
complete:13: command not found: compdef
error. –
Yell if is-bash
check at the top. –
Mitzvah _get_comp_words_by_ref
which is not defined by bashcompinit
. Most completions don't use this function though. –
Ogdan autoload -U +X compinit && compinit
autoload -U +X bashcompinit && bashcompinit
source /path/to/your/bash_completion_script
I am running zsh zsh 5.0.2 (x86_64-apple-darwin13.0)
without any ~/.zshrc and the above sequence worked in a freshly spawned zsh shell.
Thanks to git-completion.bash script for the hint :D
Read-on for more details on above 3 lines:
Bash has awesome in built auto completion support but the bash autocomplete scripts don't work directly zsh as zsh environment doesn't have the essential bash autocomplete helper functions like compgen
, complete
. It does so in an effort to keep zsh session fast.
These days zsh is shipped with appropriate completion scripts like compinit
and bashcompinit
which have the required functions to support bash autocomplete scripts.
autoload <func_name>
: Note that autoload is defined in zsh and not bash. autoload
looks for a file named in the directory paths returned by fpath
command and marks a function to load the same when it is first invoked.
- -U: Ignore any aliases when loading a function like compinit or bashcompinit
- +X: Just load the named function fow now and don't execute it
For example on my system echo $fpath
returns /usr/share/zsh/site-functions
and /usr/share/zsh/5.0.5/functions
and both compinit
and bashcompinit
are available at /usr/share/zsh/5.0.5/functions
.
Also for most people may be only autoload -U +X bashcompinit && bashcompinit
is required because some other script like git autocomplete or their own ~/.zshrc
may be doing autoload -U +X compinit && compinit
, but it's safe to just run both of them.
-U
and +X
for? –
Jacindajacinta +X foo
as doing the same auto-loading just invoking foo
does, but without immediately executing it … so autoload +X foo && foo
seems completely redundant. Can you explain why you chose this pattern? –
Amylopectin autoload -U +X bashcompinit && bashcompinit
is that (due to the &&
) it will execute bashcompinit
IFF the module is actually found. Normal autoload foo
always returns true. Later, when you try to run foo
, then zsh finally tells you "function definition file not found". With autoload +X
you get the error immediately. –
Lynnalynne autoload +X
loads the function without executing it — which is more than autoload
does, without +X
. Without +X
, autoload doesn't even load the function; It merely marks the name as referencing a function to be loaded later. The actual loading isn't attempted until the function is first invoked. If you want to determine whether the named function successfully loads, you need to use +X
to perform the loading step immediately. –
Lynnalynne bash
(which I have used since the late 90s) because I didn't understand details like what you just explained in our comments. Thank you! I actually get it and can make decades worth of my tools work in zsh
with this new understanding. ☮️❤️🌈🧘🏽 –
Vaud echo *(ah-5)
would echo files accessed within the last five hours, while echo *(ah+5)
would echo files accessed at least six hours ago, as times strictly between five and six hours are treated as five hours." ...Guh? I'm going with "definitely both", on that one. –
Lynnalynne From this page (dated 2010/01/05):
Zsh can handle bash completions functions. The latest development version of zsh has a function bashcompinit, that when run will allow zsh to read bash completion specifications and functions. This is documented in the zshcompsys man page. To use it all you need to do is run bashcompinit at any time after compinit. It will define complete and compgen functions corresponding to the bash builtins.
/etc/zsh/zshrc
. Also, check the following files in reverse order (listed in the order they are sourced during zsh startup): /etc/zsh/zshenv
, $ZDOTDIR/.zshenv
, /etc/zsh/zprofile
and $ZDOTDIR/.zprofile
(probably ~/.zprofile
) to see if the RCS
variable is unset. If it is, that would prevent the subsequent files after the file in which it's unset from being sourced. Finally, check the shell for the user in /etc/passwd
and make sure it's zsh
and make sure it doesn't have a -f
argument. –
Bock For zsh
use:
compdef
compadd
My example:
# bash completion for bxrun (/home/ecuomo/projects/bashx/bxrun)
_bxrun_methods() {
grep "^\s*\(function\s\+\)\?__.\+()\s*{.*$" "${1}" | while read line ; do
echo "$line" | sed "s/()\s*{.*//g" | sed "s/\s*\(function\s\+\)\?__//g"
done
}
_bxrun_lst() {
if [ -d "/home/ecuomo/projects/bashx/src/actions" ]; then
for f in /home/ecuomo/projects/bashx/src/actions/* ; do
if [ -f "${f}" ]; then
basename "${f}" | sed 's/\..*$//g'
fi
done
fi
_bxrun_methods "/home/ecuomo/projects/bashx/bxrun"
_bxrun_methods "/home/ecuomo/projects/bashx/src/bashx.sh"
}
_bxrun() {
local cur
COMPREPLY=()
cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $( compgen -W '$( _bxrun_lst )' -- $cur ) )
}
_bxrun_zsh() {
compadd `_bxrun_lst`
}
if type complete >/dev/null 2>/dev/null; then
# bash
complete -F _bxrun bxrun
else if type compdef >/dev/null 2>/dev/null; then
# zsh
compdef _bxrun_zsh bxrun
fi; fi
Source: My code https://github.com/reduardo7/bashx
I am running Antigen as a Oh-My-Zsh plugin manager. I had a few bash completion scripts written by coworkers that I wanted to load into Zsh with a simple source /path/to/completion
.
I had some trouble, because it seems like either Antigen or OMZ (hard to tell) concern themselves with only loading completion scripts from their plugins. I finally got around this by autoloading bashcompinit
and compinit
after antigen apply
. Simply autoloading bashcompinit
wasn't enough.
source ~/.antigen/antigen.zsh
antigen use oh-my-zsh
antigen apply
autoload -U +X compinit && compinit
autoload -U +X bashcompinit && bashcompinit
source /path/to/bash_completion
Antigen creates its .zcompdump
file at $ANTIGEN_COMPDUMP
which for me was ~/.antigen/.zcompdump
The re-invoke of compinit and bashcompinit create a second .zcompdump at $HOME/.zcompdump
That seems to all work out, because I am able to use the completions set up by /path/to/bash_completion
. I've deleted both .zcompdump files a few times to make sure they're regenerated and seems to work.
I've had to rm the .zcompdump files a few times after a machine reboot because of errors thrown when trying to tab complete, but I'm unsure if that's due to this set up or something else. rm ~/.zcompdump && rm $ANTIGEN_COMPDUMP
and a new shell fixes that for me.
Versions used at time of writing:
Antigen = v2.2.3 = d3d4ee0
Oh-my-zsh = c3b072e
Zsh = 5.3
@JatinKumar's answer got me on the right track, but I had to use complete
instead of source
. So all together:
autoload -Uz compinit && compinit
autoload -U +X bashcompinit && bashcompinit
complete -C /usr/local/bin/terraform terraform
complete -C /usr/local/aws/bin/aws_completer aws
complete -C /usr/local/bin/az az
complete
builtin (which bashcompinit
installs an emulator for) say that complete -C <command>
means: "command is executed in a subshell environment, and its output is used as the possible completions." So, with those commands, you're just saying, "When I go to complete terraform
, display the output of running /usr/local/bin/terraform
with no arguments." That can be useful at times (since most commands will output a usage statement), but it's not the same as real completion. –
Lynnalynne © 2022 - 2024 — McMap. All rights reserved.