# syntactic sugar

* [oneline commands](#oneline-commands)
  * [cat and EOF](#cat-and-eof)
  * [while read from input](#while-read-from-input)
  * [tail and timestamp](#tail-and-timestamp)
  * [backup multiple files](#backup-multiple-files)
  * [ssh](#ssh)
  * [find and tar](#find-and-tar)
  * [find and rename](#find-and-rename)
  * [find and sort](#find-and-sort)
  * [find and copy](#find-and-copy)
  * [download and extract](#download-and-extract)
  * [mirror website](#mirror-website)
  * [kubectl apply from stdin](#kubectl-apply-from-stdin)
  * [show 256 colors](#show-256-colors)
  * [rm and exclude](#rm-and-exclude)
  * [split file to equal size](#split-file-to-equal-size)
* [sync mirror](#sync-mirror)
* [get all declare](#get-all-declare)
  * [print env](#print-env)
* [using string as variable name](#using-string-as-variable-name)
* [`<<<`, `< <(..)`](#--)
  * [`< <(..)` && `> >(..)`](#----)
* [parameter substitution](#parameter-substitution)
  * [arguments substitution](#arguments-substitution)
  * [quotas](#quotas)
* [string manipulations](#string-manipulations)
* [compound comparison](#compound-comparison)
  * [SC2155](#sc2155)
  * [SC2155](#sc2155-1)
  * [escape code](#escape-code)
* [echo](#echo)
  * [echo var name from variable](#echo-var-name-from-variable)
  * [echo var name](#echo-var-name)
* [ls](#ls)
* [bash completion](#bash-completion)
  * [osx](#osx)
  * [linux](#linux)
  * [completion for alias](#completion-for-alias)
  * [troubleshooting](#troubleshooting)
* [tricky](#tricky)
  * [alias for sudo](#alias-for-sudo)
  * [get md5sum](#get-md5sum)
  * [env](#env)
  * [shortcuts](#shortcuts)
  * [man](#man)
  * [debugging a script](#debugging-a-script)

{% hint style="info" %}

> reference:
>
> * [ppo/gist/bash.md](https://github.com/ppo/gist/blob/master/bash.md)
> * [Unix / Linux - Shell Substitution](https://www.tutorialspoint.com/unix/unix-shell-substitutions.htm)
> * [ShellCheck Wiki Sitemap](https://www.shellcheck.net/wiki/)
> * [\* cd is not a program](https://seb.jambor.dev/posts/cd-is-not-a-program/)
>   {% endhint %}

### oneline commands

> \[!TIP]
>
> * [iMarslo : kubernetes - oneline cmd](https://github.com/marslo/ibook/blob/marslo/docs/virtualization/kubernetes/cheatsheet.html#oneline-command)
> * [johnnypea/useful-one-liners.sh](https://gist.github.com/johnnypea/b0cd77e5734d65691fa21d93274b305b)
> * [ITT: Post useful shell aliases/functions](https://4chanarchives.com/board/g/thread/51917516)
> * [Post your handy self made command line utilities](https://bbs.archlinux.org/viewtopic.php?id=56646\&p=56)

#### cat and EOF

> \[!NOTE|label:references:]
>
> * [Chapter 19. Here Documents](https://tldp.org/LDP/abs/html/here-docs.html)
> * [bash Heredoc](https://linuxize.com/post/bash-heredoc/)
>   * using heredoc with ssh
>
>     ```bash
>     ssh -T user@host.com << EOF
>     echo "The current local working directory is: $PWD"
>     echo "The current remote working directory is: \$PWD"
>     EOF
>     ```
> * [\* POSIX.1 states](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap01.html#tag_17_04)
>
>   ```
>   ...an operand naming a file can be specified as '-', which means to use the standard input instead of a named file ....
>   ```

* kubectl apply from stdin

  > \[!NOTE|label:references:]
  >
  > * [Need some explaination of kubectl stdin and pipe](https://stackoverflow.com/a/72168173/2940319)

  ```bash
  $ cat << 'EOF' | kubectl apply -f -
  ...
  ...
  EOF

  # or
  $ kubectl apply -f - << EOF
  ...
  ...
  EOF
  ```
* git apply from stdin

  ```bash
  $ cat >> 'EOF' | git apply --inaccurate-eof --ignore-whitespace
  ...
  ...
  EOF

  # or
  $ git apply --inaccurate-eof --ignore-whitespace --stat << 'EOF'
  ...
  ...
  EOF
   install |    6 ++----
   1 file changed, 2 insertions(+), 4 deletions(-)
  ```
* git apply from clipboard

  > \[!NOTE|label:references:]
  >
  > * [Pipe (a patch in my clipboard) to `git apply`?](https://superuser.com/q/1493215/112396)

  ```bash
  $ pbpaste | git apply

  $ xsel --clipboard --input | git apply
  # or
  $ xclip -selection clipboard -o | git apply
  ```
* patch from stdin

  > \[!NOTE|label:references:]
  >
  > * [How can I run patch through a heredoc in bash?](https://unix.stackexchange.com/a/409327/29178)

  ```bash
  $ patch --dry-run --ignore-whitespace << 'EOF'
  ...
  ...
  EOF
  ```

#### while read from input

> \[!NOTE|label:references:]
>
> * [How to use a shell command to only show the first column and last column in a text file?](https://unix.stackexchange.com/a/148416/29178)

```bash
$ while IFS=\| read -r col1 col2; do echo ">> $col1 .. $col2 <<"; done <<\INPUT
  a|b
  INPUT
>> a .. b <<
```

#### [tail and timestamp](https://www.commandlinefu.com/commands/view/7110/monitor-a-file-with-tail-with-timestamps-added)

```bash
$ tail -f file | while read line; do echo -n $(date -u -Ins); echo -e "\t$line"; done
```

#### [backup multiple files](https://www.commandlinefu.com/commands/view/2527/quickly-backup-or-copy-a-file-with-bash)

```bash
$ cp -bfS.bak filename filename
```

#### ssh

* compress and ssh and extract

  ```bash
  $ tar cf - . | ssh elsewhere tar xf - -C /other/dir

  # or: https://www.commandlinefu.com/commands/view/4034/copy-a-folder-tree-through-ssh-using-compression-no-temporary-files
  $ ssh <host> 'tar -cz /<folder>/<subfolder>' | tar -xvz
  ```
* tips

  > \[!NOTE|label:references:]
  >
  > * [How can I copy multiple files over scp in one command?](https://superuser.com/a/116001/112396)

  ```bash
  # tips
  $ tar cfz - . | ssh otherhost "cd /mydir; tar xvzf -"

  # or: https://www.commandlinefu.com/commands/view/1629/pack-up-some-files-into-a-tarball-on-a-remote-server-without-writing-to-the-local-filesystem
  $ tar -czf - * | ssh example.com "cat > files.tar.gz"

  # the z-flag to tar does compression. Or you can use -C to ssh:
  $ tar cf - . | ssh -C otherhost "cd /mydir; tar xvf -"
  ```

#### find and tar

```bash
$ find . -name builds -prune -o -type f -print | tar czf ~/m.tar.gz --files-from -

# or find with maxdepth
$ find . -type f -name "config.xml" -maxdepth 2 -prune -print | tar czf ~/config.xml.130.tar.gz --files-from -

# find with special name
$ find . -name config\.xml -type f -print | tar czf ~/m.tar.gz --files-from -

# and ssh and extract
$ tar cf - . | ssh -C otherhost "cd /mydir; tar xvf -"
```

* for JENKINS\_HOME
  * backup all `config.xml` in JENKINS\_HOME

    ```bash
    $ find ${JENKINS_HOME}/jobs \
           -maxdepth 2 \
           -name config\.xml \
           -type f -print |
      tar czf ~/config.xml.tar.gz --files-from -
    ```
  * back build history

    ```bash
    $ find ${JENKINS_HOME}/jobs \
           -name builds \
           -prune -o \
           -type f \
           -print |
      tar czf ~/m.tar.gz --files-from -
    ```
* [advanced ls](https://www.commandlinefu.com/commands/view/5815/advanced-ls-output-using-find-for-formattedsortable-file-stat-info)

  ```bash
  $ find $PWD -maxdepth 1 -printf '%.5m %10M %#9u:%-9g %#5U:%-5G [%AD | %TD | %CD] [%Y] %p\n'

  # or
  $ find $PWD -maxdepth 1 -printf '%.5m %10M %#9u:%-9g %#5U:%-5G [%AD | %TD | %CD] [%Y] %p\n' | sort -rgbS 50%
  ```
* [tar and ssh](https://www.commandlinefu.com/commands/view/37/scping-files-with-streamlines-compression-tar-gzip)

  > \[!TIP|label:references:]
  >
  > * [\* iMarslo: ssh and tar](https://imarslo.gitbook.io/book/awesomeshell/ssh#ssh-and-tar)
  >   * [copy multiple files to remote server](https://imarslo.gitbook.io/book/awesomeshell/ssh#copy-multiple-files-to-remote-server)
  >   * [tar all and extra in remote](https://imarslo.gitbook.io/book/awesomeshell/ssh#tar-all-and-extra-in-remote)
  >   * [copy local file content into remote](https://imarslo.gitbook.io/book/awesomeshell/ssh#copy-local-file-content-into-remote)

  ```bash
  $ tar czv file1 file2 file3 | ssh user@host 'tar xzv -C /target/dir'
  ```
* [recursively unrar into dir containing archive](https://www.commandlinefu.com/commands/view/11324/recursively-unrar-into-dir-containing-archive)

  ```bash
  $ find . -name '*.rar' -execdir unrar e {} \;
  ```

#### find and rename

```bash
$ find -iname "*.sh" -exec rename "s/.sh$/.shell/" {} \; -print
```

#### find and sort

* via timestamp

  > \[!NOTE]
  >
  > * [How to Find and Sort Files Based on Modification Date and Time in Linux](https://www.tecmint.com/find-and-sort-files-modification-date-and-time-in-linux/)
  >
  >   ```bash
  >   $ find . -type f -printf "\n%AD %AT %p" | head -n 11 | sort -k1.8n -k1.1nr -k1
  >   # or
  >   $ find . -type f -printf "\n%Tm/%Td/%TY %TT %p" | sort -k1.8n -k1.1nr -k1
  >   ```
  > * [\* iMarslo : find printf](https://github.com/marslo/ibook/blob/marslo/docs/cheatsheet/cheatsheet/text-processing/text-processing.html#printf)
  > * [\* `printf` time formats](https://www.codedodle.com/find-printf.html#time-formats)
  >
  >   ```bash
  >   $ find . -printf "%T<format>\n"
  >   ```
  > * [sort by datetime format in bash](https://stackoverflow.com/a/67608960/2940319)
  > * [how to sort file by DD-MM-YYYY dates](https://unix.stackexchange.com/a/485708/29178)
  > * [Sort logs by date field in bash](https://stackoverflow.com/a/5243126/2940319)

  ```bash
  # via %T+
  $ find ~/.marslo -type f -printf "%10T+ | %p\n" | sort -r | head -10

  # via %Td-%Tm-%TY
  $ find ~/.marslo -type f -printf "\n%Td-%Tm-%TY %TT %p" |
    sort -b -k1.7,1.10 -k1.4,1.5 -k1.1,1.2 -r
  ```

  * via [awk+sort](https://stackoverflow.com/a/73881909/2940319)

    ```bash
    $ cat a.txt
    ABC,1/02/2022,05:50
    OPQ,18/10/2023,07:50
    HIJ,31/09/2023,08:50
    DEF,1/02/2021,06:00

    $ awk -F'[,/]' '{printf "%04d-%02d-%02d\t%s\n", $4,$3,$2, $0}' a | sort -r | cut -f2-
    OPQ,18/10/2023,07:50
    HIJ,31/09/2023,08:50
    ABC,1/02/2022,05:50
    DEF,1/02/2021,06:00
    ```

#### find and copy

> \[!TIP]
>
> * [\* iMarslo: find by timestamp](https://github.com/marslo/ibook/blob/marslo/docs/cheatsheet/text-processing/text-processing.html#find-by-timestamp)

```bash
$ find . -type f -newermt '2023-10-16 00:00:00' -exec cp -a --parents -t /path/to/target "{}" \+

# or
$ diff=$(( ($(date --date "24-02-29" +%s) - $(date --date "231016" +%s) )/(60*60*24) ))
$ find . -type f -daystart -mtime -$((diff+1)) -exec cp -a --parents -t /path/to/target "{}" \+
```

#### download and extract

> \[!TIP|label:references:]
>
> * [\* see also: iMarslo: download and extract](https://imarslo.gitbook.io/book/good/..#download-and-extract)

* gz

  ```bash
  $ wget -O - http://example.com/a.gz    | tar xz
  # or
  $ curl -fsSL http://example.com/a.gz   | tar xz
  # or
  $ curl -fsSL "http://example.com/a.gz" | tar zxvf -

  # or: https://www.commandlinefu.com/commands/view/353/extract-tarball-from-internet-without-local-saving
  $ wget -qO - "http://www.tarball.com/tarball.gz" | tar zxvf -
  ```
* tar.gz

  ```bash
  $ curl -fsSL https://dlcdn.apache.org/maven/maven-3/3.9.5/binaries/apache-maven-3.9.5-bin.tar.gz | tar xzf - -C /path/to/targeT
  ```
* tar.xz

  ```bash
  $ curl -fsSL https://ftp.gnu.org/gnu/glibc/glibc-2.38.tar.xz | tar -xJf - -C /path/to/target
  ```
* zip

  ```bash
  $ curl -fsSL https://downloads.gradle.org/distributions/gradle-8.4-bin.zip | bsdtar xzf - -C /path/to/target

  # with password
  $ curl -fsSL https://path/to/file.zip | bsdtar -xzf- --passphrase <PASSWD_OF_ZIP> - -C <EXTRACT_PATH>
  ```

#### [mirror website](https://explainshell.com/explain?cmd=wget+--mirror+--page-requisites+--html-extension+--convert-links++example.com)

```bash
$ wget --mirror --page-requisites --html-extension --convert-links $URL

# https://www.linuxjournal.com/content/downloading-entire-web-site-wget
$ wget --recursive --no-clobber --page-requisites --html-extension --convert-links --restrict-file-names=windows --domains website.org --no-parent sample.com

# or download entire website: https://www.commandlinefu.com/commands/view/901/download-an-entire-website
$ wget --random-wait -r -p -e robots=off -U mozilla http://www.example.com

# https://superuser.com/a/1415765/112396
$ wget --recursive --level 5 --no-clobber --page-requisites --adjust-extension --span-hosts --convert-links --restrict-file-names=windows --domains sample.com --no-parent sample.com

# https://www.commandlinefu.com/commands/view/30/retrieve-a-list-of-all-webpages-on-a-site
$ URL=www.example.com && wget -rq --spider --force-html "http://$URL" && find $URL -type d > url-list.txt && rm -rf $URL
```

* [download image only](https://www.commandlinefu.com/commands/view/29/download-all-images-from-a-site)

  ```bash
  $ wget -r -l1 --no-parent -nH -nd -P/tmp -A".gif,.jpg" http://example.com/images
  ```

#### kubectl apply from stdin

```bash
$ cat << EOF | kubectl create -f -
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: $(echo -n 'admin' | base64)
  password: $(echo -n 'password' | base64)
EOF
```

#### [show 256 colors](https://www.commandlinefu.com/commands/view/5879/show-numerical-values-for-each-of-the-256-colors-in-bash)

```bash
$ for code in {0..255}; do echo -e "\e[38;05;${code}m $code: Test"; done)

# or: https://www.commandlinefu.com/commands/view/6138/show-numerical-values-for-each-of-the-256-colors-in-bash
$ for i in {0..255}; do echo -e "\e[38;05;${i}m${i}"; done | column -c 80 -s ' '; echo -e "\e[m"

# or: https://www.commandlinefu.com/commands/view/11759/show-numerical-values-for-each-of-the-256-colors-in-bash-for-bold-and-normal-fonts
$ for code in $(seq -w 0 255); do for attr in 0 1; do printf "%s-%03s %bTest%b\n" "${attr}" "${code}" "\e[${attr};38;05;${code}m" "\e[m"; done; done | column -c $((COLUMNS*2))
# better vesion
$ for code in $(seq -w 0 255); do for attr in 0 1; do printf "%b%s-%03s%b\n"  "\e[${attr};38;05;${code}m" "${attr}" "${code}" "\e[m"; done; done | column -c $((COLUMNS*3))

# .. zsh ..
# or: https://www.commandlinefu.com/commands/view/5876/show-numerical-values-for-each-of-the-256-colors-in-zsh
$ for code in {000..255}; do print -P -- "$code: %F{$code}Test%f"; done

# or: https://www.commandlinefu.com/commands/view/12471/show-numerical-values-for-each-of-the-256-colors-in-zsh
for i in {0..255}; do echo -e "\e[38;05;${i}m\\\e[38;05;${i}m"; done | column -c 80 -s ' '; echo -e "\e[m"
```

#### [rm and exclude](https://www.commandlinefu.com/commands/view/5351/delete-all-files-in-a-folder-that-dont-match-a-certain-file-extension)

```bash
$ rm !(*.foo|*.bar|*.baz)

# https://www.commandlinefu.com/commands/view/4576/remove-everything-except-that-file
$ find . ! -name <FILENAME> -delete

# https://www.commandlinefu.com/commands/view/4570/remove-everything-except-that-file
$ ( shopt -s extglob; rm !(<PATTERN>) )
```

#### [split file to equal size](https://www.commandlinefu.com/commands/view/5106/split-a-file-into-equal-size-chunks-and-archive-to-email-account.)

```bash
$ split -b4m file.tgz file.tgz. ; for i in file.tgz.*; do SUBJ="Backup Archive"; MSG="Archive File Attached"; echo $MSG | mutt -a $i -s $SUBJ YourEmail@(E)mail.com
```

### sync mirror

> \[!NOTE]
>
> * [How to create public mirrors for CentOS](https://wiki.centos.org/HowTos\(2f\)CreatePublicMirrors.html)
> * [Configure DNF/Yum Mirror Server](https://www.server-world.info/en/note?os=CentOS_Stream_9\&p=localrepo)

```bash
$ rsync -aqzH --delay-updates --delete-after  msync.centos.org::CentOS /path/to/local/mirror/root

# stream 9
$ rsync -aqzH --delay-updates --delete-after rsync.stream.centos.org::CentOS-Stream-All /path/to/local/mirror/root

# or src
$ rsync -aqzH --delay-updates --delete-after rsync.stream.centos.org::CentOS-Stream-nosrc /path/to/local/mirror/root

# exclude debuginfo
$ rsync -aqzH --delay-updates --delete-after rsync.stream.centos.org::CentOS-Stream-nodebug /path/to/local/mirror/root
```

### get all declare

> \[!NOTE|label:references:]
>
> * [How to list all variables names and their current values?](https://askubuntu.com/a/1451692/92979)
> * [What is the difference between source and export?](https://stackoverflow.com/q/15474650/2940319)

```bash
$ declare
$ declare -p
$ declare -xp

# or
$ typeset
$ typeset -p

# or
$ compgen -v
$ compgen -v | while read line; do echo $line=${!line};done
$ compgen -v | while read line; do declare -p $line; done

# or
$ export
$ printenv
```

#### print env

> \[!NOTE|label:references:]
>
> * [List all shell variables](https://unix.stackexchange.com/a/176003/29178)

```bash
$ set -o posix ; set | awk -F '=' '{ print $1 }'

# or
$ env
$ env | awk -F '=' '{ print $1 }'
$ env | awk -F '=' '{ print $1 }' | tr '\n' ' '

# or
$ printenv
```

### using string as variable name

> \[!NOTE|label:references:]
>
> * [3.5.3 Shell Parameter Expansion](https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html)
> * [How to get a variable value if variable name is stored as string?](https://stackoverflow.com/a/1921337/2940319)
> * [Dynamic variable names in Bash](https://stackoverflow.com/a/18124325/2940319)

* [`eval`](https://stackoverflow.com/a/1921376/2940319)

  ```bash
  $ aa='echo me'
  $ var='aa'
  $ eval echo \$$var
  echo me
  ```
* [`${!var}`](https://stackoverflow.com/a/1921337/2940319)

  ```bash
  $ var1="this is the real value"
  $ a="var1"
  $ echo "${!a}"
  this is the real value
  ```
* more usage

  ```bash
  $ sunny='''
  \033[38;5;226m    \\   /    \033[0m
  \033[38;5;226m     .-.     \033[0m
  \033[38;5;226m  ― (   ) ―  \033[0m
  \033[38;5;226m     `-’     \033[0m
  \033[38;5;226m    /   \\    \033[0m
  '''

  $ fewClouds='''
  \033[38;5;226m   \\  /\033[0m
  \033[38;5;226m _ /\"\"\033[38;5;250m.-.    \033[0m
  \033[38;5;226m   \\_\033[38;5;250m(   ).  \033[0m
  \033[38;5;226m   /\033[38;5;250m(___(__) \033[0m

  '''

  $ codeMap=(
               ["01"]="sunny"
               ["02"]="fewClouds"
             )
  $ icon="$(/usr/bin/curl -sg "https://api.openweathermap.org/data/3.0/onecall?lat=37.3541132&lon=-121.955174&units=metric&exclude=hourly,daily,minutely,alerts&appid=${OWM_API_TOKEN}" | jq -r .current.weather[].icon)"
  $ echo ${icon}
  02n

  $ echo -e "${!codeMap["${icon:0:-1}"]}"
  ```

  ![using string as var name](https://676843113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Me-5q7RO3-g9rN8QNaK%2Fuploads%2Fgit-blob-9b269d76d6da2471f8070272e452098802f94cbb%2Fbash-map-var-is-string.png?alt=media)

### `<<<`, `< <(..)`

> \[!TIP]
>
> * `< <(` is [Process Substitution](http://mywiki.wooledge.org/ProcessSubstitution)
>   * The difference between `<(...)` and `>(...)` is merely which way the redirections are done

#### `< <(..)` && `> >(..)`

> \[!NOTE]
>
> * [process substitution](http://mywiki.wooledge.org/ProcessSubstitution)
> * [syntax](https://askubuntu.com/a/678924/92979)
>
>   ```bash
>   $ command1 < <( command2 )
>   # equals to
>   $ command2 | command1
>
>   # if read from file, then using `< /path/to/file`
>   ```
> * [SubShell](http://mywiki.wooledge.org/BashFAQ/024)
> * example:
>
>   ```bash
>   $ while read line; do echo "-- ${line} --"; done < <(ls -1)
>
>   # equals to: http://mywiki.wooledge.org/BashFAQ/024
>   $ ls -1 | while read line; do echo "-- ${line} --"; done
>   # equals to
>   $ ls -1 | xargs -n1 -i bash -c "echo \"-- {} --\""
>
>   # equals to read from file via `< /path/to/file`
>   $ ls -1 > ls.txt
>   $ while read line; do echo "-- ${line} --"; done < ls.txt
>   ```

```bash
$ wc < <(date)
    1       6      29

# same as:
$ date | wc
    1       6      29
```

* `< <(..)`

  > \[!TIP|label:referencs:]
  >
  > * [subshell](http://mywiki.wooledge.org/BashFAQ/024)
  > * tips:
  >
  >   ```bash
  >   # If commandA can read the data from stdin
  >   commandB | commandA                 # You can now get the exit code of commandB from PIPESTATUS.
  >   commandB > >(commandA)              # You can now get the exit code of commandB from $? (or by putting this in an if)
  >
  >   # If commandA cannot read it from stdin, but requires a file argument
  >   commandB > >(commandA <(cat))       # Again, commandB's exit code is available from $?
  >
  >   # You can also keep commandB's output in memory.  When you do this, you can get commandB's exit code from $? or put the assignment in an if
  >   b=$(commandB); commandA <<< "$b"    # Here, commandA reads commandB's output from stdin
  >   ```

  * common usage

    ```bash
    $ diff <(sort list1) <(sort list2)

    # or
    $ while read file; do
        echo -e "\n\033[1;33m${file}\n---\033[0m"
        sed -n "/<<<<<<< HEAD/,/>>>>>>> /!d;=;p" ${file}
        echo -e "\n\033[1;33m---\033[0m"
      done < <(git grep --no-color -l "<<<<<<< HEAD")
    ```
* `> >(..)`

  > \[!TIP]
  >
  > * [Process Substitution](http://mywiki.wooledge.org/ProcessSubstitution)
  >   * `>(...)` is used less frequently; the most common situation is in conjunction with `tee(1)`.
  >   * `>(...)` is handy when redirecting the output to multiple files, based on some criteria.

  ```bash
  # For example:
  $ some_command | tee >(grep A > A.out) >(grep B > B.out) >(grep C > C.out) > /dev/null
  ```

### parameter substitution

{% hint style="info" %}

> reference:
>
> * [10.2. Parameter Substitution](https://tldp.org/LDP/abs/html/parameter-substitution.html)
> * [\* iMarslo: params](https://github.com/marslo/ibook/blob/marslo/docs/linux/util/params.html)
> * [How can I keep quotes in Bash arguments?](https://stackoverflow.com/q/1668649/2940319)
> * [How can I preserve quotes in printing a Bash script's arguments?](https://stackoverflow.com/q/10835933/2940319)
>   {% endhint %}

|          EXPR          | DESCRIPTION                                               |
| :--------------------: | --------------------------------------------------------- |
|  `${variable-default}` | if variable is unset, use default                         |
|  `${variable=default}` | if variable is unset, set variable to default             |
|    `${variable+alt}`   | if variable is set, use alt, else use null string         |
| `${variable:-default}` | with `":[-=+]"`, condition takes also "declared but null" |

#### arguments substitution

{% hint style="info" %}

> reference:
>
> * [Process all arguments except the first one (in a bash script)](https://stackoverflow.com/a/62630975/2940319)
> * [Getting the last argument passed to a shell script](https://stackoverflow.com/a/5496054/2940319)
> * [Extract parameters before last parameter in "$@"](https://stackoverflow.com/a/1215592/2940319)
>   {% endhint %}

<table><thead><tr><th align="center">EXPR</th><th>DESCRIPTION</th></tr></thead><tbody><tr><td align="center"><code>$@</code></td><td><pre><code>           p1 p2 p3 p4 p5 p6 
</code></pre></td></tr><tr><td align="center"><code>${@: 0}</code></td><td><pre><code> ./args.sh p1 p2 p3 p4 p5 p6 
</code></pre></td></tr><tr><td align="center"><code>${@: 1}</code></td><td><pre><code>           p1 p2 p3 p4 p5 p6 
</code></pre></td></tr><tr><td align="center"><code>${@: 2}</code></td><td><pre><code>              p2 p3 p4 p5 p6 
</code></pre></td></tr><tr><td align="center"><code>${@: 2:1}</code></td><td><pre><code>              p2             
</code></pre></td></tr><tr><td align="center"><code>${@: 2:2}</code></td><td><pre><code>              p2 p3          
</code></pre></td></tr><tr><td align="center"><code>${@: -2}</code></td><td><pre><code>                       p5 p6 
</code></pre></td></tr><tr><td align="center"><code>${@: -2:1}</code></td><td><pre><code>                       p5    
</code></pre></td></tr><tr><td align="center"><code>${*: -1}</code> or <code>${@: $#}</code></td><td><pre><code>                          p6 
</code></pre></td></tr><tr><td align="center"><code>${@: 1:$#-1}</code></td><td><pre><code>           p1 p2 p3 p4 p5    
</code></pre></td></tr></tbody></table>

* sample with uncertain parameters

  ```bash
  local opt=''
  local loop=true
  local path params

  while ${loop} && [[ $# -gt 0 ]]; do
    case "$1" in
      -*) opt+="$1 "; shift;;
       *) loop=false       ;;
    esac
  done

  if [[ 1 = "$#" ]]; then
    path=''
    params="$1"
  else
    path=${*: -1}
    params=${*:1:$#-1}
  fi
  ```

  ```bash
  echo '---------------- before shift -------------------'
  echo ".. \$# : $#"
  echo ".. \$@ : $@"
  echo ".. \$* : $*"

  echo '---------------- after shift -------------------'
  opt=''
  ss=''
  loop=true

  while $loop && [[ $# -gt 0 ]]; do
    case "$1" in
      -*) opt+="$1 "; shift;;
       *) loop=false       ;;
    esac
  done

  echo ".. \$#   : $#"
  echo ".. \$@   : $@"
  echo ".. \$*   : $*"
  echo ".. \$opt : $opt"

  if [[ 0 = "$#" ]]; then
    echo -e "\033[0;33mERROR: must provide at least one non-opt param\033[0m"
    exit 2
  elif [[ 1 = "$#" ]]; then
    path=''
    params="$1"
  else
    path=${*: -1}
    params=${*:1:$#-1}
  fi

  echo '---------------- result -------------------'
  echo ">> opt    : ${opt}"
  echo ">> params : ${params}"
  echo ">> path   : ${path}"
  ```

  ```bash
  $ ./c.sh -1 -2 --3-4 a b c d e
  ---------------- before shift -------------------
  .. $# : 8
  .. $@ : -1 -2 --3-4 a b c d e
  .. $* : -1 -2 --3-4 a b c d e
  ---------------- after shift -------------------
  .. $#   : 5
  .. $@   : a b c d e
  .. $*   : a b c d e
  .. $opt : -1 -2 --3-4
  .. $ss  : a b c d e
  ---------------- result -------------------
  >> opt    : -1 -2 --3-4
  >> params : a b c d
  >> path   : e

  $ ./c.sh aa bb
  ---------------- before shift -------------------
  .. $# : 2
  .. $@ : aa bb
  .. $* : aa bb
  ---------------- after shift -------------------
  .. $#   : 2
  .. $@   : aa bb
  .. $*   : aa bb
  .. $opt :
  .. $ss  : aa bb
  ---------------- result -------------------
  >> opt    :
  >> params : aa
  >> path   : bb

  $ ./c.sh -1
  ---------------- before shift -------------------
  .. $# : 1
  .. $@ : -1
  .. $* : -1
  ---------------- after shift -------------------
  .. $#   : 0
  .. $@   :
  .. $*   :
  .. $opt : -1
  ERROR: must provide at least one non-opt param
  ```

#### quotas

* `${@@Q}`

  ```bash
  # a.sh
  line="${@@Q}"
  echo $line

  $ bash a.sh -a -b --c='1 2 3'
  '-a' '-b' '--c=1 2 3'
  ```

  ```bash
  # https://stackoverflow.com/a/39463371/2940319
  $ expand-q() { for i; do echo ${i@Q}; done; }
  $ expand-q -a -b --c='1 2 3'
  '-a'
  '-b'
  '--c=1 2 3'
  ```

  ```bash
  # https://stackoverflow.com/a/72745869/2940319
  function quote() {
    local QUOTED_ARRAY=()
    for ARGUMENT; do
      case ${ARGUMENT} in
        --*=*)
          QUOTED_ARRAY+=( "${ARGUMENT%%=*}=$(printf "%q" "${ARGUMENT#*=}")" )
          shift
        ;;
        *)
          QUOTED_ARRAY+=( "$(printf " %q" "${ARGUMENT}")" )
        ;;
      esac
    done
    echo ${QUOTED_ARRAY[@]}
  }

  ARGUMENTS="$(quote "${@}")"
  echo "${ARGUMENTS}"
  ```
* `printf " %q" "${@}"`

  > \[!NOTE]
  >
  > * [git-effort](https://github.com/tj/git-extras/blob/main/bin/git-effort#L162)

  ```bash
  while test -n "$1"; do
    case "$1" in
      -- ) shift; GIT_OPT=$(printf " %q" "${@}"); break ;;
    esac
  done
  GIT_OPT="${GIT_OPT#\ \'\'}"
  ```

  ```bash
  # https://stackoverflow.com/a/39463371/2940319
  $ params-q() { printf "%q\n" "$@"; }
  $ params-q -a -b --c='1 2 3'
  -a
  -b
  --c=1\ 2\ 3
  ```
* String replacement

  > \[!TIP]
  >
  > * [\* iMarslo: add `'` or `"` to strings](https://github.com/marslo/ibook/blob/marslo/docs/cheatsheet/text-processing/text-processing.html#tricky)

  ```bash
  # a.sh
  while test -n "$1"; do
    case "$1" in
      -- ) shift; GIT_OPT="$@";;
      *  ) shift;;
    esac
  done

  GIT_OPT=$(echo "${GIT_OPT}" |
               sed -r 's/\s+--/\n--/g' |
               sed -r "s/^([^=]+)=(.+)$/\1='\2'/g" |
               sed -e 'N;s/\n/ /'
            )
  echo $GIT_OPT

  $ bash a.sh -a -b -- --c='1 2 3'
  --c='1 2 3'
  ```

  ```bash
  # https://stackoverflow.com/a/8723305/2940319
  # a.sh
  C=''
  for i in "$@"; do
      i="${i//\\/\\\\}"
      C="$C \"${i//\"/\\\"}\""
  done
  echo $C

  $ bash ~/a.sh -a -b --c='1 2 3'
  "-a" "-b" "--c=1 2 3"
  ```

### string manipulations

{% hint style="info" %}

> reference:
>
> * [10.1. Manipulating Strings](https://tldp.org/LDP/abs/html/string-manipulation.html)
>   {% endhint %}

| EXPR                               | DESCRIPTION                                                |
| ---------------------------------- | ---------------------------------------------------------- |
| `${#string}`                       | length                                                     |
| `${string:position}`               | substring, or positional parameter with `$*` and `$#`      |
| `${string:position:length}`        | substring                                                  |
| `${string#substring}`              | deletes shortest match of $substring from front of $string |
| `${string##substring}`             | same but longest match                                     |
| `${string%substring}`              | shortest from back                                         |
| `${string%%substring}`             | longest from back                                          |
| `${string/substring/replacement}`  | replace first match                                        |
| `${string//substring/replacement}` | replace all matches                                        |
| `${string/#substring/replacement}` | replace if matches front end of $string                    |
| `${string/%substring/replacement}` | replace if matches back end of $string                     |
| `${var^}`                          | uppercase first char                                       |
| `${var^^}`                         | uppercase all chars                                        |
| `${var,}`                          | lowercase first char                                       |
| `${var,,}`                         | lowercase all chars                                        |

### compound comparison

#### [SC2155](https://www.shellcheck.net/wiki/SC2235)

* problematic code:

  ```bash
  ([ "$x" ] || [ "$y" ]) && [ "$z" ]
  ```
* correct code:

  ```bash
  { [ "$x" ] || [ "$y" ]; } && [ "$z" ]
  ```
* example
  * [git-retag](https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/git-retag#L33)

#### [SC2155](https://www.shellcheck.net/wiki/SC2155)

* problematic code:

  ```bash
  export foo="$(mycmd)"
  ```
* correct code:

  ```bash
  foo="$(mycmd)"
  export foo
  ```

#### escape code

> \[!TIP]\
> references:
>
> * [Color Codes, Escapes & Languages](https://gist.github.com/Prakasaka/219fe5695beeb4d6311583e79933a009?permalink_comment_id=4086771#gistcomment-4086771)
> * [Escape codes](https://smallbasic.github.io/pages/escape.html)
> * [Escape sequences](https://perldoc.perl.org/perlre#Escape-sequences)
> * [Quote and Quote-like Operators](https://perldoc.perl.org/perlop#Quote-and-Quote-like-Operators)

| ESCAPE CODE | LANGUAGE               | DESCRIPTION  |
| :---------: | ---------------------- | ------------ |
|    `\x1b`   | Node.js                | hex char     |
|    `\x1b`   | Node.js w/ TS          | hex char     |
|   `\u001b`  | Python                 | hex char     |
|    `\033`   | GNU Cpp                | octal char   |
|    `\033`   | ANSI C                 | octal char   |
|    `\033`   | POSIX-compliant shells | octal char   |
|     `\e`    | Bash                   | -            |
|    `\c[`    | -                      | control char |

### echo

#### echo var name from variable

> \[!NOTE]
>
> * [Chapter 28. Indirect References](https://tldp.org/LDP/abs/html/ivr.html)
> * [3.5.3 Shell Parameter Expansion: `{!parameter}`](https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html)
> * [9.2. Typing variables: declare or typeset](https://tldp.org/LDP/abs/html/declareref.html)
> * sample:
>
>   ```bash
>   $ foo=bar             # `bar` is the var name
>   $ bar=baz             # `baz` is the var value
>   ```

* `typeset`

  ```bash
  $ typeset -p "${foo}"$
  declare -- bar="baz"
  ```
* `eval \$$`

  ```bash
  $ eval echo \$$foo
  baz
  ```

  * more:

    ```bash
    $ echo \$$foo
    $bar
    # or
    $ echo '$'$foo
    $bar
    ```
* `{!parameter}`

  ```bash
  $ echo "${!foo}"
  baz
  ```

#### echo var name

> \[!NOTE]
>
> * [Shell: print both variable name and value?](https://stackoverflow.com/a/71637863/2940319)
> * [\* How to Echo the Variable Name Instead of Variable Value](https://www.baeldung.com/linux/echo-variable-name)
> * [`typeset -p`](https://unix.stackexchange.com/a/397595/29178)
> * sample
>
>   ```bash
>   $ a1='a'
>   $ a2='aa'
>   $ c1='c'
>   $ c2='cc'
>   ```

* `typeset`

  ```bash
  $ typeset -p c2
  declare -- c2="cc"
  ```
* `{!parameter@}`

  ```bash
  $ echo "${!c@}"
  c1 c2

  $ echo "${!a@}"
  a1 a2
  ```
* more

  ```bash
  superEcho() {
    echo "$1 = ${!1}"
  }

  $ superEcho foo
  foo = bar
  $ superEcho bar
  bar = baz
  ```

### ls

> \[!NOTE|label:references]
>
> * [How can I make ls only display files?](https://askubuntu.com/a/1322137/92979)

```bash
# show file only
$ ls -p | command grep -v /

# show folder only
$ ls -p | command grep / | command grep "^."

# show all files ( including hidden )
$ ls -Ap | command grep -v / | command grep "^."

# show all folders ( including hidden )
$ ls -Ap | command grep / | command grep "^."

# show hidden folder only
$ ls -Ap | command grep / | command grep "^\." | command grep "\."

# show hidden file only
$ ls -Ap | command grep -v / | command grep "^\." | command grep "\."

# show all including hidden
$ ls -Ap

# show hidden file and folder
$ ls -Ap | command grep "^\."
```

### bash completion

> \[!NOTE|label:references]
>
> * [\* iMarslo: bash completion](https://github.com/marslo/ibook/blob/marslo/docs/cheatsheet/cheatsheet/bash/sugar.md#bash-completion)
> * [\* iMarslo: bash completion troubleshooting for linux](https://github.com/marslo/ibook/blob/marslo/docs/cheatsheet/linux/troubleshooting.md#bash_completion)
> * [\* iMarslo : `_vim()`](https://raw.githubusercontent.com/marslo/mylinux/master/confs/home/.marslo/.completion/vim.sh)
> * create bash completion
>   * [\* Creating a bash completion script](https://iridakos.com/programming/2018/03/01/bash-programmable-completion-tutorial)
>   * [\* An introduction to bash completion: part 1](https://web.archive.org/web/20190722115536/https://debian-administration.org/article/316/An_introduction_to_bash_completion_part_1)
>   * [\* An introduction to bash completion: part 2](https://web.archive.org/web/20200327211933/https://debian-administration.org/article/317/An_introduction_to_bash_completion_part_2)
>
>     ```bash
>     $ complete -W "--help --verbose --version" foo
>     $ foo --<TAB>
>     --help
>     --verbose
>     --version
>     ```
>   * [8.7 Programmable Completion Builtins](https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html)
>   * [8.6 Programmable Completion](https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html)
>   * [8.8 A Programmable Completion Example](https://www.gnu.org/software/bash/manual/html_node/A-Programmable-Completion-Example.html#A-Programmable-Completion-Example)
>   * [cykerway/complete-alias](https://github.com/cykerway/complete-alias)
>   * [Multi Level Bash Completion](https://stackoverflow.com/a/5303225/2940319)
>   * [List all commands that a shell knows](https://unix.stackexchange.com/a/94825/29178)
>   * [Integralist/1. bash autocomplete for your custom programs.md](https://gist.github.com/Integralist/0500e6b5aabf95034cd83eff8c9e2ead)
>   * [8.6 Programmable Completion : `_completion_loader()`](https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html)
>   * [8.4.6 Letting Readline Type For You](https://www.gnu.org/software/bash/manual/html_node/Commands-For-Completion.html)
> * paths:
>   * osx: `$(brew --prefix)/etc/bash_completion.d`
>     * completion files in `bash-completion@2`: `$(brew --prefix bash-completion@2)/share/bash-completion/completions/`
>   * centos: `/usr/share/bash-completion/completions` or `/etc/bash_completion.d`
>   * ubuntu: `/usr/share/bash-completion/completions`

* print existing completion

  ```bash
  $ complete -p vim
  complete -o bashdefault -o default -F _fzf_opts_completion vim

  $ complete -p ffs
  complete -o bashdefault -o default -o nosort -F _fd ffs

  $ complete -p ff
  complete -o bashdefault -o default -o nosort -F _fd ff
  ```
* remove completion

  ```bash
  $ complete -p vim
  complete -o bashdefault -o default -F _fzf_opts_completion vim

  $ complete -r vim

  $ complete -p vim
  -bash: complete: vim: no completion specification
  ```
* [list all completions](https://unix.stackexchange.com/a/94784/29178)

  > \[!TIP|label:references:]
  >
  > ```bash
  > $ compgen --help
  > Display possible completions depending on the options
  > ```

  ```bash
  $ complete

  # show all commands
  $ compgen -c
  ```

  | COMMAND                       | DESCRIPTION |
  | ----------------------------- | ----------- |
  | `$ compgen -c`                | commands    |
  | `$ compgen -a`                | aliases     |
  | `$ compgen -b`                | built-ins   |
  | `$ compgen -k`                | keywords    |
  | `$ compgen -A function`       | functions   |
  | `$ compgen -A function -abck` | all above   |

  ```bash
  $ complete
  complete -o default -F _quotaon quotaon
  complete -o default -F _fzf_path_completion mv
  complete -F _postcat postcat
  complete -o default -o nospace -v -F _fzf_var_completion printenv
  complete -o default -F __start_kubectl kcn
  complete -F _filedir_xspec mpg321
  complete -F _filedir_xspec tex
  complete -F _make gmake
  complete -o bashdefault -o default -F _fzf_path_completion diff3
  complete -o default -F _ansible ansible
  complete -o default -F _fzf_path_completion head
  complete -o default -F __start_kubectl kt1
  complete -o default -F __start_kubectl kt2
  complete -o default -F _fzf_path_completion uniq
  complete -F _command else
  complete -o default -F __start_kubectl kt3
  complete -F _ldapdelete ldapdelete
  complete -F _configure configure
  complete -F _filedir_xspec freeamp
  complete -F _lzma lzma
  complete -o default -F __start_kubectl kcc
  complete -F _filedir_xspec gqmpeg
  complete -F _filedir_xspec texi2html
  complete -o default -F _complete_groovydoc groovydoc
  complete -F _filedir_xspec hbpp
  complete -F _xsltproc xsltproc
  complete -F _filedir_xspec jadetex
  complete -F _docker droot
  complete -o default -F _longopt mkfifo
  complete -o bashdefault -o default -F _fzf_path_completion svn
  complete -o default -F _fzf_path_completion tee
  complete -F _javaws javaws
  complete -F _mktemp mktemp
  complete -F _filedir_xspec rpm2cpio
  complete -F _docker dvi
  complete -F _make pmake
  complete -o default -F _repquota repquota
  complete -F _filedir_xspec hbrun
  complete -F _autoscan autoscan
  complete -o default -F __start_kubectl kubectl
  complete -o default -F _screen screen
  complete -F _filedir_xspec ps2pdf14
  complete -o default -F _fzf_path_completion grep
  complete -F _fzf_path_completion vi
  complete -F _autoreconf autoheader
  complete -F _composite composite
  complete -F _fzf_path_completion bat
  complete -F _filedir_xspec ps2pdf13
  complete -o default -F _longopt objdump
  complete -F _filedir_xspec ps2pdf12
  complete -o default -F _longopt sha1sum
  complete -o default -F _longopt cut
  complete -F _filedir_xspec lyx
  complete -o bashdefault -o default -F _fzf_path_completion file
  complete -F _gcc gpc
  complete -F _filedir_xspec latex
  complete -o default -F _look look
  complete -F _gradle gradlew.bat
  complete -o bashdefault -o default -F _fzf_path_completion hx
  complete -F _filedir_xspec poedit
  complete -F _fzf_path_completion view
  complete -o bashdefault -o default -F _fzf_path_completion dirname
  complete -F _function typeset
  complete -o bashdefault -o default -F _fzf_path_completion hg
  complete -F _command nohup
  complete -a -F _fzf_alias_completion unalias
  complete -F _vipw vipw
  complete -g groupdel
  complete -F _make gnumake
  complete -u groups
  complete -F _filedir_xspec chromium-browser
  complete -F _filedir_xspec opera
  complete -F _filedir_xspec kbabel
  complete -F _fzf_host_completion telnet
  complete -F _gcc g77
  complete -F _filedir_xspec bzme
  complete -o bashdefault -o default -F _fzf_complete_ssh ssh
  complete -F _command vsound
  complete -c which
  complete -F _fzf_path_completion tar
  complete -o default -F _longopt m4
  complete -F _filedir_xspec madplay
  complete -o default -F _fzf_opts_completion fzf-tmux
  complete -F _docker drm
  complete -F _filedir_xspec dviselect
  complete -o default -F _fzf_path_completion cp
  complete -F _mas mas
  complete -F _animate animate
  complete -F _man whatis
  complete -o default -F _complete_groovysh groovysh.sh
  complete -F _filedir_xspec evince
  complete -o bashdefault -o default -F _brew brew
  complete -F _docker dcleanall
  complete -F _filedir_xspec realplay
  complete -o default -F _longopt strip
  complete -o bashdefault -o default -o nospace -F __git_wrap__gitk_main gitk
  complete -v readonly
  complete -o nospace -F _fzf_path_completion rsync
  complete -F _ctest ctest
  complete -o nospace -F _fzf_dir_completion cd
  complete -o default -F _complete_groovydoc groovydoc.bash
  complete -F _known_hosts showmount
  complete -F _filedir_xspec kdvi
  complete -o default -F _longopt tac
  complete -F _ldapaddmodify ldapmodify
  complete -F _filedir_xspec elinks
  complete -F _known_hosts fping
  complete -o default -F _longopt env
  complete -o default -F _quota quota
  complete -F _gradle ./gradlew
  complete -u chfn
  complete -F _docker drp
  complete -F _filedir_xspec compress
  complete -F _filedir_xspec pdfjadetex
  complete -F _filedir_xspec kghostview
  complete -F _man man
  complete -F _filedir_xspec pbunzip2
  complete -o default -F _brctl brctl
  complete -c type
  complete -F _ldapcompare ldapcompare
  complete -F _known_hosts ssh-installkeys
  complete -F _filedir_xspec iceweasel
  complete -F _filedir_xspec gtranslator
  complete -F _fzf_path_completion unzip
  complete -o default -F _longopt expand
  complete -o default -F _complete_groovyConsole groovyConsole.bash
  complete -o bashdefault -o default -o nospace -F _fzf_path_completion git
  complete -F _filedir_xspec lrunzip
  complete -o default -F _fzf_path_completion ln
  complete -F _command aoss
  complete -F _docker drps
  complete -F _filedir_xspec ggv
  complete -F _filedir_xspec oomath
  complete -F _filedir_xspec dvipdfmx
  complete -o default -F _fzf_path_completion ld
  complete -F _fzf_path_completion gunzip
  complete -F _filedir_xspec makeinfo
  complete -F _filedir_xspec okular
  complete -o default -F _complete_groovysh groovysh
  complete -F _ldapsearch ldapsearch
  complete -F _command xargs
  complete -j -P '"%' -S '"' jobs
  complete -o default -F _complete_groovy groovy.sh
  complete -F _filedir_xspec oowriter
  complete -o bashdefault -o default -F _fzf_path_completion emacsclient
  complete -F _cpack cpack
  complete -o default -F _fzf_path_completion tail
  complete -o default -F _longopt unexpand
  complete -o default -F _longopt netstat
  complete -F _docker dexe
  complete -o default -F _fzf_path_completion ls
  complete -F _filedir_xspec epiphany
  complete -o nospace -F __gio gio
  complete -o bashdefault -o default -F _fzf_path_completion nvim
  complete -o dirnames -o nospace -F _fzf_dir_completion pushd
  complete -F _filedir_xspec acroread
  complete -o default -o nospace -v -F _fzf_var_completion unset
  complete -F _postmap postalias
  complete -F _nmap nmap
  complete -o default -F _longopt csplit
  complete -F _known_hosts rsh
  complete -F _filedir_xspec sxemacs
  complete -F _command exec
  complete -F _filedir_xspec aviplay
  complete -F _ldapmodrdn ldapmodrdn
  complete -F _filedir_xspec rgvim
  complete -F _chsh chsh
  complete -F _autoconf autoconf
  complete -o default -F _longopt nm
  complete -o default -F _longopt nl
  complete -o default -F _complete_grape grape.bash
  complete -o nospace -F _user_at_host ytalk
  complete -F _fzf_proc_completion kill
  complete -F _fzf_path_completion java
  complete -F _cmake cmake
  complete -u sux
  complete -F _cancel cancel
  complete -F _filedir_xspec znew
  complete -o default -F _complete_groovydoc groovydoc.sh
  complete -F _id id
  complete -o default -F _longopt paste
  complete -F _ldapaddmodify ldapadd
  complete -F _docker dip
  complete -o bashdefault -F _perldoc perldoc
  complete -F _filedir_xspec kwrite
  complete -F _root_command really
  complete -o default -F _complete_groovyc groovyc.bash
  complete -o bashdefault -o default -o nospace -F __git_wrap__tig_main tig
  complete -F _filedir_xspec firefox
  complete -o bashdefault -o default -F _fzf_path_completion open
  complete -F _ip ip
  complete -o default -F __start_kubectl klc
  complete -F _docker drit
  complete -F _filedir_xspec dvipdfm
  complete -F _filedir_xspec ly2dvi
  complete -F _filedir_xspec oodraw
  complete -F _docker drun
  complete -F _import import
  complete -o default -F __start_kubectl kcswatch
  complete -F _gzip pigz
  complete -o default -F __start_kubectl kln
  complete -F _autoscan autoupdate
  complete -F _known_hosts dig
  complete -o nospace -F _user_at_host talk
  complete -F _filedir_xspec xemacs
  complete -F _docker dls
  complete -o nospace -F _dd dd
  complete -F _jarsigner jarsigner
  complete -F _filedir_xspec kpdf
  complete -F _man apropos
  complete -o default -F _longopt df
  complete -F _command eval
  complete -F _docker di
  complete -F _postsuper postsuper
  complete -F _postconf postconf
  complete -F _filedir_xspec bibtex
  complete -o default -F _pip_completion pip
  complete -F _docker dclr
  complete -F _postfix postfix
  complete -F _fzf_path_completion chown
  complete -F _filedir_xspec netscape
  complete -o default -F _longopt wget
  complete -F _command do
  complete -F _cargo cargo
  complete -F _gradle gradle
  complete -F _pgrep pgrep
  complete -F _filedir_xspec gview
  complete -F _filedir_xspec lzfgrep
  complete -o bashdefault -o default -o nosort -F _fd ffs
  complete -F _filedir_xspec lzless
  complete -o default -F _fzf_path_completion du
  complete -F _renice renice
  complete -F _lsof lsof
  complete -F _docker dv
  complete -F _known_hosts tracepath
  complete -o default -F __start_kubectl kit
  complete -o default -F _fzf_path_completion wc
  complete -F _fzf_path_completion gzip
  complete -F _newgrp newgrp
  complete -o default -F _ansible-galaxy ansible-galaxy
  complete -F _filedir_xspec cdiff
  complete -F _fzf_path_completion emacs
  complete -F _filedir_xspec zipinfo
  complete -F _docker dcleani
  complete -F _filedir_xspec google-chrome
  complete -F _gcc c++
  complete -F _crontab crontab
  complete -F _filedir_xspec rview
  complete -A shopt shopt
  complete -F _docker dcleanc
  complete -F _root_command sudo
  complete -F _killall pkill
  complete -F _fzf_path_completion javac
  complete -F _fzf_path_completion ftp
  complete -o default -F _longopt uname
  complete -o bashdefault -o default -F _rg rg
  complete -F _known_hosts ping
  complete -F _filedir_xspec wine
  complete -F _filedir_xspec galeon
  complete -F _filedir_xspec pdflatex
  complete -F _docker dex
  complete -F _known_hosts rlogin
  complete -o default -F _fzf_opts_completion fzf
  complete -F _filedir_xspec portecle
  complete -o default -F _longopt sha384sum
  complete -o default -F _fzf_path_completion rm
  complete -F _filedir_xspec modplugplay
  complete -F _ri ri
  complete -o default -F _quotaoff quotaoff
  complete -F _filedir_xspec dillo
  complete -F _filedir_xspec fbxine
  complete -F _filedir_xspec lokalize
  complete -F _root_command gksudo
  complete -F _command nice
  complete -o default -F _longopt tr
  complete -o default -F _npm_completion npm
  complete -F _filedir_xspec oocalc
  complete -o default -F _complete_groovyc groovyc.sh
  complete -F _gradle gradlew
  complete -o default -F _longopt sha256sum
  complete -F _root_command gksu
  complete -F _filedir_xspec qiv
  complete -F _chgrp chgrp
  complete -F _filedir_xspec ps2pdfwr
  complete -o default -F _edquota edquota
  complete -F _filedir_xspec harbour
  complete -o bashdefault -o default -F _fzf_path_completion basename
  complete -o default -F _longopt ptx
  complete -F _filedir_xspec dvitype
  complete -o nospace -F __gsettings gsettings
  complete -F _gradle ./gradlew.bat
  complete -F _known_hosts traceroute
  complete -F _fzf_path_completion bzip2
  complete -j -P '"%' -S '"' fg
  complete -o bashdefault -o default -o nosort -F _fd ff
  complete -F _convert convert
  complete -F _filedir_xspec unpigz
  complete -o default -F _complete_groovy groovy
  complete -o bashdefault -o default -o nosort -F _fd fd
  complete -F _filedir_xspec mozilla
  complete -F _filedir_xspec dvips
  complete -o default -F _longopt who
  complete -F _montage montage
  complete -F _complete compgen
  complete -F _filedir_xspec ps2pdf
  complete -F _filedir_xspec gpdf
  complete -F _complete complete
  complete -F _filedir_xspec texi2dvi
  complete -o dirnames -F _umount umount
  complete -F _function function
  complete -o bashdefault -o default -F _fzf_path_completion mvim
  complete -o default -F _fzf_path_completion less
  complete -o default -F _longopt mknod
  complete -F _command padsp
  complete -F _passwd passwd
  complete -F _filedir_xspec kate
  complete -F _pkg_config pkg-config
  complete -o default -F _longopt bison
  complete -F _filedir_xspec mozilla-firefox
  complete -F _filedir_xspec kid3-qt
  complete -o default -F _longopt od
  complete -F _fzf_path_completion bunzip2
  complete -o default -o dirnames -F _mount mount
  complete -F _function declare
  complete -F _filedir_xspec pdftex
  complete -F _ag ag
  complete -o default -o nospace -F _fzf_var_completion export
  complete -F _vipw vigr
  complete -o default -F _ansible-doc ansible-doc
  complete -F _nslookup nslookup
  complete -F _ssh slogin
  complete -o nospace -F _alias alias
  complete -F _fzf_path_completion gvim
  complete -F _filedir_xspec kaffeine
  complete -F _stream stream
  complete -F _docker drdp
  complete -o default -F _complete_grape grape.sh
  complete -F _filedir_xspec mpg123
  complete -F _fzf_path_completion find
  complete -F _filedir_xspec lzegrep
  complete -o default -F _ansible-pull ansible-pull
  complete -o default -F _longopt split
  complete -o bashdefault -o default -F _fzf_path_completion zip
  complete -F _ssh autossh
  complete -F _filedir_xspec xv
  complete -o default -F _longopt fold
  complete -F _known_hosts mtr
  complete -o bashdefault -o default -F _fzf_path_completion ruby
  complete -o nospace -F _fzf_path_completion scp
  complete -F _known_hosts ping6
  complete -F _filedir_xspec timidity
  complete -F _filedir_xspec xdvi
  complete -F _filedir_xspec xfig
  complete -F _filedir_xspec xpdf
  complete -o default -F _longopt indent
  complete -o bashdefault -o default -F _fzf_path_completion chmod
  complete -o nospace -F _user_at_host finger
  complete -o bashdefault -o default -o nospace -F _python_argcomplete pipx
  complete -F _ktutil ktutil
  complete -F _xz xz
  complete -F _filedir_xspec oobase
  complete -F _docker dpa
  complete -F _fzf_path_completion perl
  complete -F _root_command kdesudo
  complete -F _docker drmi
  complete -F _filedir_xspec ogg123
  complete -F _filedir_xspec lzgrep
  complete -u w
  complete -F _filedir_xspec ee
  complete -F _sh sh
  complete -o default -F __start_kubectl kubecolor
  complete -F _docker dps
  complete -F _filedir_xspec gharbour
  complete -u su
  complete -o default -F _complete_grape grape
  complete -o default -F _longopt irb
  complete -F _known_hosts host
  complete -o default -F __start_kubectl k
  complete -o bashdefault -o default -F _fzf_path_completion ex
  complete -o default -F _complete_groovyConsole groovyConsole
  complete -o nospace -F __gdbus gdbus
  complete -F _sysctl sysctl
  complete -F _sqlite3 sqlite3
  complete -o default -F _iconv iconv
  complete -F _command tsocks
  complete -F _docker d
  complete -F _xmllint xmllint
  complete -o default -F _fzf_path_completion diff
  complete -F _ldapwhoami ldapwhoami
  complete -F _bzip2 pbzip2
  complete -F _postmap postmap
  complete -o bashdefault -o filenames -F _pandoc pandoc
  complete -F _filedir_xspec bzcat
  complete -F _filedir_xspec unlzma
  complete -F _filedir_xspec dragon
  complete -F _xzdec xzdec
  complete -o default -F _longopt shar
  complete -F _filedir_xspec ooimpress
  complete -F _cpio cpio
  complete -F _filedir_xspec xanim
  complete -o default -F _complete_groovysh groovysh.bash
  complete -o default -F _ansible-vault ansible-vault
  complete -j -P '"%' -S '"' disown
  complete -F _filedir_xspec xine
  complete -o default -F _longopt bash
  complete -o default -F _longopt md5sum
  complete -o bashdefault -o default -F _fzf_path_completion source
  complete -F _filedir_xspec amaya
  complete -F _filedir_xspec gv
  complete -F _make make
  complete -o default -F _fzf_path_completion curl
  complete -A stopped -P '"%' -S '"' bg
  complete -o default -F __start_kubectl kubeproxy
  complete -F _filedir_xspec kid3
  complete -o nospace -F __gresource gresource
  complete -F _filedir_xspec lilypond
  complete -o default -F _longopt bc
  complete -F _identify identify
  complete -F _filedir_xspec modplug123
  complete -o default -F __start_kubectl k4
  complete -F _pack200 pack200
  complete -A binding bind
  complete -o default -F _setquota setquota
  complete -b builtin
  complete -F _unpack200 unpack200
  complete -o default -F _quotacheck quotacheck
  complete -F _filedir_xspec pbzcat
  complete -F _known_hosts tracepath6
  complete -o default -F _complete_groovyc groovyc
  complete -o default -F _longopt shasum
  complete -F _command ltrace
  complete -o default -F __start_kubectl k3
  complete -F _fzf_path_completion gcc
  complete -F __app gapplication
  complete -o bashdefault -o default -F _fzf_path_completion xdg-open
  complete -o default -F _ansible-playbook ansible-playbook
  complete -u write
  complete -F _known_hosts traceroute6
  complete -F _fzf_path_completion jar
  complete -o default -F _longopt date
  complete -F _gcc gcj
  complete -F _filedir_xspec rgview
  complete -o default -F _fzf_path_completion cat
  complete -o default -F _fzf_path_completion awk
  complete -o default -F _complete_groovyConsole groovyConsole.sh
  complete -o default -F _longopt sha512sum
  complete -F _filedir_xspec unxz
  complete -o default -F _longopt seq
  complete -o default -F _longopt mkdir
  complete -F _filedir_xspec rvim
  complete -o default -F __start_kubectl krn
  complete -o default -F _longopt sha224sum
  complete -A helptopic help
  complete -F _fzf_path_completion sftp
  complete -A setopt set
  complete -o default -F __start_kubectl krc
  complete -F _compare compare
  complete -F _tmux tmux
  complete -F _ssh_copy_id ssh-copy-id
  complete -o default -F _fzf_path_completion sort
  complete -o default -F _longopt pr
  complete -o default -F _longopt colordiff
  complete -o default -F _fzf_path_completion patch
  complete -F _fzf_path_completion g++
  complete -o bashdefault -o default -F _fzf_path_completion python
  complete -F _conjure conjure
  complete -F _ldappasswd ldappasswd
  complete -F _filedir_xspec playmidi
  complete -o default -F __start_kubectl kcEvicted
  complete -o default -F _openssl openssl
  complete -o default -F _longopt fmt
  complete -o default -F _fzf_path_completion sed
  complete -F _tcpdump tcpdump
  complete -F _javadoc javadoc
  complete -F _filedir_xspec lzcat
  complete -o default -F _longopt gperf
  complete -F _command time
  complete -F _filedir_xspec zcat
  complete -F _mogrify mogrify
  complete -F _display display
  complete -F _root_command fakeroot
  complete -o default -F _complete_groovy groovy.bash
  complete -F _filedir_xspec lynx
  complete -u slay
  complete -F _filedir_xspec uncompress
  complete -F _autoreconf autoreconf
  complete -F _filedir_xspec xzcat
  complete -o default -F _fzf_dir_completion rmdir
  complete -F _filedir_xspec slitex
  complete -o bashdefault -o default -F _fzf_opts_completion vim
  complete -F _filedir_xspec aaxine
  complete -F _filedir_xspec advi
  complete -o bashdefault -o default -F _fzf_path_completion more
  complete -o default -F _longopt units
  complete -F _docker dcleanfull
  complete -o default -F _longopt touch
  complete -F _filedir_xspec lzmore
  complete -F _command then
  complete -F _command command
  complete -F _docker dkill
  complete -o default -F __start_kubectl kd
  complete -F _known_hosts fping6
  complete -u runuser
  complete -F _filedir_xspec dvipdf
  complete -o default -F __start_kubectl kc
  complete -F _gradle gradle.bat
  ```

#### osx

> \[!NOTE|label:references:]
>
> * [Bash Completion](https://sourabhbajaj.com/mac-setup/BashCompletion/)
> * [How to Enable Bash Completion on macOS](https://tecadmin.net/enable-bash-completion-on-macos/)
> * [iMarslo: brew bash-completion@2](https://imarslo.gitbook.io/book/osx#bash-completion2)

```bash
$ brew install bash-completion
# or
$ brew install bash-completion@2

# -- add to bash_profile --
$ echo '[[ -r "$(brew --prefix)/etc/profile.d/bash_completion.sh" ]] && source "$(brew --prefix)/etc/profile.d/bash_completion.sh"' >> ~/.bash_profile
$ echo 'command -v brew >/dev/null && source "$(brew --prefix git)"/etc/bash_completion.d/git-*.sh || source "$(brew --prefix git)"/etc/bash_completion.d/git-prompt.sh' >> ~/.bash_profile
# or
$ echo "[ -f /usr/local/etc/bash_completion  ] && . /usr/local/etc/bash_completion" >> ~/.bash_profile
$ cat ~/.bash_profile
[ -f /usr/local/etc/bash_completion ] && . /usr/local/etc/bash_completion
```

* to check link of bash-completion

  ```bash
  $ brew unlink bash-completion --dry-run
  Would remove:
  /usr/local/etc/bash_completion
  /usr/local/etc/bash_completion.d/abook
  /usr/local/etc/bash_completion.d/ant
  ...
  ```
* add more completion files

  ```bash
  $ fd --gen-completions bash | sudo tee $(brew --prefix)/etc/bash_completion.d/fd
  ```
* more

  ```bash
  $ brew search completion
  ==> Formulae
  apm-bash-completion       docker-completion         open-completion           stormssh-completion
  bash-completion ✔         fabric-completion         packer-completion         t-completion
  bash-completion@2         gem-completion            pip-completion            tmuxinator-completion
  boom-completion           gradle-completion ✔       rails-completion          vagrant-completion
  brew-cask-completion ✔    grunt-completion          rake-completion           wp-cli-completion
  bundler-completion        kitchen-completion        ruby-completion           yarn-completion
  cap-completion            launchctl-completion      rustc-completion          zsh-completions
  conda-zsh-completion      maven-completion          sonar-completion
  django-completion         mix-completion            spring-completion

  ==> Casks
  compositor
  ```

#### linux

* enable

  ```bash
  if ! shopt -oq posix; then
    if [ -f /usr/share/bash-completion/bash_completion ]; then
      . /usr/share/bash-completion/bash_completion
    elif [ -f /etc/bash_completion ]; then
      . /etc/bash_completion
    fi
  fi
  ```
* add more completion files

  ```bash
  $ fd --gen-completions bash | sudo tee /usr/share/bash-completion/completions/fd
  ```

  * centos

    ```bash
    $ fd --gen-completions bash | sudo tee /etc/bash_completion.d/fd
    ```

#### completion for alias

> \[!TIP|label:rerefences:]
>
> * [\* iMarslo: complete-alias](https://imarslo.gitbook.io/book/virtualization/kubernetes#completealias)
> * [\_complete\_alias](https://unix.stackexchange.com/a/332522/29178) | [cykerway/complete-alias](https://github.com/cykerway/complete-alias)
> * [How to work with `complete -F _start_kubectl` when alias contains `--kubeconfig`?](https://stackoverflow.com/q/78259041/2940319) | [How do I get bash completion for command aliases?](https://unix.stackexchange.com/a/332522/29178)

* download/install

  ```bash
  # download bash_completion.sh for kubectl
  $ curl -fsSL https://github.com/cykerway/complete-alias/raw/master/complete_alias -o ~/.bash_completion.sh
  # or rhel/centos
  $ sudo curl -fsSL https://github.com/marslo/dotfiles/raw/main/.marslo/.completion/complete_alias -o /etc/profile.d/complete_alias.sh
  # or osx
  $ sudo curl -fsSL https://github.com/marslo/dotfiles/raw/main/.marslo/.completion/complete_alias -o $(brew --prefix)/etc/bash_completion.d/complete_alias

  $ sudo chmod +x !$
  ```
* setup for specific alias

  ```bash
  $ echo "complete -F _complete_alias <alias>" >> ~/.bash_profile
  ```

  * example

    ```bash
    # i.e.: for kubec* ( kubectl or kubecolor )
    $ complete -o nosort -o bashdefault -o default -F _complete_alias $(alias | sed -rn 's/^alias ([^=]+)=.+kubec.+$/\1/p' | xargs)

    # or
    $ alias k=kubectl
    $ echo "complete -F _complete_alias k" >> ~/.bash_profile
    ```

#### troubleshooting

* [`-bash: _compopt_o_filenames: command not found`](https://imarslo.gitbook.io/book/basic/devenv#bash-compoptofilenames-command-not-found)
* [`-bash: [: too many arguments`](https://imarslo.gitbook.io/book/basic/devenv#bash--too-many-arguments)
* `$ ssh bash_completion: _comp_compgen_known_hosts__impl: -F: an empty filename is specified`

  > \[!NOTE|label:references:]
  >
  > * [Problem with ssh and bash-completion](https://bbs.archlinux.org/viewtopic.php?pid=858200#p858200)
  > * [Autocomplete server names for SSH and SCP](https://unix.stackexchange.com/a/181603/29178)
  > * [`compgen -A hostname`](https://github.com/scop/bash-completion/blob/main/bash_completion#L2470) | [`__fzf_list_hosts`](https://github.com/junegunn/fzf/blob/master/shell/completion.bash#L436)

  * clear completion

    ```bash
    $ complete -r ssh
    ```
  * or add into `/usr/local/etc/bash_completion.d/ssh` or `$(brew --prefix)/etc/bash_completion.d/ssh`

    ```bash
    # https://unix.stackexchange.com/a/181603/29178
    _ssh_hosts()
    {
        local cur prev opts
        COMPREPLY=()
        cur="${COMP_WORDS[COMP_CWORD]}"
        prev="${COMP_WORDS[COMP_CWORD-1]}"
        opts=$(command grep '^Host' ~/.ssh/config ~/.ssh/config.d/* 2>/dev/null | command grep -v '[?*]' | cut -d ' ' -f 2-)

        COMPREPLY=( $(compgen -W "$opts" -- ${cur}) )
        return 0
    }

    _ssh()
    {
        local cur prev configfile
        local -a config
        # configfile="$HOME/.ssh/config"

        COMPREPLY=()
        _get_comp_words_by_ref -n : cur prev
        #cur=`_get_cword :`
        #prev=`_get_pword`

        _ssh_suboption_check && return 0

        case $prev in
            -F|-i|-S)
                _filedir
                return 0
                ;;
            -c)
                _ssh_ciphers
                return 0
                ;;
            -m)
                _ssh_macs
                return 0
                ;;
            -l)
                COMPREPLY=( $( compgen -u -- "$cur" ) )
                return 0
                ;;
            -o)
                _ssh_options
                return 0
                ;;
            -w)
                _available_interfaces
                return 0
                ;;
            -b)
                _ssh_bindaddress
                return 0
                ;;
        esac

        if [[ "$cur" == -F* ]]; then
            cur=${cur#-F}
            _filedir
            # Prefix completions with '-F'
            COMPREPLY=( "${COMPREPLY[@]/#/-F}" )
            cur=-F$cur  # Restore cur
        elif [[ "$cur" == -* ]]; then
            COMPREPLY=( $( compgen -W '-1 -2 -4 -6 -A -a -C -f -g -K -k -M \
                -N -n -q -s -T -t -V -v -X -v -Y -y -b -b -c -D -e -F \
                -i -L -l -m -O -o -p -R -S -w' -- "$cur" ) )
        else
            # Search COMP_WORDS for '-F configfile' or '-Fconfigfile' argument
            set -- "${COMP_WORDS[@]}"
            while [ $# -gt 0 ]; do
                if [ "${1:0:2}" = -F ]; then
                    if [ ${#1} -gt 2 ]; then
                        configfile="$(dequote "${1:2}")"
                    else
                        shift
                        [ "$1" ] && configfile="$(dequote "$1")"
                    fi
                    break
                fi
                shift
            done
            # marslo >> disable _known_hosts_real from $configfile
            # marslo >> using self-defined _ssh_hosts function
            # _known_hosts_real -a -F "$configfile" "$cur"
            _ssh_hosts
            if [ $COMP_CWORD -ne 1 ]; then
                _compopt_o_filenames
                COMPREPLY=( "${COMPREPLY[@]}" $( compgen -c -- "$cur" ) )
            fi
        fi

        return 0
    }
    shopt -u hostcomplete && complete -F _ssh ssh slogin autossh

    $ complete -F _ssh ssh
    ```

### tricky

#### alias for sudo

> \[!TIP|label:references:]
>
> * [Aliases not available when using sudo](https://askubuntu.com/a/22043/92979)
> * [6.6 Aliases](https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Aliases)

```bash
alias sudo='sudo '
```

#### get md5sum

* [get file in tar without exacting](https://unix.stackexchange.com/a/208509/29178)

  ```bash
  $ tar -O -xf file.tar.gz file.txt | md5sum

  # or
  $ tar xfO file.tar.gz file.txt | md5sum
  ```
* [get file in zip without extracting](https://stackoverflow.com/a/68703145/2940319)

  ```bash
  $ unzip -p file.zip file.txt | md5sum
  ```

#### env

* HISTTIMEFORMAT

  ```bash
  # https://www.commandlinefu.com/commands/view/3642/save-date-and-time-for-each-command-in-history
  export HISTTIMEFORMAT="%h/%d-%H:%M:%S "

  # or: YYYY-MM-DD HH:MM:SS : https://www.commandlinefu.com/commands/view/3646/save-date-and-time-for-each-command-in-history
  $ export HISTTIMEFORMAT='%F %T '
  ```
* [GREP\_OPTIONS](https://www.commandlinefu.com/commands/view/6947/most-commonly-used-grep-options)

  ```bash
  $ GREP_OPTIONS='-D skip --binary-files=without-match --ignore-case'
  ```

#### shortcuts

* [inserts the results of an autocompletion in the command line](https://www.commandlinefu.com/commands/view/2424/inserts-the-results-of-an-autocompletion-in-the-command-line)

  ```bash
  $ <command> <kbd>esc</kbd> <kbd>*</kbd>
  ```

  * i.e.: `$ echo |` -> <kbd>esc</kbd> <kbd>\*</kbd>

#### man

* [linux file system](https://www.commandlinefu.com/commands/view/4671/show-file-system-hierarchy)

  ```bash
  $ man hier
  ```
* show ascii

  ```bash
  $ man ascii
  ```
* [ASCII code of key](https://www.commandlinefu.com/commands/view/10109/quick-access-to-ascii-code-of-a-key)

  ```bash
  $ showkey -a
  ```

#### [debugging a script](https://web.archive.org/web/20230401201759/https://wiki.bash-hackers.org/scripting/debuggingtips#making_xtrace_more_useful)

* [making xtrace more useful](https://web.archive.org/web/20230401201759/https://wiki.bash-hackers.org/scripting/debuggingtips#making_xtrace_more_useful)

  > \[!TIP]
  >
  > * [`PS4` has to be start with `+`](https://gist.github.com/marslo/8e25d3250c13ecd5147787d38a5b85f8) | [and](https://stackoverflow.com/a/17805088/2940319)
  > * [5.2 Bash Variables - PS4](https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-PS4)
  >
  >   PS4\
  >   The value of this parameter is expanded like PS1 and the expanded value is the prompt printed before the command line is echoed when the -x option is set (see The Set Builtin). The first character of the expanded value is replicated multiple times, as necessary, to indicate multiple levels of indirection. The default is ‘+ ’.

  ```bash
  $ export PS4='+\033[37;2;3m(${BASH_SOURCE}:${LINENO})\033[0m: \033[35;2;3m${FUNCNAME[0]:+${FUNCNAME[0]}():}\033[0m '
  # or
  $ export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'

  # even more:
  $ export PS4='Line $LINENO @ $(date +%s.%N): '
  ```
* [LINENO and BASH\_LINENO](https://stackoverflow.com/a/17804850/2940319)

  > \[!NOTE|label:references:]
  >
  > * [Bash Variables](https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html)**BASH\_LINENO**\
  >   An array variable whose members are the line numbers in source files where each corresponding member of `FUNCNAME` was invoked. `${BASH_LINENO[$i]}` is the line number in the source file (`${BASH_SOURCE[$i+1]}`) where `${FUNCNAME[$i]}` was called (or `${BASH_LINENO[$i-1]}` if referenced within another shell function).\
  >   Use `LINENO` to obtain the current line number.
  >
  >   **FUNCNAME**\
  >   An array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element (the one with the highest index) is "main". This variable exists only when a shell function is executing. Assignments to `FUNCNAME` have no effect.\
  >   If `FUNCNAME` is unset, it loses its special properties, even if it is subsequently reset.
  >
  >   This variable can be used with `BASH_LINENO` and `BASH_SOURCE`. Each element of `FUNCNAME` has corresponding elements in `BASH_LINENO` and `BASH_SOURCE` to describe the call stack. For instance, `${FUNCNAME[$i]}` was called from the file `${BASH_SOURCE[$i+1]}` at line number `${BASH_LINENO[$i]}`.\
  >   The `caller` builtin displays the current call stack using this information.

  ```bash
  #!/usr/bin/env bash

  function log() {
    echo "LINENO: ${LINENO}"
    echo "BASH_LINENO: ${BASH_LINENO[*]}; LINENO: ${LINENO}"
  }

  function foo() { log "$@"; }

  foo "$@"
  ```

  ```bash
  # result
  $ bash -x debug.sh
  +(debug.sh:12):  foo
  +(debug.sh:9): foo(): log
  +(debug.sh:4): log(): echo 'LINENO: 4'
  LINENO: 4
  +(debug.sh:5): log(): echo 'BASH_LINENO: 9 12 0; LINENO: 5'
  BASH_LINENO: 9 12 0; LINENO: 5
  ```
