The case of the mysterious --help directory (2020)
leahneukirchen.org
leahneukirchen.org
As someone who routinely uses tab completion to look into the contents of directories before entering them to confirm that they are the correct ones, I was very confused when a recent system I had to use briefly (I don't remember if it was zsh) decided to not show files if the command started with 'cd'. It wasted more time trying to be "helpful" because it required an extra 'ls' which I could essentially get for free via tab completion.
"Complete with a path on <tab>" is simple to understand and reason with and becomes second nature after working a lot in the shell prompt.
"Run a complex set of possibly incomplete/broken rules to come up with a string" is the exact opposite. Commonly bumping at "why isn't this command-line completing/is completing with something unexpected" just negates all benefits of <tab> sometimes working for stuff like git branches, long command-line flags, etc.
> cd path/to/
and then realizing that you don't remember whether you need subdirectory1 or subdirectory2 next. With tab completion, you can just continue typing
> cd path/to/subdirectory1<tab><tab>
to check whether what's in there is what you're looking for. Without tab completion, you need to move to the start of the line, change "cd" to "ls", run that, go back up in the history, change "ls" to "cd" and continue from there. It's much more inconvenient.
¹ zsh users can imitate the same effect with a custom zle function using `completer _files`
Take bash, for instance - each time you run a command (this list is nonexhaustive; I'd love to know others):
1. If you have a `PROMPT_COMMAND`, it'll be run.
2. The command will be echoed to your history file.
3. If you hit <TAB>, arbitrary functions might run (for completion).
I don't know what zsh may do on top of this, but my guess is that it's significantly more than bash. That's not a demerit to zsh - that's exactly what it should do - but visibility would be nice.
Important caveat: this goes into an in-memory history buffer; the history file is written when bash exits. This means that if you have multiple shells open, you will lose the history from all but the last shell to exit.
promptFunc() {
# right before prompting for the next command, save the previous
# command in a file.
echo "$(date +%Y-%m-%d--%H-%M-%S) $(hostname) $PWD $(history 1)" \
>> ~/.full_history
}
PROMPT_COMMAND=promptFunc history -a
Setting the histappend option does the append thing on exit: shopt -s histappend
Instead of the ~/.full_history file you can just set your normal history to infinite: HISTFILESIZE=-1
HISTSIZE=-1 $ man bash
/HISTFILESIZE
The maximum number of lines contained in the history file.
When this variable is assigned a value, the history file is
truncated, if necessary, by removing the oldest entries, to
contain no more than that number of lines. The default
value is 500. The history file is also truncated to this
size after writing it when an interactive shell exits.
(I also like that with my approach I also get the timestamp and current direct rate.)I figured I'd learn something by putting in the "I'd love to know others" bit, but this is something I didn't know I could do, and is genuinely useful to know!
I am used to NetBSD's ash where I set HISTSIZE very high in .profile and save history with
fc -l 0 > .history
With NetBSD I never "lose the history from all but the last shell to exit". I write history files when and where I want them written. (The irony is I am more likely to lose history if using bash, which auto-saves it, than I am when using ash, which does not. As GP suggests, bash is needlessly complicated.)A large number of GNU/Linux use a fork of NetBSD's ash called dash. It is the installed default scripting shell on Debian, Ubuntu, etc. and the default shell in Busybox, OpenWRT, etc.
https://en.wikipedia.org//wiki/Dash_(shell)
Dash can be compiled with history support using libedit.^1 The static dash binary I use is 448k compiled with musl.
Dash is not as nice as NetBSD's ash for interactive use. For example, there's no tab completion in dash^2, hash -v does not work despite being listed in the manpage; there are various other small differences. In any event, I am using it as an interactive shell.
The Debian project decided dash is faster than bash as a scripting shell. For me, it is also faster as an interactive shell, because I am used to libedit and most of the interactive use I do is editing and running scripts/command line history
1. Instructions
# install libedit - may already be installed for other programs such as openssh or sqlite
wget https://git.kernel.org/pub/scm/utils/dash/dash.git/snapshot/dash-0.5.11.5.tar.gz
tar xzf dash-0.5.11.5.tar.gz
cd dash-0.5.11.5
./configure --with-libedit
make
cd src
gcc -static -Wall -g -O2 -o dash alias.o arith_yacc.o arith_yylex.o cd.o error.o eval.o exec.o expand.o histedit.o input.o jobs.o mail.o main.o memalloc.o miscbltin.o mystring.o options.o parser.o redir.o show.o trap.o output.o bltin/printf.o system.o bltin/test.o bltin/times.o var.o builtins.o init.o nodes.o signames.o syntax.o -ledit -lcurses
2. Mostly I just use globbing instead. If I absolutely must have tab completion, I use tcsh. If I am not mistaken, tcsh was the first shell to offer that feature. I find it is the fastest of any shell when it comes to filename completionIt took the command completion idea from TENEX and TOPS-20
I’m sure this would be overly verbose for most users, who would likely turn it off, but it would still be educational in terms of the huge set of implicit side effects normal every day software produces.
I was told that this rule came about when they "discovered", presumably at nontrivial cost, a disk imaging tool that would default to unconditionally mirroring the first hard disk onto the second.
edit Looks like the GNU echo, sort, and uniq programs are the same. GNU has an official policy endorsing --help but not -h. [0]
Also related: --version should print the version numbers (and perhaps build details) of the software, and immediately terminate.
-v lacks the (at least somewhat) universal meaning of -h, though. Some commands, such as curl, use -v as shorthand for --verbose rather than --version, but all commands should support --version.
I believe Java HotSpot used to use -version, supporting neither -v nor --version. Now it supports both --version and -version, but not -v.
[0] https://www.gnu.org/prep/standards/html_node/_002d_002dhelp....
/?I also sometimes write `2>1` instead of `2>&1` and get a mysterious file named `1` in my current directory. Luckily it I figured that one out pretty quick.
This means that sometimes people accidentally create files whose name is a backspace.
These cannot be seen directly as '\127' via ls (or at least the default Solaris ls; not sure what GNU does), but instead as the output missing a character, usually padding, so people do not realise it is a problem.
Then it causes issues when the file gets caught in globs that process all files in the directory, but the offending file is almost invisible, leaving the user confused.
And we cannot change the program not to change terminal settings because who knows what that will break!
Things like that is why I use PROMPT_COMMAND="stty sane"
That would've been writing a custom kernel extension to track this down, but fortunately tracing exists these days.
Seriously, I don't see anything wrong here, it's about the same route I would've gone down. Except the other way round because writing a custom ~/bin/mkdir would've saved me reading up.
I jest, with full knowledge of the trouble I'm causing.
> Jannis Harder told me that the zsh completion for mkdir would run mkdir --help (to see if it’s the GNU variant), which I could verify.
Completions are typically installed as an add-on or as part of a core library of a shell.
I found this line, maybe it's the culprit? https://github.com/zsh-users/zsh/blob/master/Completion/Unix...
> Completions are typically installed as an add-on or as part of a core library of a shell.
Right, but the completions were doing the correct thing for `mkdir`. The problem is that in their dotfiles they told it to use the `mkdir` completions for a command that isn't `mkdir`; and what is correct for `mkdir` isn't correct for that other command.
This fix was to make this edit to their own dotfiles:
-compdef mkcd=mkdir
+_mkcd() { _path_files -/ }
+compdef _mkcd mkcd(this said more in a spirit of morbid fascination with these sorts of weird interactions than anything else, I'm genuinely unsure if there's a right answer here, only "pick which sort of wrong will be least intolerable for your userbase as a whole")
If I'd use a completely unrelated program (e.g. tar) and told zsh completion to handle it as if it were mkdir, nobody would be surprised if things misbehaves spectacularly.
https://github.com/zsh-users/zsh/blob/master/Completion/Unix...