4 ms·
As many others here have noted, terse syntax for things like `repeat` loops or normal `for`/`while` loops or shell var expansion can all be addictive, but some
by cb321 6y ago
As many others here have noted, terse syntax for things like `repeat` loops or normal `for`/`while` loops or shell var expansion can all be addictive, but some specific things I miss in Bash that are easy in Zsh are:
1) auto cd to any evar just from its name
e.g. lb=/usr/local/bin; lb
($lb works in bash, but I always forget
the $. I know I could do aliases...Yuck.)
2) auto cd does not seem to use $CDPATH
this is a real pain since I am used to
never typing "cd ".
3) **c instead of **/*c for a recursive glob
At 2 vs. 4 chars (or 3 vs. 6 keydowns) it
may not *look* much briefer, but it feels
much easier to type once you're used to it.
4) On the fly command line colorization along
the lines of zsh-syntax-highlighting.
Spot syntax errors/missing cmds *before*
you hit <ENTER>.
5) vared MyVar
Use the ZLE to easily modify any evar.
6) Zsh's REPORTTIME
*automatically* report time/memory of only
"expensive" programs/pipelines.
7) Zsh's extended history format that includes
both the time started as well as *duration*
of all my commands.
8) Floating point arithmetic and $[]
forget all this calculator jazz. I just
do `alias ec=echo` and go `ec $[1234./56]`
or even just : $[1234./56]<TAB>
9) glob qualifiers (forget `find` or `fd`)
My main use cases are *(.) *(@) & *(/),
sometimes with a ^ for "not" in there,
e.g. *(^/). The codes are mostly the
old ls -F codes to be easy to recall.
If anyone knows how to make Bash do any of those, I would appreciate the tip. Otherwise, maybe they count as "killer" features to someone.
Also, I tend to use a global alias "/n" for "/dev/null". I know I could just use $n with n=/dev/null, but I enjoy having it look like a path in / that never actually exists.
- chrisshroba 6y ago> Zsh's extended history format that includes both the time started as well as duration of all my commands. Woah, how do I enable this? From what I can tell this information is not being stored in $HISTFILE: ... : 1613659860:0;sleep 1 : 1613659863:0;sleep 3 : 1613659871:0;exec zsh : 1613659880:0;vim $HISTFILE
- cb321 6y agoNot sure why you are getting all zeros in your duration column. Should be setopt ExtendedHistory (Zsh setopts, like Nim identifiers, are "style insensitive"). From the Zsh man page: EXTENDED_HISTORY <C> Save each command's beginning timestamp (in seconds since the epoch) and the duration (in seconds) to the history file. The format of this prefixed data is: `: <beginning time>:<elapsed seconds>;<command>'. (EDIT: My best guess explanation is some competing/conflicting setopt, but the Zsh mailing list could almost surely help you. Also, `history -fD` may show the correct answer even if the saved file has all 0s.)
- chrisshroba 6y agoThank you! How strange. `history -fD` does work, but only for commands in the current zsh session. It seems durations are being stored in memory but written out as 0's. I'll try the mailing list soon :)
- cb321 6y agoLooks like you probably also have SHARE_HISTORY set? [1] [1] https://unix.stackexchange.com/questions/396809/zsh-share-history-or-inc-append-history-breaks-extended-history https://unix.stackexchange.com/questions/396809/zsh-share-hi...
- jolmg 6y agoIn my case, I have INC_APPEND_HISTORY enabled. It appears I get 0 in the duration column because it's writing to the history file before executing the command. I see there's the alternative option INC_APPEND_HISTORY_TIME to append to history after the command finishes in order to include the duration. It makes sense. It's preferring to add to history immediately so that it's available to other shells without having to wait for the command to finish. From the manpage: > INC_APPEND_HISTORY_TIME This option is a variant of INC_APPEND_HISTORY in which, where possible, the history entry is written out to the file after the command is finished, so that the time taken by the command is recorded correctly in the history file in EXTENDED_HISTORY for‐ mat. This means that the history entry will not be available immediately from other instances of the shell that are using the same history file. This option is only useful if INC_APPEND_HISTORY and SHARE_HISTORY are turned off. The three options should be considered mutually exclusive.
- dchess 6y agoWhat's Yuck about using aliases? I use those extensively. Is that a bad practice?
- cb321 6y agoI use aliases, too. But an alias for every possible "bookmarked" directory seems excessive. Like this guy [1] in the current thread, I tend to just say x=`pwd` (no need to always `export`). Agreed there is probably a way to set up a shell function `a() {...}` that establishes an alias (or even a shell function) almost as simply, but another problem that remains is that aliases and variables are separate namespaces. So, if you do it the variable way like Zsh then you don't have to worry about clobbering some existing alias or shell function. You do have to keep these separated namespaces in your head, but the updating of the prompt to the variable name is a pretty good reinforcement cue (at least for me, in practice). Also, the variable lets you use it in composed paths like `$x/bin/foo` while an alias/function would only change your directory. TL;DR - Yuck = namespace squishing in this application only, but then again one man's "collapse of namespaces being yucky" is another's "yay, I do not have to remember 3..4 namespaces!". EDIT: for what it's worth, I never intended my 9..11 items to be "in order of importance or persuasiveness". Mostly in order of personally driving me batty when I use Bash and just meant to actually be things Zsh could do that (present)Bash could not, to my knowledge due to @deadbunny's complaint. I do try to keep a side Bash config that is as close as possible to my Zsh config, because it's not always worth my time to compile/install Zsh on a system. But when I do that and start using Bash, I notice all these missing things. { That's why I would genuinely appreciate anyone who knows how to replicate them in Bash saying how. I'm not just "daring the Bash apologists to a challenge" or whatever. :-) } [1] https://news.ycombinator.com/item?id=26177537 https://news.ycombinator.com/item?id=26177537
- arve0 6y agoOne yuck about aliases, it doesn’t give you autocomplete. See abbr in fish shell for an alternative.
- jolmg 6y ago9 goes quite far. My most common uses are downloads/*(oc[1]) for the latest downloaded file (order by time of inode change and return just first result), or downloads/*(c0) for all the files downloaded today (created 0 days ago in downloads directory). 10) =() expansion. It's like <() which both bash and zsh have, but instead of substituting with the path to a pipe, it substitutes with the path to a temporary regular file. Sometimes pipes don't suffice. For example, I can do `viewnior =(maim -s)` to select a region of the screen and see it in an image viewer immediately. `viewnior <(maim -s)` fails because viewnior can't display an image from a pipe. 11) short syntax for control structures. For example, you can do `for x in $(xs); foo $x` without bothering with `do` and `done`. 12) variable expansion in brace expansion. In bash, `x=5; echo {3..$x}` outputs `{3..5}`. zsh outputs `3 4 5`. 13) Command can be just redirection without any executable. `<<< foo` is equivalent to `cat <<< foo`, `> foo.txt` is equivalent to `cat > foo.txt`. 14) Able to convert numbers between any bases. To convert base-5 10 to base-4: `<<< $(( [#4] 5#10 ))` outputs `4#11` which means 11 in base-4. 15) Named directories. I have my project files organized under deep directories like ~/work-for/.../.../.../foo/, and have configured all those as named directories to be able to access them as `~foo`, for instance. Different from using simple variables for this, named directories are compacted when presented by the shell. So in my prompt, I see my current directory is e.g. `~foo/bar` instead of the whole thing from the home dir. Named directories come in 2 forms, static and dynamic. Static named dirs are when you set them up in a hash of name => directory. Dynamic named dirs are when you define a function that expands arbitrary names to paths and compacts arbitrary paths to names. 16) `=foo` expands to the full path of executable foo. So you can, e.g. see the source of `pass` with `less =pass`. 17) Variable expansion syntax, glob qualifiers, and history modifiers can be combined/nested quite nicely. For example, this outputs all the commands available from $PATH: `echo ${~${path/%/\/*(*N:t)}}`. `${~foo}` is to enable glob expansion on the result of foo. `${foo/%/bar}` substitutes the end of the result of foo to "bar" (i.e. it appends it); when foo is an array, it does it for each element. In `/*(*N:t)`, we're adding the slash and star to the paths from `$path`, then the parentheses are glob qualifiers. `*` inside means only match the executables, `N` is to activate NULL_GLOB for the match so that we don't get errors for globs that didn't match anything, `:t` is a history mod used for globs that returns just the "tail" of the result, i.e. the basename. IIRC, bash can't even nest multiple parameter expansions; you need to save each step separately. 18) Suffix aliases can be used to define how to open arbitrary files. `./foo.mp4` opens it up in with `mpv`, because I have `alias -s mp4=mpv`. 19) Global aliases are useful for expanding stuff anywhere. For example, I have `alias -g %LD='~/downloads/*(oc[1])'` so that `%LD` expands to the latest download as part of any argument to a command. 20) Globbing flags. There are various flags for globbing, that enable e.g. backreferences in globs, allow n-number of errors while matching (approximate matching), etc. The only one I've really used though is `i` which enables a glob to be case-insensitive. So `(#i)*.pdf` matches `foo.pdf` as well as `bar.PDF`. 21) Multios. You can use multiple files in individual redirections. For example, `foo > bar | baz` is equivalent to `foo | tee bar | baz`. That accepts globbing and brace expansion too: `foo > *.{c,h}` is the same as `foo | tee *.{c,h}`. Also works for reading: `foo < *.txt` is practically the same as `cat *.txt | foo`. I must admit that I haven't really used this, though.
- tux1968 6y agoFor your #8 a function might be helpful to remove the $[] characters. $ function c;{echo $[$*]} $ c 1234./56 22.035714285714285 $ a=33;c a / 3 11
- cb321 6y agoIf I did that & something for bash that used awk | python to do FP arithmetic I might be able to evolve to a more "portable habit/muscle memory" even if Bash never learns FP arithmetic. Solid suggestion, @tux1968. Thanks. { EDIT: 1 down, a dozen to go! lol :-) and I'll go with a(){..} for a)rithmetic not c since I use that for clear. }