Ideally this could all be part of a library such as argparse for typical cases, right?
A similar thing in Bash is `complete -F _longopt YourCmd`, but these will not work with "multi-commands" that have "sub-commands" as the article of this thread covers. Truth is, as non-standard as GNU long opts already are, how subcommands work is even more non-standard (are there even global options? Or between each subcommand? Is it how Python does it? Or Go? Or some one specific library or ..?)
[^1]: https://no-color.org/
ripgrep exposes its (bespoke) shell completion and man page generation through a --generate option: rg --generate=man, rg --generate=complete-bash, etcetera. In xh (clap-based) we provide the same but AFAIK we're the only one to copy that interface.
Symfony (for PHP) provides some kind of runtime completion generation but I don't know the details.
https://pkgstats.archlinux.de/compare/packages#packages=bash...
OTOH, it's only 4-7% on Debian (also opt-in):
I'm firmly in the never-zsh camp, but I'd guess so long as one doesn't do something silly like use #!/bin/zsh in shared scripts you can continue to use whatever interactive shell you like
I had a college who was hard core into fish but had to be completely self sufficient because he was the only user in the office. I presume it's the same as being on Linux in a Windows shop or being on Windows in a macOS shop: no one has the day to day experience to have the answers handy
If you don’t like the Verb-Noun nonsense I’d encourage you to look at the default aliases as they make everything a lot less verbose. For example Where-Object is just “where” or getchild-item is default aliased to ls and gci.
Id encourage you to look at NuShell as well since it is mostly the same philosophy as PowerShell.
Bad for your reasons (slow etc), good for siblings (objects).
Also, understandable flags that auto-shorten. Eg:
Descriptive-Command-Name -DescriptiveOption 32
Can be typed as:
d-c-n <tab> -des 32
---
Interactively it's still far behind zsh sadly due to startup time but when I'm doing something thoughtful I prefer it. If you're not entering directories and doing small stuff, use pwsh. And for all shell scripting
If you are bothered by the verbose `Verb-Noun` structure of commands, those are mostly used in scripts (and you never type them fully, you use tab-completion of course). On the commandline you use aliases for many of them.
For example listing files and directories created in the last 10 days:
ls | ? CreationTime -ge (Get-Date).AddDays(-10) | ft name, size
All the long words are tab completed (CreationTime, Get-Date, AddDays).
The fully verbose syntax you might write in a script looks a bit uglier: Get-ChildItem | Where-Object CreationTime -ge (Get-Date).AddDays(-10) | Format-Table Name, Size
Also PowerShell is NOT case-sensitive, even though many people seem to think so.And `rc` under plan9/9front did a Unix shell better than the classic Unix itself.
It parses all man pages on your system and generates completion files for you. By default, they go into ~/.cache/fish/generated_completions/*
If the man page was written poorly/is missing, you can always write your own completion (and hopefully send it upstream). fish uses such a simple format that I don't think there's any need for tutorials save the official doc:
https://fishshell.com/docs/current/completions.html
For example, here's an excerpt from curl
complete --command curl --short-option 'L' --long-option 'location' --description 'Follow redirects'
complete --command curl --short-option 'O' --long-option 'remote-name' --description 'Write output to file named as remote file'Still in an early stage, but it should work.
thanks a lot
Say you have a widget that has 3 commands: list, start and stop.
With one of those completion files widget stop <tab> will show you a tab-able list of widgets which are running.
https://github.com/fish-shell/fish-shell/discussions/11670#d...
> I'm not sure that subsequence matching ever produces results that people expect (I feel like we've discussed this before but haven't had time to go digging)
No solution, but the discussion seems positive, from a maintainer too.
> We don't really do config flags like this, as a philosophical point.
> I would be against accepting such a PR. I do not believe this should be changed.
I understand that they want to keep the list of configs short and manageable, but it means that's not a tool for me. I'm all for good, opinionated defaults but I want to be able to make some changes if I want to.
They apparently reconsidered and implemented the change since migrating fish to rust though.
(Well that and all my machines come from the same NixOS configs.)
I have personally embraced the insanity but let’s not kid ourselves about nixos basically just being three bashes in a trench coat.
Basically https://xkcd.com/224/ , but s/lisp/nix/ s/perl/bash/
Some year something better will show up from someone with more taste (and I know I am not alone in having thoughts on what it would look like), but for now the yak has already been shaven. At least the builds are sandboxed.
When I first found shellcheck, I'd sit on the train every morning and fix all the stupid little bugs in all our shell code. I learned a TON about shell scripting, because the shellcheck wiki is amazing. We had some redhat 6 init scripts checked into our repos, and shellcheck helpd me find syntax errors in those init scripts. If Redhat can't reliably write shell scripts without a linter, or catch bugs from upstream, what makes us think we're any better?
I wouldn't write serious programs in another language without a linter. Why should I write shell scripts without one?
All these comments here "Uhhh just use PythoN!" Yeah? I write scripts that need to run on macOS. No Python there. Yeah I can have my script sit there and pull the command line tools in an automated way and wait the 20 minutes it takes to install or I can pull on my big boy pants and just figure out how to do it in shell. It's turing complete. It works. Whatever.
Thanks GPLv3
(Seriously, what am I missing here? Is `apt-brew pacman install fish` really that hard to type? If cross-platform portability is actually a concern, shouldn't you be using Python anyway?)
Maybe it's because I was a sysadmin in a former life, but overmanaging is almost always a mistake. If I had one single computer and only one, sure, who cares. But I have a homelab full of servers and a constant churn of VMs and I don't need to add in another step of "oh shit X isn't installed yet". Maybe it's also because of that that I don't really give a shit about how "bad" sh/bash/zsh are.
Yes, I can sync dotfiles and crap to git (which I also have to install to use), but it's just a small weight off my shoulders to not have to worry or think about that shit before I can use a server or workstation. I don't want to have to unlearn normal shell to learn fish and then have to switch back when I'm trying to get some thing online so that I can install fish. I'm just not fuckin interested.
(inb4 someone is like nix solves this, i don't give a shit about nix either)
in the end i switched back to bash.
As an example of diversity estimation that you can try at home, a couple of times I have run every single command in my command search PATH with --help </dev/null >/tmp/help.$c 2>&1 . Caution - be careful if you do this! Have backups/checksums of everything important and run as an unprivileged user. I always have to kill off several processes that just hang doing something or otherwise manually intervene. Anyway, this alone suggests data collection of help text is not a trivial problem.
Beyond data collection, many commands did not/do not use CLI toolkits at all. Their commands may have even less regular syntax. Freeform help makes it harder to produce a regular help syntax to convert into the interpreter needed by a completion system. That said, as elsethread commented for some toolkits the Zsh _gnu_generic works great! It essentially IS the "automagic" system you might want, just for a highly restricted circumstance.
Any CLI toolkit itself does have the data, by necessity. So, if the CLI framework supports the 2 or 3 common shells there is no need for a translator exactly. You just need a code generator. There is a stab at an auto-generation framework from said data for the Nim CLI toolkit, cligen, over at:
https://github.com/c-blake/cligen/blob/master/util/complgen....
but it only works for Zsh right now. Anyway, I don't think perfect should be the enemy of the good or anything like that, but you seemed to ask an earnest "why" question and these are some of the complexities.
As for the 30..60 commands, I use the following "tricks"
1) my ctrl-p is mapped in a way that it searches the prefix into history. (On phone right now)
2) long history which syncs with other machines in a machine-name-suffixed file via syncthing
3) justfile which are really a game changer. I used bare j to tell me what commands are available (as opposed to running the first command) and I basically know everything I can and do do in that folder
4)
See: https://pixi.carapace.sh/ or https://github.com/withfig/autocomplete
It's still a hard problem as lots of tools format --help differently. One of the things I'm jealous in Poweshell is their standardized completions
Sometimes I’m close to disabling/uninstalling all completion scripts out of irritation as decades of muscle memory are frustrated by this behavior.
It’s like that bad/annoying UX with text fields where the UI is constantly fighting against you in order prevent you from producing “illegal” intermediate input - e.g. let me paste the clipboard here goddammit - I know what I’m doing - I’ll correct it.
Unbelievably frustrating.
It should at least print a message like "file foo.exe exists but it isn't executable".
It's not a fix but it'll save a little time sometimes.
TLDR: M-/ (Alt /) will do file auto-completions regardless of the commands auto-comoletion. It is a different method, but maybe could help.
There are a collection of other non-context aware completion functions that are bound by default too, useful for example when you when you wish to complete hostnames in a for-loop.
zle has what is largely a significant superset of this, the documentation is spread about between the zshzle and zshcomp* manpages.
#compdef set-java-home
local -a versions=(~/apps/java/*(:t))
_describe 'version' versions
So basically almost a one-liner (but couldn't do it really one-liner, unfortunately).That being said, csh advocates definitely influenced everything in the Bourne/POSIX family.
Custom completions may be configured by creating an array named ‘complete_command’, optionally suffixed with an argument number to complete only for a single argument. So defining an array named ‘complete_kill’ provides possible completions for any argument to the kill(1) command, but ‘complete_kill_1’ only completes the first argument. For example, the following command makes ksh offer a selection of signal names for the first argument to kill(1):
set -A complete_kill_1 -- -9 -HUP -INFO -KILL -TERMOr is this ksh93 syntax that oksh back ported?
David Korn thanked the pdksh authors and maintainers for making it available in the years when true ksh was closed (ksh88) or open but licensed with awkward terms (ksh93 for several years).
David Korn interview (I asked one of the questions):
https://m.slashdot.org/story/16351
"First of all pdksh is a ksh88 clone; and I might add a better clone than the MKS Korn Shell...
"I don't know the pdksh development team but I would like to thank them for the service they have done in making a version of ksh available while ksh was proprietary. I have noticed remarkable improvements in pdksh in its ability to mimic ksh88 functionality. I don't know what plans the pdksh development team has now that ksh93 is available in open source form, but I certainly would help them try to maintain compatibility if they do continue pdksh distribution. Otherwise, I would hope that they would pick up the ksh93 source and help support and enhance it."
Unfortunately my work doesn't allow me to share code, but essentially I remapped ssh to a bash script that maintains an environment variable containing the args (you must do this because each <tab> press is an independent invocation of the script. Then you run into persistence problems, so I added a call to compute elapsed seconds so that it flushes the state variable after a 10s timeout).
The bash script then forwards the args to a python script that reads a JSON file and figures out which params (such as 'co' or 'qa') map to which hostnames. It also matches against partial hostnames, so when you see this after tab
qa-server-co1 qa-server-co2 pr-server-co3
you only need to add '3' to the list of args to narrow it down to 1 match, then hit <enter> to ssh to that host.
I'm still on the fence if replacing the argparse blocks in my fish scripts is worth the hassle, but against things like old school optparse, it's far better
lihaoyi•6mo ago
oezi•6mo ago
tetha•6mo ago
But at work, I've been slowly adding auto completion to our ansible wrapper scripts, like explanations which playbooks to use when, smart `-l` completion based off a possibly selected playbook (so, if the playbook is postgres.yml, it doesn't suggest mariadb groups), tag autocompletion (with a few, admittedly, hardcoded explanations how these tags should be used) and such.
It's somewhat of a friday-afternoon struggle project, but it's making the big ansible project pretty approachable to use.
imcritic•6mo ago
tetha•5mo ago
This needs to be in a directory in your FPATH.
At the core, it uses _arguments to dissect the command line. This both suggests that "-t" exists, is called "tags" and later on sets a state variable to "tags" or "limits" if we're completing these arguments.
Given this, the limits autocompletion goes one step further. `detect_playbook` mainly goes through $words and looks for a singular argument looking like "*.yml" and sets that as $PLAYBOOK. Then, based on "$PLAYBOOK", it selects a filter-expression for the groups. This ensures that a `./wrapper mariadb.yml -l<TAB>` only sees mariadb-groups, and not postgres-groups.All of that is shoved into `_values`, and then the usual zsh completion works, so with something like `./wrapper mariadb.yml -l prj4<TAB>`, zsh tries to filter the values based on the word, so this finds stuff like `prj4`, `prj49`, `dc2_prj45`, and so on, but not `prj5`.
For the tags I'm working on a similar thing, but this contains enough ugly shell-script already. However, the key parts there are:- You can run `ansible-playbook "$WORD" --list-tags` to get all tags a playbook references in the current inventory.
- One can give `_values` descriptions. If `_values` sees 'foo[bar]' as an option, it will show the user something like: "foo: bar" and only auto-completes to foo.
- This means, we can give standard or well-established tags short descriptions in a directory or an array or whatever, and instead of offering just "postgres_client_tls_certs" as possible auto-completions for `-t`, we can give the user a prompt like "postgres_client_tls_certs: Ensures the postgres cluster has valid and up-to-date TLS certificates for mutual TLS with applications".
It took a bit of time to understand all of this. But now documenting a tag in a place that people actually look at is very easy and straightforward.
imcritic•5mo ago
Still cool though, thanks for sharing!
bbkane•6mo ago
Instead of sourcing the zsh completion script on every startup, you can install it into somewhere on $fpath and zsh will "compile" and cache the completions. This can really speed up shell startup time, but of course is harder to set up. Users have to understand $fpath to put the completions there.
I distribute my CLIs via Homebrew which can install completions automatically.