Can zsh complete command flags? - shell

Is there some way to get zsh to complete long flag names on the command line?
$ command --reall<tab>
$ command --really-long-flag-name
Seems like a zshy thing to do.

The short answer is yes, or course it can.
To turn on zsh’s completion system, you need to do the following – probably in a startup file like your ~/.zshrc:
autoload -U compinit && compinit
On most modern Unix-like systems, once you do that you ought to find that many commands already have their flags and parameters completed, because zsh ships with a library of completion functions for common Unix commands and utilities. This library ought to be installed in a location like /usr/local/share/zsh/function (or similar, depending on your system) and consists of a bunch of scripts with filenames starting in a _ character, each of which defines the completion for a specific command.
If a command or utility you’re interested in is not yet completed correctly by zsh, you have several options:
Look into the zsh-completions package. (It may well be installable by your operating system or distribution’s package manager.)
Read the documentation for the tool you wish to have completion. Many Unix utilities ship with completion scripts for bash and/or zsh, or with some way of generating completion scripts.
If all else fails, read the documentation on zsh’s completion system (or find a good book or online tutorial) and write it yourself. This can — obviously — be non-trivial!
Reading that zsh documentation might also show you how to do other things that you may not even know yet that you want, like turning on menu-based completion.

Related

How to provide shell completion with python a package? [duplicate]

