diff options
author | Johannes Stoelp <johannes.stoelp@gmail.com> | 2024-05-01 14:47:26 +0200 |
---|---|---|
committer | Johannes Stoelp <johannes.stoelp@gmail.com> | 2024-05-01 14:49:10 +0200 |
commit | 50e07a8bca68d2f568df44166fa94383141c2696 (patch) | |
tree | 087c1a6dfce9745782cc62c021f2c0833077569b /src/shells | |
parent | 1c20849c87f2d936ec599b56f965507dee97ade6 (diff) | |
download | notes-50e07a8bca68d2f568df44166fa94383141c2696.tar.gz notes-50e07a8bca68d2f568df44166fa94383141c2696.zip |
shells: move shells into own group
Diffstat (limited to 'src/shells')
-rw-r--r-- | src/shells/README.md | 5 | ||||
-rw-r--r-- | src/shells/bash.md | 259 | ||||
-rw-r--r-- | src/shells/fish.md | 280 | ||||
-rw-r--r-- | src/shells/zsh.md | 371 |
4 files changed, 915 insertions, 0 deletions
diff --git a/src/shells/README.md b/src/shells/README.md new file mode 100644 index 0000000..3a7c121 --- /dev/null +++ b/src/shells/README.md @@ -0,0 +1,5 @@ +# Shells + +- [zsh](./zsh.md) +- [bash](./bash.md) +- [fish](./fish.md) diff --git a/src/shells/bash.md b/src/shells/bash.md new file mode 100644 index 0000000..f27b3a2 --- /dev/null +++ b/src/shells/bash.md @@ -0,0 +1,259 @@ +# bash(1) + +## Expansion + +### Generator + +```bash +# generate sequence from n to m +{n..m} +# generate sequence from n to m step by s +{n..m..s} + +# expand cartesian product +{a,b}{c,d} +``` + +### Parameter +```bash +# default value +bar=${foo:-some_val} # if $foo set, then bar=$foo else bar=some_val + +# alternate value +bar=${foo:+bla $foo} # if $foo set, then bar="bla $foo" else bar="" + +# check param set +bar=${foo:?msg} # if $foo set, then bar=$foo else exit and print msg + +# indirect +FOO=foo +BAR=FOO +bar=${!BAR} # deref value of BAR -> bar=$FOO + +# prefix +${foo#prefix} # remove prefix when expanding $foo +# suffix +${foo%suffix} # remove suffix when expanding $foo + +# substitute +${foo/pattern/string} # replace pattern with string when expanding foo +# pattern starts with +# '/' replace all occurences of pattern +# '#' pattern match at beginning +# '%' pattern match at end + +# set programmatically with priintf builtin +printf -v "VAR1" "abc" +NAME=VAR2 +printf -v "$NAME" "%s" "def" +``` + +> Note: `prefix`/`suffix`/`pattern` are expanded as [pathnames](#pathname). + +### Pathname + +```bash +* match any string +? match any single char +\\ match backslash +[abc] match any char of 'a' 'b' 'c' +[a-z] match any char between 'a' - 'z' +[^ab] negate, match all not 'a' 'b' +[:class:] match any char in class, available: + alnum,alpha,ascii,blank,cntrl,digit,graph,lower, + print,punct,space,upper,word,xdigit +``` + +With `extglob` shell option enabled it is possible to have more powerful +patterns. In the following `pattern-list` is one ore more patterns separated +by `|` char. + +```bash +?(pattern-list) matches zero or one occurrence of the given patterns +*(pattern-list) matches zero or more occurrences of the given patterns ++(pattern-list) matches one or more occurrences of the given patterns +@(pattern-list) matches one of the given patterns +!(pattern-list) matches anything except one of the given patterns +``` +> Note: `shopt -s extglob`/`shopt -u extglob` to enable/disable `extglob` +> option. + +## I/O redirection + +> Note: The trick with bash I/O redirection is to interpret from left-to-right. + +```bash +# stdout & stderr to file +command >file 2>&1 +# equivalent +command &>file + +# stderr to stdout & stdout to file +command 2>&1 >file +``` +> The article [Bash One-Liners Explained, Part III: All about +> redirections](https://catonmat.net/bash-one-liners-explained-part-three) +> contains some nice visualization to explain bash redirections. + +### Explanation + +```bash +j>&i +``` +Duplicate `fd i` to `fd j`, making `j` a copy of `i`. See [dup2(2)][dup2]. + +Example: +```bash +command 2>&1 >file +``` +1. duplicate `fd 1` to `fd 2`, effectively redirecting `stderr` to `stdout` +2. redirect `stdout` to `file` + +## Process substitution ([ref][psub]) + +Process substitution allows to redirect the stdout of multiple processes at +once. +```bash +vim -d <(grep foo bar) <(grep foo moose) +``` + +## Command grouping + +Execute commands in a group with or without subshell. Can be used to easily +redirect stdout/stderr of all commands in the group into one file. +```bash +# Group commands without subshell. +v=abc ; { v=foo; echo $v; } ; echo $v +# foo +# foo + +# Group commands with subshell. +v=abc ; ( v=foo; echo $v; ) ; echo $v +# foo +# abc +``` + +## Argument parsing with `getopts` + +The `getopts` builtin uses following global variables: +- `OPTARG`, value of last option argument +- `OPTIND`, index of the next argument to process (user must reset) +- `OPTERR`, display errors if set to `1` + +```bash +getopts <optstring> <param> [<args>] +``` +- `<optstring>` specifies the names of supported options, eg `f:c` + - `f:` means `-f` option with an argument + - `c` means `-c` option without an argument +- `<param>` specifies a variable name which `getopts` fills with the last parsed option argument +- `<args>` optionally specify argument string to parse, by default `getopts` parses `$@` + +### Example +```bash +#!/bin/bash +function parse_args() { + while getopts "f:c" PARAM; do + case $PARAM in + f) echo "GOT -f $OPTARG";; + c) echo "GOT -c";; + *) echo "ERR: print usage"; exit 1;; + esac + done + # users responsibility to reset OPTIND + OPTIND=1 +} + +parse_args -f xxx -c +parse_args -f yyy +``` + +## Regular Expressions + +Bash supports regular expression matching with the binary operator `=~`. +The match results can be accessed via the `$BASH_REMATCH` variable: +- `${BASH_REMATCH[0]}` contains the full match +- `${BASH_REMATCH[1]}` contains match of the first capture group + +```bash +INPUT='title foo : 1234' +REGEX='^title (.+) : ([0-9]+)$' +if [[ $INPUT =~ $REGEX ]]; then + echo "${BASH_REMATCH[0]}" # title foo : 1234 + echo "${BASH_REMATCH[1]}" # foo + echo "${BASH_REMATCH[2]}" # 1234 +fi +``` +> **Caution**: When specifying a `regex` in the `[[ ]]` block directly, quotes will be treated as part of the pattern. +> `[[ $INPUT =~ "foo" ]]` will match against `"foo"` not `foo`! + +## Completion + +The `complete` builtin is used to interact with the completion system. +```bash +complete # print currently installed completion handler +complete -F <func> <cmd> # install <func> as completion handler for <cmd> +complete -r <cmd> # uninstall completion handler for <cmd> +``` + +Variables available in completion functions: +```bash +# in +$1 # <cmd> +$2 # current word +$3 # privous word + +COMP_WORDS # array with current command line words +COMP_CWORD # index into COMP_WORDS with current cursor position + +# out +COMPREPLY # array with possible completions +``` + +The `compgen` builtin is used to generate possible matches by comparing `word` +against words generated by `option`. +```bash +compgen <option> <word> + +# usefule options: +# -W <list> specify list of possible completions +# -d generate list with dirs +# -f generate list with files +# -u generate list with users +# -e generate list with exported variables + +# compare "f" against words "foo" "foobar" "bar" and generate matches +compgen -W "foo foobar bar" "f" + +# compare "hom" against file/dir names and generate matches +compgen -d -f "hom" +``` + +### Example +Skeleton to copy/paste for writing simple completions. + +Assume a program `foo` with the following interface: +```bash +foo -c green|red|blue -s low|high -f <file> -h +``` + +The completion handler could be implemented as follows: +```bash +function _foo() { + local curr=$2 + local prev=$3 + + local opts="-c -s -f -h" + case $prev in + -c) COMPREPLY=( $(compgen -W "green red blue" -- $curr) );; + -s) COMPREPLY=( $(compgen -W "low high" -- $curr) );; + -f) COMPREPLY=( $(compgen -f -- $curr) );; + *) COMPREPLY=( $(compgen -W "$opts" -- $curr) );; + esac +} + +complete -F _foo foo +``` + +[dup2]: http://man7.org/linux/man-pages/man2/dup.2.html +[psub]: https://tldp.org/LDP/abs/html/process-sub.html diff --git a/src/shells/fish.md b/src/shells/fish.md new file mode 100644 index 0000000..a499e10 --- /dev/null +++ b/src/shells/fish.md @@ -0,0 +1,280 @@ +# fish(1) + +## Quick Info +Fish initialization file `~/.config/fish/config.fish` + +Switch between different key bindings: +- `fish_default_key_bindings` to use default key bindings +- `fish_vi_key_bindings` to use vi key bindings + +## Variables +Available scopes +- `local` variable local to a block +- `global` variable global to shell instance +- `universal` variable universal to all shell instances + preserved across + shell restart + +### Set/Unset Variables +```text +set <name> [<values>] + -l local scope + -g global scope + -U universal scope + -e erase variable + -S show verbose info + -x export to ENV + -u unexport from ENV +``` + +### Special Variables [ref](https://fishshell.com/docs/current/language.html#special-variables) +```sh +$status # exit code of last command +$pipestatus # list of exit codes of pipe chain + +$fish_pid # pid of parent fish shell ($$ in bash) +$last_pid # pid of last started process ($! in bash) + +$CMD_DURATION # runtime of last command in ms +``` + +### Lists +In `fish` all variables are lists (start with index `1`, but lists can't +contain lists. +```sh +set foo a b c d + +echo $foo[1] # a +echo $foo[-1] # d +echo $foo[2..3] # b c +echo $foo[1 3] # a c +``` + +`$` can be seen as dereference operator. +```sh +set foo a; set a 1337 +echo $$foo # outputs 1337 +``` + +Cartesian product. +```sh +echo file.{h,cc} +# file.h file.cc + +echo {a,b}{1,2} +# a1 b1 a2 b2 +``` + +#### `*PATH` [ref](https://fishshell.com/docs/current/language.html#path-variables) +Lists ending with `PATH` are automatically split at `:` when used and joined +with `:` when quoted or exported to the environment. +```sh +set -x BLA_PATH a:b:c:d +echo $BLA_PATH # a b c d +echo "$BLA_PATH" # a:b:c:d (quoted) +env | grep BLA_PATH # BLA_PATH=a:b:c:d (env) + +set FOO_PATH x y z +echo $FOO_PATH # x y z +echo "$FOO_PATH" # x:y:z +``` + +## Command Handling +```sh +# sub-commands are not run in quotes +echo "ls output: "(ls) +``` + +### I/O redirection +```sh +# 'noclobber', fail if 'log' already exists +echo foo >? log +``` + +### Process substitution +Redirect output of multiple processes. Same as `<(..)` in bash. +```sh +diff (sort a | psub) (sort b | psub) +``` + +## Control Flow +### `if` / `else` +```sh +if grep foo bar + # do sth +else if grep foobar bar + # do sth else +else + # do sth else +end +``` + +### `switch` +```sh +switch (echo foo) +case 'foo*' + # do start with foo +case bar dudel + # do bar and dudel +case '*' + # do else +end +``` + +### `while` Loop +```sh +while true + echo foo +end +``` + +### `for` Loop +```sh +for f in (ls) + echo $f +end +``` + +## Functions +Function arguments are passed via `$argv` list. +```sh +function fn_foo + echo $argv +end +``` + +### Autoloading +When running a command fish attempts to autoload a function. The shell looks +for `<cmd>.fish` in the locations defined by `$fish_function_path` and loads +the function lazily if found. + +This is the preferred way over monolithically defining all functions in a +startup script. + +### Helper +```sh +functions # list al functions +functions foo # describe function 'foo' +functions -e foo # erase function 'foo' + +funced foo # edit function 'foo' + # '-e vim' to edit in vim +``` + +### Argument parsing and completion +`argparse` puts options into variables of name `_flag_NAME`. + +References: +- [Argument Handling](https://fishshell.com/docs/current/language.html#argument-handling) +- [`argparse`](https://fishshell.com/docs/current/cmds/argparse.html) +- [Writing your own completions](https://fishshell.com/docs/current/completions.html) +- [`complete`](https://fishshell.com/docs/current/cmds/complete.html) + +```sh +function moose --d "my moose fn" + # h/help : short / long option (boolean) + # color : only long option (boolean) + # u/user= : option with required argument, only last specified is taken + # f/file+= : option with required argument, can be specified multiple times + # + argparse h/help color= u/user= f/file=+ -- $argv + or return + + if set -ql _flag_help + echo "usage ..." + return 0 + end + + set -ql _flag_file + and echo "file=$_flag_file | cnt:" (count $_flag_file) + + set -ql _flag_color + and echo "color=$_flag_color" + + set -ql _flag_user + and echo "user=$_flag_user" +end + +# Delete all previous defined completions for 'moose'. +complete -c moose -e + +# Don't complete files for command. +complete -c moose --no-files + +# Help completion. +# -n specifies a conditions. The completion is only active if the command +# returns 0. +complete -c moose -s h -l help -n "not __fish_contains_opt -s h help" \ + --description "Print usage help and exit" + +# File completion. +# -F force complete files (overwrite --no-files). +# -r requires argument. +complete -c moose -s f -l file -F -r \ + --description "Specify file (can be passed multiple times)" + +# Color completion. +# -a options for completion. +# -x short for -r and --no-files (-f) +complete -c moose -x -l color -a "red blue" \ + --description "Specify a color." + +# User completion. +# -a options for completion. Call a function to generate arguments. +complete -c moose -x -s u -l user -a "(__fish_complete_users)" \ + --description "Specify a user" +``` + +## Prompt +The prompt is defined by the output of the `fish_prompt` function. +```sh +function fish_prompt + set -l cmd_ret + echo "> "(pwd) $cmd_ret" " +end +``` +> Use `set_color` to manipulate terminal colors and `set_color -c` to print the +> current colors. + + +## Useful Builtins +List all builtins with `builtins -n`. + +```sh +# history +history search <str> # search history for <str> +history merge # merge histories from fish sessions + +# list +count $var # count elements in list + +contains /bin $PATH # return 0 (true) 1 (false) +contains -i /bin $PATH # additionally print index on stdout + +# string +string split SEP STRING + +# math +math -b hex 4096 # output dec as hex +math 0x1000 # output hex as dec +math "log2(1024)" # call functions +math -s0 7/3 # integer division (by default float) + +# status +status -f # abs path of current file +``` + +## Keymaps +```text + Shift-Tab .............. tab-completion with search + Alt-Up / Alt-Down ...... search history with token under the cursor + Alt-l .................. list content of dir under cursor + Alt-p .................. append '2>&1 | less;' to current cmdline + Alt-Left / Alt - Right . prevd / nextd, walk dir history +``` + +## Debug +```text + status print-stack-trace .. prints function stacktrace (can be used in scripts) + breakpoint ................ halt script execution and gives shell (C-d | exit + to continue) +``` diff --git a/src/shells/zsh.md b/src/shells/zsh.md new file mode 100644 index 0000000..f702655 --- /dev/null +++ b/src/shells/zsh.md @@ -0,0 +1,371 @@ +# zsh(1) + +## Keybindings + +Change input mode: +```zsh +bindkey -v change to vi keymap +bindkey -e change to emacs keymap +``` + +Define key-mappings: +```zsh +bindkey list mappings in current keymap +bindkey in-str cmd create mapping for `in-str` to `cmd` +bindkey -r in-str remove binding for `in-str` + +# C-v <key> dump <key> code, which can be used in `in-str` +# zle -l list all functions for keybindings +# man zshzle(1) STANDARD WIDGETS: get description of functions +``` + +Access edit buffer in zle widget: +```zsh +$BUFFER # Entire edit buffer content +$LBUFFER # Edit buffer content left to cursor +$RBUFFER # Edit buffer content right to cursor + +# create zle widget which adds text right of the cursor +function add-text() { + RBUFFER="some text $RBUFFER" +} +zle -N add-text + +bindkey "^p" add-text +``` + +## Parameter + +Default value: +```zsh +# default value +echo ${foo:-defval} # defval +foo=bar +echo ${foo:-defval} # bar +``` + +Alternative value: +```zsh +echo ${foo:+altval} # '' +foo=bar +echo ${foo:+altval} # altval +``` + +Check variable set, error if not set: +```zsh +echo ${foo:?msg} # print `msg` and return errno `1` +foo=bar +echo ${foo:?msg} # bar +``` + +Sub-string `${var:offset:length}`: +```zsh +foo=abcdef +echo ${foo:1:3} # bcd +``` + +Trim prefix `${var#prefix}`: +```zsh +foo=bar.baz +echo ${foo#bar} # .baz +``` + +Trim suffix `${var%suffix}`: +```zsh +foo=bar.baz +echo ${foo%.baz} # bar +``` + +Substitute pattern `${var/pattern/replace}`: +```zsh +foo=aabbccbbdd +echo ${foo/bb/XX} # aaXXccbbdd +echo ${foo//bb/XX} # aaXXccXXdd +# replace prefix +echo ${foo/#bb/XX} # aabbccbbdd +echo ${foo/#aa/XX} # XXbbccbbdd +# replace suffix +echo ${foo/%bb/XX} # aabbccbbdd +echo ${foo/%dd/XX} # aabbccbbXX +``` + +> Note: `prefix`/`suffix`/`pattern` are expanded as pathnames. + +## Variables + +```zsh +# Variable with local scope +local var=val + +# Read-only variable +readonly var=bal +``` + +Indexed arrays: +```zsh +arr=(aa bb cc dd) +echo $arr[1] # aa +echo $arr[-1] # dd + +arr+=(ee) +echo $arr[-1] # ee + +echo $arr[1,3] # aa bb cc +``` + +Associative arrays: +```zsh +typeset -A arr +arr[x]='aa' +arr[y]='bb' +echo $arr[x] # aa +``` + +Tied arrays: +```zsh +typeset -T VEC vec=(1 2 3) '|' + +echo $vec # 1 2 3 +echo $VEC # 1|2|3 +``` + +Unique arrays (set): +``` +typeset -U vec=(1 2 3) + +echo $vec # 1 2 3 +vec+=(1 2 4) +echo $vec # 1 2 3 4 +``` + +### Expansion Flags + +Join array to string `j:sep:`: +```zsh +foo=(1 2 3 4) +echo ${(j:-:)foo} # 1-2-3-4 +echo ${(j:\n:)foo} # join with new lines +``` + +Split string to array `s:sep`: +```zsh +foo='1-2-3-4' +bar=(${(s:-:)foo}) # capture as array +echo $bar # 1 2 3 4 +echo $bar[2] # 2 +``` + +Upper/Lower case string: +```zsh +foo=aaBB +echo ${(L)foo} # aabb +echo ${(U)foo} # AABB +``` + +Key/values in associative arrays: +```zsh +typeset -A vec; vec[a]='aa'; vec[b]='bb' + +echo ${(k)vec} # a b +echo ${(v)vec} # aa bb +echo ${(kv)vec} # a aa b bb + +# Iterate over key value pairs. +for k v in ${(kv)vec)}; do ...; done +``` + +## I/O redirections + +See [bash - I/O redirection](bash.md#io-redirection) + +## Process substitution + +Process substitution allows to redirect the stdout of multiple processes at +once. +```bash +vim -d <(grep foo bar) <(grep foo moose) +``` + +## Argument parsing with `zparseopts` + +```zsh +zparseopts [-D] [-E] [-A assoc] specs +``` +Arguments are copied into the associative array `assoc` according to `specs`. +Each spec is described by an entry as `opt[:][=array]`. +- `opt` is the option without the `-` char. Passing `-f` is matched against `f` + opt, `--long` is matched against `-long`. +- Using `:` means the option will take an argument. +- The optional `=array` specifies an alternate storage container where this + option should be stored. +> Documentation can be found in `man zshmodules`. + +### Example +```zsh +#!/bin/zsh +function test() { + zparseopts -D -E -A opts f=flag o: -long: + echo "flag $flag" + echo "o $opts[-o]" + echo "long $opts[--long]" + echo "pos $1" +} + +test -f -o OPTION --long LONG_OPT POSITIONAL + +# Outputs: +# flag -f +# o OPTION +# long LONG_OPT +# pos POSITIONAL +``` + +## Regular Expressions + +Zsh supports regular expression matching with the binary operator `=~`. +The match results can be accessed via the `$MATCH` variable and +`$match` indexed array: +- `$MATCH` contains the full match +- `$match[1]` contains match of the first capture group + +```zsh +INPUT='title foo : 1234' +REGEX='^title (.+) : ([0-9]+)$' +if [[ $INPUT =~ $REGEX ]]; then + echo "$MATCH" # title foo : 1234 + echo "$match[1]" # foo + echo "$match[2]" # 1234 +fi +``` + +## Completion + +### Installation + +Completion functions are provided via files and need to be placed in a location +covered by `$fpath`. By convention the completion files are names as `_<CMD>`. + +A completion skeleton for the command `foo`, stored in `_foo` +```zsh +#compdef _foo foo + +function _foo() { + ... +} +``` + +Alternatively one can install a completion function explicitly by calling `compdef <FUNC> <CMD>`. + +### Completion Variables + +Following variables are available in Completion functions: +```zsh +$words # array with command line in words +$#words # number words +$CURRENT # index into $words for cursor position +$words[CURRENT-1] # previous word (relative to cursor position) +``` + +### Completion Functions +- `_describe` simple completion, just words + description +- `_arguments` sophisticated completion, allow to specify actions + +#### Completion with [`_describe`][zsh-comp-fn] +```zsh +_describe MSG COMP +``` +- `MSG` simple string with header message +- `COMP` array of completions where each entry is `"opt:description"` + +```zsh +function _foo() { + local -a opts + opts=('bla:desc for bla' 'blu:desc for blu') + _describe 'foo-msg' opts +} +compdef _foo foo + +foo <TAB><TAB> + -- foo-msg -- +bla -- desc for bla +blu -- desc for blu +``` + +#### Completion with [`_arguments`][zsh-comp-fn] +```zsh +_arguments SPEC [SPEC...] +``` +where `SPEC` can have one of the following forms: +- `OPT[DESC]:MSG:ACTION` for option flags +- `N:MSG:ACTION` for positional arguments + +Available actions +```zsh +(op1 op2) list possible matches +->VAL set $state=VAL and continue, `$state` can be checked later in switch case +FUNC call func to generate matches +{STR} evaluate `STR` to generate matches +``` + +### Example +Skeleton to copy/paste for writing simple completions. + +Assume a program `foo` with the following interface: +```zsh +foo -c green|red|blue -s low|high -f <file> -d <dir> -h +``` + +The completion handler could be implemented as follows in a file called `_foo`: +```zsh +#compdef _foo foo + +function _foo_color() { + local colors=() + colors+=('green:green color') + colors+=('red:red color') + colors+=('blue:blue color') + _describe "color" colors +} + +function _foo() { + _arguments \ + "-c[define color]:color:->s_color" \ + "-s[select sound]:sound:(low high)" \ + "-f[select file]:file:_files" \ + "-d[select dir]:dir:_files -/" \ + "-h[help]" + + case $state in + s_color) _foo_color;; + esac +} +``` + +### Example with optional arguments +For this example we assume that the command `foo` takes at least three optional +arguments such as +```zsh +foo arg1 arg2 arg3 [argN..] +``` + +```zsh +function _foo() { + _arguments \ + "1:opt 1:(a b c)" \ + ":opt next:(d e f)" \ + "*:opt all:(u v w)" +} +``` +Explanation: +- `1:MSG:ACTION` sets completion for the **first** optional argument +- `:MSG:ACTION` sets completion for the **next** optional argument +- `*:MSG:ACTION` sets completion for the optional argument where none of the + previous rules apply, so in our example for `arg3, argN..`. + +> `_files` is a zsh builtin utility function to complete files/dirs see +> - [zsh completion functions][zsh-comp-fn] +> - [zsh completion utility functions][zsh-comp-utility-fn] + + +[zsh-comp-fn]: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-Functions +[zsh-comp-utility-fn]: https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org#utility-functions |