I am writing a command line tool in python and using pip to distribute it. I have written some scripts (one for bash and one for zsh) to allow the tool to have tab completion. Is there a way to get pip to install these scripts when someone does a pip install?
For example:
I have a completion.bash file. When someone does
pip install mypackage
It will also source the bash file.
I'm pretty sure I can do this for linux and bash by putting the script in my data_files section in the setup.py script.
data_file=[
('/etc/bash_completion.d', ['bin/completion.bash'])
],
But how can I do this so it is platform and shell independent? I need it to work for mac/linux in both bash and zsh. If possible, even support windows.
Is this possible? And if so, how?
In case it matters, here is my bash script:
_foo_complete() {
COMPREPLY=()
local words=( "${COMP_WORDS[#]}" )
local word="${COMP_WORDS[COMP_CWORD]}"
words=("${words[#]:1}")
local completions="$(foo completer --cmplt=\""${words[*]}"\")"
COMPREPLY=( $(compgen -W "$completions" -- "$word") )
}
complete -F _foo_complete foo
I am currently installing it by just running source completion.bash
You are asking several different questions.
First, there's no cross-platform or cross-shell solution for defining custom shell-completions. The one you posted works for bash, but in tcsh, for example, you use tcsh's complete command, which works differently than bash's.
Second, sourcing the files which contain those completion-definitions at the time of pip install wouldn't do much good. The completions might work in that very session, but what you probably want is for them to take effect in future sessions (i.e. shell invocations) as well. For that, your files would have to be sourced each time the shell starts (e.g. from within user's .bashrc, in case of bash).
This measn that "installing" your files simply means placing them somewhere, and suggesting the users should source them from their respective dot-rc file. Even if you could, you shouldn't try to "force" it. Give your users the option to add that to their dot-rc file if they want.
The best approach would probably be to include in your package a completion-definitions file per supported shell, e.g. complete.bash, complete.tcsh, and god knows what for windows (sorry, I'm not a windows user).

Does bash source bash completion files in /usr/local/etc/bash_completion.d by default?

I have a bunch of bash completion files in
/usr/local/etc/bash_completion.d
most of the scripts in there have something like this at the bottom of them:
complete -F _tmux tmux
the above is for tmux.
My question is - it doesn't look like bash by default sources these files?
I see some instructions online about doing something like this:
for f in '/usr/local/etc/bash_completion.d/'*; do
source "$f"
done;
do I need to do this manually or should bash be doing this out of the box?
This varies depending on your platform and/or versions of bash and bash-completion. For example:
Ubuntu
On Ubuntu 20.04 the file /etc/bash_completion does this:
. /usr/share/bash-completion/bash_completion
And in that file I find
for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do
dirs+=( $dir/bash-completion/completions )
done
Which indicates that /usr/local/share/bash-completion/completions is scanned for completion scripts. Empirical experiments supports this.
MacOS/Brew
I could not find anything about bash completion in /etc or /usr/share on my MacOS 11.2.3. Which indicates that bare Darwin does not have bash completion, which makes sense since Apple have left Bash at 3.2 because of licensing. Might have for zsh, though, I didn't look.
/usr/local/etc/bash-completion.d, which you reference, is a part of the Homebrew installation under /usr/local. In there I found some completion scripts, but not the activation script. You should not have to activate those explicitly yourself.
I did find /usr/local/share/bash-completion, again from Homebrew, and it has the script bash_completion. In it are the same lines as Ubuntu, which also makes sense since Homebrew is kinda complete "GNU" but under /usr/local. But it also reference the directory /usr/local/etc/bash-completion.d. Sourcing /usr/local/share/bash-completion/bash_completion added the completion from that directory also.
But /usr/local/share/bash-completion/bash_completion is not executed by default, so you have to add that to your ~/bash_profile or ~/profile as described here. It also describes how to handle zsh and fish.
Cygwin
Cygwin is another Posix-compliant environment which has bash completion. (I haven't checked if bash completion is part of the Posix standard, though) After installing the bash-completion package there is /usr/share/bash-completion/bash_completion as Ubuntu and Homebrew has. Here there is no /etc/bash_completion and as the ~/.bashrc I had (generated long ago) did only look for this completions wasn't activated.
Summary
Many GNU-like environments support bash_completion but you might have to
install a package
ensure that it is sourced when you log in, which is not always the case by default
If it is not activated by default in your environment, you can activate it by sourcing the "root" script (in /etc, /usr/share/bash-completion or where it might be located) from your .bashrc, bash_profile or similar.
I'm guessing YMMV for all other possible platforms (other Linux distros, MSYS2 etc.) but the above might help you to figure out how to enable completion. Which is really helpful when available.

Using a non-system-dependent bash shebang (instead of #!/bin/bash) that works everywhere?

I typically put a shebang for bash at the top of my shell scripts, e.g.:
#!/usr/bin/bash
However I see many other variants of this, like #!/bin/bash or #!/usr/local/bin/bash etc.
It seems to me these different conventions would result in compatibility or portability issues. If my bash is at another location than someone else's, my script won't work on their machine and vice versa.
If a shell interpreter like bash is apparently not always at the same location, isn't it plain WRONG to explicitly use a hardcoded path in a script?
I understood you can use a somewhat more flexible or less system-dependent approach like this:
#!/usr/bin/env bash
Which results in the (or a?) local version of bash, wherever that may be installed.
Does the latter variant always work? Or is there a better approach that has the highest chance of referring to any system's bash regardless of where it's installed?
I would recommend either "#!/bin/bash" or "#!/usr/bin/bash". On a modern Linux distro, bash should be installed in both places.
Apparently, that isn't true for OpenBSD ... which uses ksh as the default shell. But on an OpenBSD system, you are liable to find that bash isn't installed at all. It is apparently an optional package, and the admin may have not installed it.
So, if you want to maximize portability, use "/bin/sh" and restrict yourself to standard POSIX shell syntax and commands. "/bin/sh" is typically a link to bash or ksh, and runs in POSIX compliant mode.
Other variations:
"#!/usr/local/bin/bash" typically won't work on Linux. If it does, it may give you a locally built / modified version of bash.
"#!/usr/bin/env bash" should work, with a couple of caveats:
This will give you whatever version of bash is first on the user's command search path (i.e. $PATH).
It is conceivable that the path to env may be different, or that it may not exist. (The env command wasn't in the first version of the POSIX specs.)

What is the Bash file extension?

I have written a bash script in a text editor, what extension do I save my script as so it can run as a bash script? I've created a script that should in theory start an ssh server. I am wondering how to make the script execute once I click on it. I am running OS X 10.9.5.
Disagreeing with the other answers, there's a common convention to use a .sh extension for shell scripts -- but it's not a useful convention. It's better not to use an extension at all. The advantage of being able tell that foo.sh is a shell script because of its name is minimal, and you pay for it with a loss of flexibility.
To make a bash script executable, it needs to have a shebang line at the top:
#!/bin/bash
and use the chmod +x command so that the system recognizes it as an executable file. It then needs to be installed in one of the directories listed in your $PATH. If the script is called foo, you can then execute it from a shell prompt by typing foo. Or if it's in the current directory (common for temporary scripts), you can type ./foo.
Neither the shell nor the operating system pays any attention to the extension part of the file name. It's just part of the name. And by not giving it a special extension, you ensure that anyone (either a user or another script) that uses it doesn't have to care how it was implemented, whether it's a shell script (sh, bash, csh, or whatever), a Perl, Python, or Awk script, or a binary executable. The system is specifically designed so that either an interpreted script or a binary executable can be invoked without knowing or caring how it's implemented.
UNIX-like systems started out with a purely textual command-line interface. GUIs like KDE and Gnome were added later. In a GUI desktop system, you can typically run a program (again, whether it's a script or a binary executable) by, for example, double-clicking on an icon that refers to it. Typically this discards any output the program might print and doesn't let you pass command-line arguments; it's much less flexible than running it from a shell prompt. But for some programs (mostly GUI clients) it can be more convenient.
Shell scripting is best learned from the command line, not from a GUI.
(Some tools do pay attention to file extensions. For example, compilers typically use the extension to determine the language the code is written in: .c for C, .cpp for c++, etc. This convention doesn't apply to executable files.)
Keep in mind that UNIX (and UNIX-like systems) are not Windows. MS Windows generally uses a file's extension to determine how to open/execute it. Binary executables need to have a .exe extension. If you have a UNIX-like shell installed under Windows, you can configure Windows to recognize a .sh extension as a shell script, and use the shell to open it; Windows doesn't have the #! convention.
You don't need any extension (or you could choose an arbitrary one, but .sh is a useful convention).
You should start your script with #!/bin/bash (that first line is understood by execve(2) syscall), and you should make your file executable by chmod u+x. so if your script is in some file $HOME/somedir/somescriptname.sh you need to type once
chmod u+x $HOME/somedir/somescriptname.sh
in a terminal. See chmod(1) for the command and chmod(2) for the syscall.
Unless you are typing the whole file path, you should put that file in some directory mentioned in your PATH (see environ(7) & execvp(3)), which you might set permanently in your ~/.bashrc if your login shell is bash)
BTW, you could write your script in some other language, e.g. in Python by starting it with #!/usr/bin/python, or in Ocaml by starting it with #!/usr/bin/ocaml...
Executing your script by double-clicking (on what? you did not say!) is a desktop environment issue and could be desktop specific (might be different with
Kde, Mate, Gnome, .... or IceWM or RatPoison). Perhaps reading EWMH spec might help you getting a better picture.
Perhaps making your script executable with chmod might make it clickable on your desktop (apparently, Quartz on MacOSX). But then you probably should make it give some visual feedback.
And several computers don't have any desktop, including your own when you access it remotely with ssh.
I don't believe it is a good idea to run your shell script by clicking. You probably want to be able to give arguments to your shell script (and how would you do that by clicking?), and you should care about its output. If you are able to write a shell script, you are able to use an interactive shell in a terminal. That it the best and most natural way to use a script. Good interactive shells (e.g. zsh or fish or perhaps a recent bash) have delicious and configurable autocompletion facilities and you won't have to type a lot (learn to use the tab key of your keyboard). Also, scripts and programs are often parts of composite commands (pipelines, etc...).
PS. I'm using Unix since 1986, and Linux since 1993. I never started my own programs or scripts by clicking. Why should I?
just .sh.
Run the script like this:
./script.sh
EDIT: Like anubhava said, the extension doesn't really matter. But for organisational reasons, it is still recommended to use extensions.
I know this is quite old now but I feel like this adds to what the question was asking for.
If your on a mac and you want to be able to run a script by double clicking it you need to use the .command extension. Also same as before make file executable with chmod -x.
As was noted before, this isn't really that useful tbh.
TL;DR -- If the user (not necessarily the developer) of the script is using a GUI interface, it depends on what file browser they are using. MacOS's Finder will require the .sh extension in order to execute the script. Gnome Nautilus, however, recognizes properly shebanged scripts with or without the .sh extension.
I know it's already been said multiple times the reasons for and against using an extension on bash scripts, but not as much why or why not to use extensions, but I have what I consider to be a good rule of thumb.
If you're the type who hops in and out of bash and using the terminal in general or are developing a tool for someone else who does not use the terminal, put a .sh extension on your bash scripts. That way, users of that script have the option of double-clicking on that file in a GUI file browser to run the script.
If you're the type who primarily does all or most of your work in the terminal, don't bother putting any extension on your bash scripts. They would serve no purpose in the terminal, assuming that you've already set up your ~/.bashrc file to visually differentiate scripts from directories.
Edit:
In the Gnome Nautilus file browser with 4 test files (each with permissions given for the file to be executed) with stupidly simple bash command to open a terminal window (gnome-terminal):
A file with NO extension with #!/bin/bash on the first line.
It worked by double-clicking on the file.
A file with a .sh extension with #!/bin/bash on the first line.
It worked by double-clicking on the file.
A file with NO extension with NO #!/bin/bash on the first line.
It worked by double-clicking on the file...technically, but the GUI gave no indication that it was a shell script. It said it was just a plain text file.
A file with a .sh extension with NO #!/bin/bash on the first line.
It worked by double-clicking on the file.
However, as Keith Thompson, in the comments of this answer, wisely pointed out, relying on the using the .sh extension instead of the bash shebang on the first line of the file (#!/bin/bash) it could cause problems.
Another however, I recall when I was previously using MacOS, that even properly shebanged (is that a word?) bash scripts without a .sh extension could not be run from the GUI on MacOS. I would love for someone to correct me on that in the comments though. If this is true, it would prove that there is a least one file browser out there where the .sh extension matters.

Emacs ido-style shell

Is there a command line shell or shell customization that supports emacs-style ido find file? In emacs, I can navigate to a directory extremely quickly using C-x C-f and (ido-mode t).
Ideally, I'm looking for a solution that can be used outside of emacs. Though I'd be open for a way to quickly change directories within an eshell buffer.
Since I also wanted something like this, I tried to implement it as a bash completion
function. Obviously it means. you have to use bash.
It is only lightly tested, so please feel free to try and report bugs /comments.
http://pgas.freeshell.org/shell/bash-ido
Try the Z-shell. It has much better completion than bash. I must admit I haven't used it for a while though and stuck with bash because it's always available.
Bash has an environment variable called CDPATH which can contain a list of directories to search when using the cd command. Also, check out the "Programmable Completion" and "READLINE" sections of the Bash manual. You should be able to cobble together something that works for you.
The best I've been able to come up with so far is autojump. Still looking for a solution closer to ido, but autojump is a great little app.
I know that some terminal emulator support extension, for instance rxvt-unicode can be extended with Perl scripts. I'm not sure since i never wrote an extension myself, but maybe what you want is doable this way.
If you want to have a look at some Perl scripts for urxvt there are some examples in /usr/lib/urxvt/perl with the default urxvt install on Debian.
If you want ido completion in eshell or similar, it might be best to write a function that uses ido to read a directory, then inserts the command to cd to that directory into the shell buffer. I don't use eshell myself, so I couldn't comment on how to actually write this function, but it's an idea.
fzf, the command-line fuzzy finder, adds fuzzy completion for bash and zsh.
According to the developer:
It's an interactive Unix filter for command-line that can be used with
any list; files, command history, processes, hostnames, bookmarks, git
commits, etc.
This is a portable solution (works on Linux, Mac, Windows), which has no dependencies.

Resources