devops

  • fuzzy completion in bash

    • $ cat **<tab>

    • $ unset **<tab>

    • $ unalias **<tab>

    • $ export **<tab>

    • $ ssh **<tab>

    • $ kill -9 **<tab>

[!NOTE|label:references:]

install

[!NOTE|label:references:]

$ brew install fzf fd
$ ln -sf $(brew --prefix fd)/share/bash-completion/completions/fd /usr/local/etc/bash_completion.d/fd
# debine
$ sudo apt install fd

$ FZF_DEFAULT_OPTS="--height 35%"
$ FZF_DEFAULT_OPTS+=" --layout=reverse"
$ FZF_DEFAULT_OPTS+=" --pointer='→' --marker='» ' --prompt='$ '"
$ FZF_DEFAULT_OPTS+=" --multi"
$ FZF_DEFAULT_OPTS+=" --inline-info"
$ FZF_DEFAULT_OPTS+=" --color=spinner:#e6db74,hl:#928374,fg:#ebdbb2,header:#928374,info:#504945,pointer:#98971a,marker:#d79921,fg+:#ebdbb2,prompt:#404945,hl+:#fb4934"
$ FZF_DEFAULT_COMMAND="fd --type f"
$ FZF_DEFAULT_COMMAND+=" --strip-cwd-prefix"
$ FZF_DEFAULT_COMMAND+=" --hidden"
$ FZF_DEFAULT_COMMAND+=" --follow"
$ FZF_DEFAULT_COMMAND+=" --exclude .git --exclude node_modules"
$ export FZF_DEFAULT_OPTS FZF_DEFAULT_COMMAND
  • install from source code for wsl

    [!NOTE|label:this solution for install latest fzf in wsl]

    $ git clone git@github.com:junegunn/fzf.git
    $ bash -x install --all
    $ sudo cp bin/fzf* /usr/local/bin/
  • offline install

    [!NOTE]

    • curl: (60) SSL certificate problem: self-signed certificate in certificate chain:

      $ curl -fL https://github.com/junegunn/fzf/releases/download/0.44.1/fzf-0.44.1-linux_amd64.tar.gz
      curl: (60) SSL certificate problem: self-signed certificate in certificate chain
      More details here: https://curl.se/docs/sslcerts.html
      
      curl failed to verify the legitimacy of the server and therefore could not
      establish a secure connection to it. To learn more about this situation and
      how to fix it, please visit the web page mentioned above.
    • ~/.curlrc

      $ echo '--insecure' >> ~/.curlrc
    • offline installation

      ################ for offline installation only ################
      # check current version for offline installation
      $ uname -sm
      Linux x86_64
      
      # download correct package according https://github.com/junegunn/fzf/blob/master/install#L170
      # i.e.: Linux x86_64 -> fzf-$version-linux_amd64.tar.gz
      $ cp fzf-0.42.0-linux_amd64.tar.gz /tmp/fzf.tar.gz
      
      # modify install script `try_curl` function to not download but use local tar.gz directly
      $ cat << 'EOF' | git apply --inaccurate-eof --ignore-whitespace
      diff --git a/install b/install
      index 5ac191b..342bc49 100755
      --- a/install
      +++ b/install
      @@ -115,10 +115,8 @@ link_fzf_in_path() {
      try_curl() {
       command -v curl > /dev/null &&
       if [[ $1 =~ tar.gz$ ]]; then
      -    curl -fL $1 | tar -xzf -
      -  else
      -    local temp=${TMPDIR:-/tmp}/fzf.zip
      -    curl -fLo "$temp" $1 && unzip -o "$temp" && rm -f "$temp"
      +    local temp=${TMPDIR:-/tmp}/fzf.tar.gz
      +    tar -xzf "$temp" && rm -rf "$temp"
       fi
      }
      EOF
      
      ### or modify manually ###
      # try_curl() {
      #   command -v curl > /dev/null &&
      #   if [[ $1 =~ tar.gz$ ]]; then
      #     local temp=${TMPDIR:-/tmp}/fzf.tar.gz
      #     tar -xzf "$temp" && rm -rf "$temp"
      #   fi
      # }
      ################ for offline installation only ################

shortcuts

[!NOTE|label:references:]

KEYSTROKEBASH FUNCTIONZSH FUNCTIONENVIRONMENT VARIABLE

CTRL-t

__fzf_select__

__fsel

FZF_CTRL_T_COMMAND

CTRL-r

__fzf_history__

fzf-history-widget

FZF_CTRL_R_OPTS

ALT-c

__fzf_cd__

fzf-cd-widget

FZF_ALT_C_COMMAND

action and select

OPENSELECT ALLDESELECT ALLTOGGLE ALLMULTIPLE SELECT

ctrl + s

ctrl + d

ctrl + t

shift + ⇥

movement

PREVIOUSNEXTMULTIPLE SELECT

ctrl + k

ctrl + j

ctrl + p

ctrl + n

shift + ⇥

ctrl + k

ctrl + j

ctrl-t

[!TIP]

  • file list:

    PREVIOUSNEXTCHANGE PREVIEW WINDOWS

    ctrl + /

    ctrl + k

    ctrl + j

    ctrl + p

    ctrl + n

  • preview content:

    PREVIOUSNEXT

    shift + ↑

    shift + ↓

    ctrl + ↑

    ctrl + ↓

    ctrl + p

    ctrl + n

usage

[!NOTE|label:references:]

magic vim

[!TIP]

# magic vim - fzf list in recent modified order
# @author      : marslo
# @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
# @description :
#   - if `nvim` installed using `nvim` instead of `vim`
#     - using `-v` to force using `vim` instead of `nvim` even if nvim installed
#   - if `vim` commands without paramters, then call fzf and using vim to open selected file
#   - if `vim` commands with paramters
#       - if single paramters and parameters is directlry, then call fzf in target directory and using vim to open selected file
#       - otherwise call regular vim to open file(s)
#   - to respect fzf options by: `type -t _fzf_opts_completion >/dev/null 2>&1 && complete -F _fzf_opts_completion -o bashdefault -o default vim`
# shellcheck disable=SC2155
function vim() {                           # magic vim - fzf list in most recent modified order
  local voption
  local target
  local orgv                               # force using vim instead of nvim
  local VIM="$(type -P vim)"
  local foption='--multi --cycle '
  local fdOpt="--type f --hidden --follow --unrestricted --ignore-file $HOME/.fdignore --exclude Music"
  [[ "$(pwd)" = "$HOME" ]] && fdOpt+=' --max-depth 3'
  if ! uname -r | grep -q "Microsoft"; then fdOpt+=' --exec-batch ls -t'; fi

  while [[ $# -gt 0 ]]; do
    case "$1" in
                 -v ) orgv=1            ; shift   ;;
        -h | --help ) voption+="$1 "    ; shift   ;;
          --version ) voption+="$1 "    ; shift   ;;
                 -c ) voption+="$1 $2"  ; shift   ;;
      --startuptime ) voption+="$1 $2 " ; shift 2 ;;
                -Nu ) voption+="$1 $2 " ; shift 2 ;;
              --cmd ) voption+="$1 $2 " ; shift 2 ;;
                 -* ) foption+="$1 $2 " ; shift 2 ;;
                  * ) break                       ;;
    esac
  done

  [[ 1 -ne "${orgv}" ]] && command -v nvim >/dev/null && VIM="$(type -P nvim)"

  if [[ 0 -eq $# ]] && [[ -z "${voption}" ]]; then
    fd . ${fdOpt} | fzf ${foption} --bind="enter:become(${VIM} {+})"
  elif [[ 1 -eq $# ]] && [[ -d $1 ]]; then
    [[ '.' = "${1}" ]] && target="${1}" || target=". ${1}"
    fd ${target} ${fdOpt} | fzf ${foption} --bind="enter:become(${VIM} {+})"
  else
    # shellcheck disable=SC2068
    "${VIM}" ${voption} $@
  fi
}

# v - open files in ~/.vim_mru_files       # https://github.com/junegunn/fzf/wiki/Examples#v
# @author      : marslo
# @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
# @description : list 10 most recently used files via fzf, and open by regular vim
function v() {                             # v - open files in ~/.vim_mru_files
  local files
  files=$( grep --color=none -v '^#' ~/.vim_mru_files |
           while read -r line; do [ -f "${line/\~/$HOME}" ] && echo "$line"; done |
           fzf-tmux -d -m -q "$*" -1
         ) &&
  vim ${files//\~/$HOME}
}

# vimr - open files by [vim] in whole [r]epository
#        same series: [`cdr`](https://github.com/marslo/dotfiles/blob/main/.marslo/bin/ffunc.sh#L310-L318)
#        similar with [`:Gfiles`](https://github.com/junegunn/fzf.vim?tab=readme-ov-file#commands)
# @author      : marslo
# @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
# @description :
#   - if pwd inside the repo, then filter all files within current git repository via data modified and open by vim
#   - if pwd not inside the repo, then call `vim`
function vimr() {                          # vimr - open file(s) via [vim] in whole [r]epository
  local repodir
  isrepo=$(git rev-parse --is-inside-work-tree >/dev/null 2>&1; echo $?)
  if [[ 0 = "${isrepo}" ]]; then
    repodir="$(git rev-parse --show-toplevel)"
    # shellcheck disable=SC2164
    files=$( fd . "${repodir}" --type f --hidden --ignore-file ~/.fdignore --exec-batch ls -t |
                  xargs -I{} bash -c "echo {} | sed \"s|${repodir}/||g\"" |
                  fzf --multi -0 |
                  xargs -I{} bash -c "echo ${repodir}/{}"
           )
    # shellcheck disable=SC2046
    [[ -z "${files}" ]] || vim $(xargs <<< "${files}")
  else
    vim
  fi
}

# vimrc - open rc files list from "${rcPaths}" to quick update/modify rc files
# @author      : marslo
# @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
# @description :
#   - default rcPaths: ~/.marslo ~/.config/nvim ~/.*rc ~/.*profile ~/.*ignore
#   - using nvim if `command -v nvim` is true
#   - using `-v` force using `command vim` instead of `command nvim`
# shellcheck disable=SC2155
function vimrc() {                         # vimrc - fzf list all rc files in data modified order
  local orgv                               # force using vim instead of nvim
  local rcPaths="$HOME/.config/nvim $HOME/.marslo"
  local VIM="$(type -P vim)"
  local foption='--multi --cycle '
  local fdOpt="--type f --hidden --follow --unrestricted --ignore-file $HOME/.fdignore"
  if ! uname -r | grep -q "Microsoft"; then fdOpt+=' --exec-batch ls -t'; fi

  while [[ $# -gt 0 ]]; do
    case "$1" in
      -v ) orgv=1 ; shift ;;
       * ) break          ;;
    esac
  done

  [[ 1 -ne "${orgv}" ]] && command -v nvim >/dev/null && VIM="$(type -P nvim)"
  fdInRC | sed -rn 's/^[^|]* \| (.+)$/\1/p' \
         | fzf ${foption} --bind="enter:become(${VIM} {+})" \
                          --bind "ctrl-y:execute-silent(echo -n {+} | ${COPY})+abort" \
                          --header 'Press CTRL-Y to copy name into clipboard'
}
  • fdInRC

    # shellcheck disable=SC2089,SC2090
    function fdInRC() {
      local rcPaths="$HOME/.config/nvim $HOME/.marslo $HOME/.idlerc $HOME/.ssh"
      local fdOpt="--type f --hidden --follow --unrestricted --ignore-file $HOME/.fdignore"
      fdOpt+=' --exec stat --printf="%y | %n\n"'
      (
        eval "fd --max-depth 1 --hidden '.*rc|.*profile|.*ignore' $HOME ${fdOpt}";
        echo "${rcPaths}" | fmt -1 | xargs -I{} bash -c "fd . {} --exclude ss/ --exclude log/ --exclude .completion/ --exclude bin/bash-completion/ ${fdOpt}" ;
      ) |  sort -r
    }

smart vimdiff

function fzfInPath() {                   # return file name via fzf in particular folder
  local fdOpt="--type f --hidden --follow --unrestricted --ignore-file $HOME/.fdignore"
  if ! uname -r | grep -q 'Microsoft'; then fdOpt+=' --exec-batch ls -t'; fi
  [[ '.' = "${1}" ]] && path="${1}" || path=". ${1}"
  eval "fd ${path} ${fdOpt} | fzf --multi --cycle ${*:2} --header 'filter in ${1} :'"
}

# magic vimdiff - using fzf list in recent modified order
# @author      : marslo
# @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
# @description :
#   - if any of paramters is directory, then get file path via fzf in target path first
#   - if `vimdiff` commands without parameter , then compare files in `.` and `~/.marslo`
#   - if `vimdiff` commands with 1  parameter , then compare files in current path and `$1`
#   - if `vimdiff` commands with 2  parameters, then compare files in `$1` and `$2`
#   - otherwise ( if more than 2 parameters )  , then compare files in `${*: -2:1}` and `${*: -1}` with paramters of `${*: 1:$#-2}`
#   - to respect fzf options by: `type -t _fzf_opts_completion >/dev/null 2>&1 && complete -F _fzf_opts_completion -o bashdefault -o default vimdiff`
function vimdiff() {                       # smart vimdiff
  local lFile
  local rFile
  local option
  local var

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --help ) option+="$1 "   ; shift   ;;
          -* ) option+="$1 $2 "; shift 2 ;;
           * ) break                     ;;
    esac
  done

  if [[ 0 -eq $# ]]; then
    lFile=$(fzfInPath '.' "${option}")
    # shellcheck disable=SC2154
    rFile=$(fzfInPath "${iRCHOME}" "${option}")
  elif [[ 1 -eq $# ]]; then
    lFile=$(fzfInPath '.' "${option}")
    [[ -d "$1" ]] && rFile=$(fzfInPath "$1" "${option}") || rFile="$1"
  elif [[ 2 -eq $# ]]; then
    [[ -d "$1" ]] && lFile=$(fzfInPath "$1" "${option}") || lFile="$1"
    [[ -d "$2" ]] && rFile=$(fzfInPath "$2" "${option}") || rFile="$2"
  else
    var="${*: 1:$#-2}"
    [[ -d "${*: -2:1}" ]] && lFile=$(fzfInPath "${*: -2:1}") || lFile="${*: -2:1}"
    [[ -d "${*: -1}"   ]] && rFile=$(fzfInPath "${*: -1}")   || rFile="${*: -1}"
  fi

  [[ -f "${lFile}" ]] && [[ -f "${rFile}" ]] && $(type -P vim) -d ${var} "${lFile}" "${rFile}"
}

# vd - open vimdiff loaded files from ~/.vim_mru_files
# @author      : marslo
# @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
# @description : list 10 most recently used files via fzf, and open by vimdiff
#   - if `vd` commands without parameter, list 10 most recently used files via fzf, and open selected files by vimdiff
#   - if `vd` commands with `-q` ( [q]uiet ) parameter, list 10 most recently used files via fzf and automatic select top 2, and open selected files by vimdiff
function vd() {                            # vd - open vimdiff loaded files from ~/.vim_mru_files
  [[ 1 -eq $# ]] && [[ '-q' = "$1" ]] && opt='--bind start:select+down+select+accept' || opt=''
  # shellcheck disable=SC2046
  files=$( grep --color=none -v '^#' ~/.vim_mru_files |
           xargs -d'\n' -I_ bash -c "sed 's:\~:$HOME:' <<< _" |
           fzf --multi 3 --sync --cycle --reverse ${opt}
         ) &&
  vimdiff $(xargs <<< "${files}")
}

smart cat

# smart cat - using bat by default for cat content, respect bat options
# @author      : marslo
# @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
# @description :
#   - using `bat` by default if `command -v bat`
#     - using `-c` ( `c`at ) as 1st parameter, to force using `type -P cat` instead of `type -P bat`
#   - if `bat` without  paramter, then search file via `fzf` and shows via `bat`
#   - if `bat` with 1   paramter, and `$1` is directory, then search file via `fzf` from `$1` and shows via `bat`
#   - otherwise respect `bat` options, and shows via `bat`
# shellcheck disable=SC2046,SC2155
function cat() {                           # smart cat
  local fdOpt='--type f --hidden --follow --exclude .git --exclude node_modules'
  local CAT="$(type -P cat)"
  if ! uname -r | grep -q "Microsoft"; then fdOpt+=' --exec-batch ls -t'; fi
  command -v nvim >/dev/null && CAT="$(type -P bat)"

  if [[ 0 -eq $# ]]; then
    "${CAT}" --theme='gruvbox-dark' $(fd . ${fdOpt} | fzf --multi --cycle --exit-1)
  elif [[ '-c' = "$1" ]]; then
    $(type -P cat) "${@:2}"
  elif [[ 1 -eq $# ]] && [[ -d $1 ]]; then
    local target=$1;
    fd . "${target}" ${fdOpt} |
      fzf --multi --cycle --exit-1 --bind="enter:become(${CAT} --theme='gruvbox-dark' {+})" ;
  else
    "${CAT}" --theme='gruvbox-dark' "${@:1:$#-1}" "${@: -1}"
  fi
}

smart copy

# smart copy - using `fzf` to list files and copy the selected file
# @author      : marslo
# @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
# @description :
#   - if `copy` without parameter, then list file via `fzf` and copy the content
#     - "${COPY}"
#       - `pbcopy` in osx
#       - `/mnt/c/Windows/System32/clip.exe` in wsl
#   - otherwise copy the content of parameter `$1` via `pbcopy` or `clip.exe`
# shellcheck disable=SC2317
function copy() {                          # smart copy
  local fdOpt='--type f --hidden --follow --exclude .git --exclude node_modules'
  [[ -z "${COPY}" ]] && echo -e "$(c Rs)ERROR: 'copy' function NOT support :$(c) $(c Ri)$(uanme -v)$(c)$(c Rs). EXIT..$(c)" && return;

  if [[ 0 -eq $# ]]; then
    file=$(fzf --cycle --exit-0) &&
      "${COPY}" < "${file}" &&
      echo -e "$(c Wd)>>$(c) $(c Gis)${file}$(c) $(c Wdi)has been copied ..$(c)"
  elif [[ 1 -eq $# ]] && [[ -d $1 ]]; then
    local target=$1;
    file=$( fd . "${target}" ${fdOpt} | fzf --cycle --exit-0 ) &&
      "${COPY}" < "${file}" &&
      echo -e "$(c Wd)>>$(c) $(c Gis)${file}$(c) $(c Wdi)has been copied ..$(c)"
  else
    "${COPY}" < "$1"
  fi
}

others

  • image view

    [!NOTE]

    # imgview - fzf list and preview images
    # @author      : marslo
    # @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
    # @description :
    #   - to respect fzf options by: `type -t _fzf_opts_completion >/dev/null 2>&1 && complete -F _fzf_opts_completion -o bashdefault -o default imgview`
    #   - disable `gif` due to imgcat performance issue
    # shellcheck disable=SC2215
    function imgview() {                       # view image via [imgcat](https://github.com/eddieantonio/imgcat)
      fd --unrestricted --type f --exclude .git --exclude node_modules '^*\.(png|jpeg|jpg|xpm|bmp)$' |
      fzf "$@" --height 100% \
               --preview "imgcat -W \$FZF_PREVIEW_COLUMNS -H \$FZF_PREVIEW_LINES {}" \
               --bind 'ctrl-y:execute-silent(echo -n {+} | pbcopy)+abort' \
               --header 'Press CTRL-Y to copy name into clipboard' \
               --preview-window 'down,80%,wrap' \
               --exit-0 \
      >/dev/null || true
    }
  • open files

    • fs

      $ function fs() { fzf --multi --bind 'enter:become(vim {+})' }
    • fe

      # fe [FUZZY PATTERN] - Open the selected file with the default editor
      #   - Bypass fuzzy finder if there's only one match (--select-1)
      #   - Exit if there's no match (--exit-0)
      fe() {
        IFS=$'\n' files=($(fzf-tmux --query="$1" --multi --select-1 --exit-0))
        [[ -n "$files" ]] && ${EDITOR:-vim} "${files[@]}"
      }
    • fo

      # Modified version where you can press
      #   - CTRL-O to open with `open` command,
      #   - CTRL-E or Enter key to open with the $EDITOR
      fo() {
        IFS=$'\n' out=("$(fzf-tmux --query="$1" --exit-0 --expect=ctrl-o,ctrl-e)")
        key=$(head -1 <<< "$out")
        file=$(head -2 <<< "$out" | tail -1)
        if [ -n "$file" ]; then
          [ "$key" = ctrl-o ] && open "$file" || ${EDITOR:-vim} "$file"
        fi
      }
  • search in files

    [!NOTE]

    function fif() {                           # [f]ind-[i]n-[f]ile
      if [ ! "$#" -gt 0 ]; then echo "Need a string to search for!"; return 1; fi
      $(type -P rg) --files-with-matches --no-messages --hidden --follow --smart-case "$1" |
      fzf --bind 'ctrl-p:preview-up,ctrl-n:preview-down' \
          --bind "enter:become($(type -P vim) {+})" \
          --header 'CTRL-N/CTRL-P or CTRL-↑/CTRL-↓ to view contents' \
          --preview "bat --color=always --style=plain {} |
                     rg --no-line-number --colors 'match:bg:yellow' --ignore-case --pretty --context 10 \"$1\" ||
                     rg --no-line-number --ignore-case --pretty --context 10 \"$1\" {} \
                    "
    }
    
    # or highlight as preview tool
    function fif() {                           # [f]ind-[i]n-[f]ile
      if [ ! "$#" -gt 0 ]; then echo "Need a string to search for!"; return 1; fi
      rg --color never --files-with-matches --no-messages "$1" |
      fzf --bind 'ctrl-p:preview-up,ctrl-n:preview-down' \
          --preview "highlight -O ansi {} 2> /dev/null |
                     rg --colors 'match:bg:yellow' --ignore-case --pretty --context 10 '$1' ||
                     rg --no-line-number --ignore-case --pretty --context 10 '$1' {} \
                    "
    }
  • changing directory

    [!NOTE|label:references:]

    • cdp

      # shellcheck disable=SC2034,SC2316
      function cdp() {                           # cdp - [c][d] to selected [p]arent directory
        local declare dirs=()
        get_parent_dirs() {
          if [[ -d "${1}" ]]; then dirs+=("$1"); else return; fi
          if [[ "${1}" == '/' ]]; then
            for _dir in "${dirs[@]}"; do echo $_dir; done
          else
            # shellcheck disable=SC2046
            get_parent_dirs $(dirname "$1")
          fi
        }
        # shellcheck disable=SC2155,SC2046
        local DIR=$(get_parent_dirs $(realpath "${1:-$PWD}") | fzf-tmux --tac)
        cd "$DIR" || return
      }
    • cdf

      function cdf() {                           # [c][d] into the directory of the selected [f]ile
        local file
        local dir
        # shellcheck disable=SC2164
        file=$(fzf +m -q "$1") && dir=$(dirname "$file") && cd "$dir"
      }
    • cdd

      function cdd() {                           # cdd - [c][d] to selected sub [d]irectory
        local dir
        # shellcheck disable=SC2164
        dir=$(fd --type d --hidden --ignore-file ~/.fdignore | fzf --no-multi -0) && cd "${dir}"
      }
    • cdr

      function cdr() {                           # cdd - [c][d] to selected [r]epo directory
        local repodir
        repodir="$(git rev-parse --show-toplevel)"
        # shellcheck disable=SC2164
        dir=$( ( echo './'; fd . "${repodir}" --type d --hidden --ignore-file ~/.fdignore |
                           xargs -I{} bash -c "echo {} | sed \"s|${repodir}/||g\""
               ) | fzf --no-multi -0
             ) && cd "${repodir}/${dir}"
      }
  • Interactive cd

    function cd() {
      if [[ "$#" != 0 ]]; then
        # shellcheck disable=SC2164
        builtin cd "$@";
        return
      fi
      while true; do
        # shellcheck disable=SC2155,SC2010
        local lsd=$(echo ".." && ls -A --color=none -p | grep --color=none '/$' | sed 's;/$;;')
        # shellcheck disable=SC2155,SC2016
        local dir="$(printf '%s\n' "${lsd[@]}" |
            fzf --reverse --preview '
                __cd_nxt="$(echo {})";
                __cd_path="$(echo $(pwd)/${__cd_nxt} | sed "s;//;/;")";
                echo $__cd_path;
                echo;
                ls -p --color=always "${__cd_path}";
        ')"
        [[ ${#dir} != 0 ]] || return 0
        # shellcheck disable=SC2164
        builtin cd "$dir" &> /dev/null
      done
    }
    • fd

      [!DANGER] conflict with fd-find

      # fd - cd to selected directory
      fd() {
        local dir
        dir=$(find ${1:-.} -path '*/\.*' -prune \
                           -o -type d -print 2> /dev/null | fzf +m) &&
        cd "$dir"
      }
      
      # Another fd - cd into the selected directory
      # This one differs from the above, by only showing the sub directories and not
      #  showing the directories within those.
      fd() {
        DIR=$(find * -maxdepth 0 -type d -print 2> /dev/null | fzf-tmux) && cd "$DIR"
      }
    • fda

      # fda - including hidden directories
      fda() {
        local dir
        dir=$(find ${1:-.} -type d 2> /dev/null | fzf +m) && cd "$dir"
      }

advanced usage

venv selector

# activate venv - using `fzf` to list and activate python venv
# @author      : marslo
# @inspired    : https://seb.jambor.dev/posts/improving-shell-workflows-with-fzf/#virtual-env
# @source      : https://github.com/marslo/dotfiles/blob/main/.marslo/bin/ffunc.sh
function avenv() {
  # shellcheck disable=SC2155
  local _venv=$(command ls --color=never "$HOME/.venv" | fzf)
  [[ -n "${_venv}" ]] &&
    source "$HOME/.venv/${_venv}/bin/activate" &&
    echo -e "$(c Wd)>>$(c) $(c Gis)${_venv}$(c) $(c Wdi)has been activated ..$(c)"
}

chrome

  • Bookmarks

    # https://github.com/junegunn/fzf/wiki/Examples#bookmarks
    # shellcheck disable=SC2016
    function b() {                             # chrome [b]ookmarks browser with jq
      if [ "$(uname)" = "Darwin" ]; then
        if [[ -e "$HOME/Library/Application Support/Google/Chrome Canary/Default/Bookmarks" ]]; then
          bookmarks_path="$HOME/Library/Application Support/Google/Chrome Canary/Default/Bookmarks"
        else
          bookmarks_path="$HOME/Library/Application Support/Google/Chrome/Default/Bookmarks"
        fi
        open=open
      else
        bookmarks_path="$HOME/.config/google-chrome/Default/Bookmarks"
        open=xdg-open
      fi
    
      jq_script='
         def ancestors: while(. | length >= 2; del(.[-1,-2]));
         . as $in | paths(.url?) as $key | $in | getpath($key) | {name,url, path: [$key[0:-2] | ancestors as $a | $in | getpath($a) | .name?] | reverse | join("/") } | .path + "/" + .name + "\t" + .url'
    
      urls=$( jq -r "${jq_script}" < "${bookmarks_path}" \
                 | sed -E $'s/(.*)\t(.*)/\\1\t\x1b[36m\\2\x1b[m/g' \
                 | fzf --ansi \
                 | cut -d$'\t' -f2
            )
      # shellcheck disable=SC2046
      [[ -z "${urls}" ]] || "${open}" $(echo "${urls}" | xargs)
    }

man page

[!NOTE]

# fman - fzf list and preview for manpage:
# @source      : https://github.com/junegunn/fzf/wiki/examples#fzf-man-pages-widget-for-zsh
# @description :
#   - CTRL-N/CTRL-P or SHIFT-↑/↓ for view preview content
#   - ENTER/Q for toggle maximize/normal preview window
#   - CTRL+O  for toggle tldr in preview window
#   - CTRL+I  for toggle man in preview window
#   - to respect fzf options by: `type -t _fzf_opts_completion >/dev/null 2>&1 && complete -F _fzf_opts_completion -o bashdefault -o default fman`
# shellcheck disable=SC2046
function fman() {
  unset MANPATH
  local option
  local batman="man {1} | col -bx | bat --language=man --plain --color always --theme='gruvbox-dark'"

  while [[ $# -gt 0 ]]; do
    case "$1" in
          -* ) option+="$1 $2 "; shift 2 ;;
           * ) break                     ;;
    esac
  done

  man -k . |
  sort -u |
  sed -r 's/(\(.+\))//g' |
  grep -v -E '::' |
  awk -v cyan=$(tput setaf 6) -v blue=$(tput setaf 4) -v res=$(tput sgr0) -v bld=$(tput bold) '{ $1=cyan bld $1; $2=res blue $2;} 1' |
  fzf ${option:-} \
      -d ' ' \
      --nth 1 \
      --height 100% \
      --ansi \
      --no-multi \
      --tiebreak=begin \
      --prompt='ᓆ > ' \
      --color='prompt:#0099BD' \
      --preview-window 'up,70%,wrap,rounded,<50(up,85%,border-bottom)' \
      --preview "${batman}" \
      --bind 'ctrl-p:preview-up,ctrl-n:preview-down' \
      --bind "ctrl-o:+change-preview(tldr --color {1})+change-prompt(ﳁ tldr > )" \
      --bind "ctrl-i:+change-preview(${batman})+change-prompt(ᓆ  man > )" \
      --bind "enter:execute(${batman})+change-preview(${batman})+change-prompt(ᓆ > )" \
      --header 'CTRL-N/P or SHIFT-↑/↓ to view preview contents; ENTER/Q to maximize/normal preview window' \
      --exit-0
}
  • simple version

    $ apropos . |
      fzf -d ') ' --nth 1 \
                  --height 100% \
                  --bind 'ctrl-p:preview-up,ctrl-n:preview-down' \
                  --header 'CTRL-N/CTRL-P or CTRL-↑/CTRL-↓ to view contents' \
                  --preview-window=up:88%:wrap \
                  --preview 'echo {} | sed -r "s/([^\(]+).*$/\1/" | xargs man' \
                  --exit-0 |
      sed -r "s/([^\(]+).*$/\1/" |
      xargs man

git alias

[!TIP]

[alias]
  ### checkout sorted [b]ranch
  bb    = "! bash -c 'branch=$(git for-each-ref refs/remotes refs/heads --sort=-committerdate --format=\"%(refname:short)\" | \n\
                      grep --color=never -v \"origin$\" | \n\
                      fzf +m --prompt=\"branch> \" | \n\
                      sed -rn \"s:\\s*(origin/)?(.*)$:\\2:p\") && \n\
                      [[ -n \"${branch}\" ]] && \n\
                      echo -e \"\\033[1;33m~~> ${branch}\\033[0m\" && \n\
                      git checkout \"${branch}\"; \n\
                     '"

  ### [b]ranch [copy]
  bcopy = "! bash -c 'branch=$(git for-each-ref refs/remotes refs/heads --sort=-committerdate --format=\"%(refname:short)\" | \n\
                      grep --color=never -v \"origin$\" | \n\
                      fzf +m --prompt=\"branch> \" | \n\
                      sed -rn \"s:\\s*(origin/)?(.*)$:\\2:p\") && \n\
                      [[ -n \"${branch}\" ]] && \n\
                      echo -e \"\\033[0;33;1m~~> branch \\033[0m\\033[0;32;3m${branch}\\033[0m \\033[0;33;1mcopied\\033[0m\" && \n\
                      pbcopy <<< \"${branch}\" \n\
                     '"

environment

  • export

    # mkexp - compilation environment variable export, support multiple select
    # @author      : marslo
    # @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
    # @description : list compilation environment variable via `fzf`, and export selected items
    #   - if paramter is [ -f | --full ], then load full tool paths
    # shellcheck disable=SC1090
    function mkexp() {                         # [m]a[k]e environment variable [e][x][p]ort
      if [[ 1 -eq $# ]] && [[ '-f' = "$1" || '--full' = "$1" ]]; then
        source ~/.marslo/.imac
      fi
    
      LDFLAGS="${LDFLAGS:-}"
      test -d "${HOMEBREW_PREFIX}"      && LDFLAGS+=" -L${HOMEBREW_PREFIX}/lib"
      test -d '/usr/local/opt/readline' && LDFLAGS+=' -L/usr/local/opt/readline/lib'
      test -d "${OPENLDAP_HOME}"        && LDFLAGS+=" -L${OPENLDAP_HOME}/lib"
      test -d "${CURL_OPENSSL_HOME}"    && LDFLAGS+=" -L${CURL_OPENSSL_HOME}/lib"
      test -d "${BINUTILS}"             && LDFLAGS+=" -L${BINUTILS}/lib"
      test -d "${PYTHON_HOME}"          && LDFLAGS+=" -L${PYTHON_HOME}/lib"
      test -d "${RUBY_HOME}"            && LDFLAGS+=" -L${RUBY_HOME}/lib"
      test -d "${TCLTK_HOME}"           && LDFLAGS+=" -L${TCLTK_HOME}/lib"
      test -d "${SQLITE_HOME}"          && LDFLAGS+=" -L${SQLITE_HOME}/lib"
      test -d "${OPENSSL_HOME}"         && LDFLAGS+=" -L${OPENSSL_HOME}/lib"
      test -d "${NODE_HOME}"            && LDFLAGS+=" -L${NODE_HOME}/lib"                       # ${NODE_HOME}/libexec/lib for node@12
      test -d "${LIBRESSL_HOME}"        && LDFLAGS+=" -L${LIBRESSL_HOME}/lib"
      test -d "${ICU4C_711}"            && LDFLAGS+=" -L${ICU4C_711}/lib"
      test -d "${EXPAT_HOME}"           && LDFLAGS+=" -L${EXPAT_HOME}/lib"
      test -d "${NCURSES_HOME}"         && LDFLAGS+=" -L${NCURSES_HOME}/lib"
      test -d "${LIBICONV_HOME}"        && LDFLAGS+=" -L${LIBICONV_HOME}/lib"
      test -d "${ZLIB_HOME}"            && LDFLAGS+=" -L${ZLIB_HOME}/lib"
      test -d "${LLVM_HOME}"            && LDFLAGS+=" -L${LLVM_HOME}/lib"
      test -d "${LLVM_HOME}"            && LDFLAGS+=" -L${LLVM_HOME}/lib/c++ -Wl,-rpath,${LLVM_HOME}/lib/c++"  # for c++
      LDFLAGS=$( echo "$LDFLAGS" | tr ' ' '\n' | uniq | sed '/^$/d' | paste -s -d' ' )
    
      CFLAGS="${CFLAGS:-}"
      CFLAGS+=" -I/usr/local/include"
      test -d "${TCLTK_HOME}" && CFLAGS+=" -I${TCLTK_HOME}/include"
      CFLAGS=$( echo "$CFLAGS" | tr ' ' '\n' | uniq | sed '/^$/d' | paste -s -d' ' )
    
      CPPFLAGS="${CPPFLAGS:-}"
      test -d "${HOMEBREW_PREFIX}"      && CPPFLAGS+=" -I${HOMEBREW_PREFIX}/include"
      test -d "${JAVA_HOME}"            && CPPFLAGS+=" -I${JAVA_HOME}/include"
      test -d "${OPENLDAP_HOME}"        && CPPFLAGS+=" -I${OPENLDAP_HOME}/include"
      test -d "${CURL_OPENSSL_HOME}"    && CPPFLAGS+=" -I${CURL_OPENSSL_HOME}/include"
      test -d "${BINUTILS}"             && CPPFLAGS+=" -I${BINUTILS}/include"
      test -d "${SQLITE_HOME}"          && CPPFLAGS+=" -I${SQLITE_HOME}/include"
      test -d '/usr/local/opt/readline' && CPPFLAGS+=' -I/usr/local/opt/readline/include'
      test -d "${OPENSSL_HOME}"         && CPPFLAGS+=" -I${OPENSSL_HOME}/include"
      test -d "${NODE_HOME}"            && CPPFLAGS+=" -I${NODE_HOME}/include"
      test -d "${LIBRESSL_HOME}"        && CPPFLAGS+=" -I${LIBRESSL_HOME}/include"
      test -d "${TCLTK_HOME}"           && CPPFLAGS+=" -I${TCLTK_HOME}/include"
      test -d "${RUBY_HOME}"            && CPPFLAGS+=" -I${RUBY_HOME}/include"
      test -d "${ICU4C_711}"            && CPPFLAGS+=" -I${ICU4C_711}/include"
      test -d "${LLVM_HOME}"            && CPPFLAGS+=" -I${LLVM_HOME}/include"
      test -d "${LIBICONV_HOME}"        && CPPFLAGS+=" -I${LIBICONV_HOME}/include"
      test -d "${EXPAT_HOME}"           && CPPFLAGS+=" -I${EXPAT_HOME}/include"
      test -d "${NCURSES_HOME}"         && CPPFLAGS+=" -I${NCURSES_HOME}/include"
      test -d "${ZLIB_HOME}"            && CPPFLAGS+=" -I${ZLIB_HOME}/include"
      CPPFLAGS=$( echo "$CPPFLAGS" | tr ' ' '\n' | uniq | sed '/^$/d' | paste -s -d' ' )
    
      PKG_CONFIG_PATH=${PKG_CONFIG_PATH:-}
      PKG_CONFIG_PATH+=":${HOMEBREW_PREFIX}/lib/pkgconfig"
      test -d "${CURL_OPENSSL_HOME}"  && PKG_CONFIG_PATH+=":${CURL_OPENSSL_HOME}/lib/pkgconfig"
      test -d "${TCLTK_HOME}"         && PKG_CONFIG_PATH+=":${TCLTK_HOME}/lib/pkgconfig"
      command -v brew >/dev/null 2>&1 && PKG_CONFIG_PATH+=':/usr/local/Homebrew/Library/Homebrew/os/mac/pkgconfig/14'
      test -d "${SQLITE_HOME}"        && PKG_CONFIG_PATH+=":${SQLITE_HOME}/lib/pkgconfig"
      test -d "${OPENSSL_HOME}"       && PKG_CONFIG_PATH+=":${OPENSSL_HOME}/lib/pkgconfig"
      test -d "${PYTHON_HOME}"        && PKG_CONFIG_PATH+=":${PYTHON_HOME}/lib/pkgconfig"
      test -d "${RUBY_HOME}"          && PKG_CONFIG_PATH+=":${RUBY_HOME}/lib/pkgconfig"
      test -d "${LIBRESSL_HOME}"      && PKG_CONFIG_PATH+=":${LIBRESSL_HOME}/lib/pkgconfig"
      test -d "${ICU4C_711}"          && PKG_CONFIG_PATH+=":${ICU4C_711}/lib/pkgconfig"
      test -d "${EXPAT_HOME}"         && PKG_CONFIG_PATH+=":${EXPAT_HOME}/lib/pkgconfig"
      test -d "${NCURSES_HOME}"       && PKG_CONFIG_PATH+=":${NCURSES_HOME}/lib/pkgconfig"
      test -d "${ZLIB_HOME}"          && PKG_CONFIG_PATH+=":${ZLIB_HOME}/lib/pkgconfig"
      PKG_CONFIG_PATH=$( echo "$PKG_CONFIG_PATH" | tr ':' '\n' | uniq | sed '/^$/d' | paste -s -d: )
    
      LIBRARY_PATH="${HOMEBREW_PREFIX}/lib"
      test -d "${LIBICONV_HOME}" && LIBRARY_PATH+=":${LIBICONV_HOME}/lib"
      LIBRARY_PATH=$( echo "$LIBRARY_PATH" | tr ':' '\n' | uniq | sed '/^$/d' | paste -s -d: )
    
      LD_LIBRARY_PATH=/usr/local/lib
      LD_LIBRARY_PATH=$( echo "$LD_LIBRARY_PATH" | tr ':' '\n' | uniq | sed '/^$/d' | paste -s -d: )
    
      while read -r _env; do
        export "${_env?}"
        echo -e "$(c Ys)>> ${_env}$(c)\n$(c Wi).. $(eval echo \$${_env})$(c)"
      done < <( echo 'LDFLAGS CFLAGS CPPFLAGS PKG_CONFIG_PATH LIBRARY_PATH LD_LIBRARY_PATH' |
                      fmt -1 |
                      fzf -1 --exit-0 \
                             --no-sort \
                             --multi \
                             --cycle \
                             --prompt 'env> ' \
                             --header 'TAB/SHIFT-TAB to select multiple items, CTRL-D to deselect-all, CTRL-S to select-all'
              )
    }
  • unset

      • alternative:

      $ unset ,,<TAB>
      # by deafult
      $ unset **<TAB>
    # eclr - environment variable clear, support multiple select
    # @author      : marslo
    # @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
    # @description : list all environment varialbe via `fzf`, and unset for selected items
    function eclr() {                          # [e]nvironment variable [c][l]ea[r]
      while read -r _env; do
        echo -e "$(c Ys)>> unset ${_env}$(c)\n$(c Wdi).. $(eval echo \$${_env})$(c)"
        unset "${_env}"
      done < <( env |
                sed -rn 's/^([a-zA-Z0-9]+)=.*$/\1/p' |
                fzf -1 --exit-0 --no-sort --multi --prompt='env> ' --header 'TAB to select multiple items'
              )
    }
  • or unset limited to environment list

    # mkclr - compilation environment variable clear, support multiple select
    # @author      : marslo
    # @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
    # @description : list compilation environment variable via `fzf`, and unset for selected items
    function mkclr() {                         # [m]a[k]e environment variable [c][l]ea[r]
      while read -r _env; do
        echo -e "$(c Ys)>> unset ${_env}$(c)\n$(c Wdi).. $(eval echo \$${_env})$(c)"
        unset "${_env}"
      done < <( echo 'LDFLAGS CFLAGS CPPFLAGS PKG_CONFIG_PATH LIBRARY_PATH LD_LIBRARY_PATH' |
                      fmt -1 |
                      fzf -1 --exit-0 \
                             --no-sort \
                             --multi \
                             --cycle \
                             --prompt 'env> ' \
                             --header 'TAB/SHIFT-TAB to select multiple items, CTRL-D to deselect-all, CTRL-S to select-all'
              )
      # echo -e "\n$(c Wdi)[TIP]>> to list all env via $(c)$(c Wdiu)\$ env | sed -rn 's/^([a-zA-Z0-9]+)=.*$/\1/p'$(c)"
    }
  • print and copy

    [!NOTE|label:references:]

    • environ – user environment : ( man environ )

    # penv - print environment variable, support multiple select
    # @author      : marslo
    # @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
    # @description : list all environment variable via `fzf`, and print values for selected items
    #   - to copy via `-c`
    #     - "${COPY}"
    #       - `pbcopy` in osx
    #       - `/mnt/c/Windows/System32/clip.exe` in wsl
    #   - to respect fzf options via `type -t _fzf_opts_completion >/dev/null 2>&1 && complete -F _fzf_opts_completion -o bashdefault -o default penv`
    #   - more options: https://github.com/junegunn/fzf/issues/3599#issuecomment-1907233847
    # shellcheck disable=SC2215,SC2016
    function penv() {                          # [p]rint [env]ironment variable
      local option
      local -a array
    
      while [[ $# -gt 0 ]]; do
        case "$1" in
          -c ) option+="$1 "   ; shift   ;;
          -* ) option+="$1 $2 "; shift 2 ;;
           * ) break                     ;;
        esac
      done
    
      option+='-1 --exit-0 --sort --multi --cycle'
      _echo_values() { echo -e "$(c Ys)>> $1$(c)\n$(c Wi).. $(eval echo \$$1)$(c)"; }
    
      while read -r _env; do
        _echo_values $_env
        array+=( "${_env}=$(eval echo \$${_env})" )
      done < <( env |
                sed -r 's/^([a-zA-Z0-9_-]+)=.*$/\1/' |
                fzf ${option//-c\ /} \
                    --prompt 'env> ' \
                    --height '50%' \
                    --preview-window 'top,30%,wrap,rounded' \
                    --preview='source ~/.marslo/bin/bash-color.sh; _env={}; echo -e "$(c Gs)${_env}=${!_env}$(c)"' \
                    --header 'TAB/SHIFT-TAB to select multiple items, CTRL-D to deselect-all, CTRL-S to select-all'
              )
      [[ "${option}" == *-c\ * ]] && [[ -n "${COPY}" ]] && "${COPY}" < <( printf '%s\n' "${array[@]}" | head -c-1 )
    }

process

[!NOTE]

$ (date; ps -ef) |
  fzf --bind='ctrl-r:reload(date; ps -ef)' \
      --header=$'Press CTRL-R to reload\n\n' --header-lines=2 \
      --preview='echo {}' --preview-window=down,3,wrap \
      --layout=reverse --height=80% |
  awk '{print $2}'

# or kill
$ (date; ps -ef) |
  fzf --bind='ctrl-r:reload(date; ps -ef)' \
      --header=$'Press CTRL-R to reload\n\n' --header-lines=2 \
      --preview='echo {}' --preview-window=down,3,wrap \
      --layout=reverse --height=80% |
  awk '{print $2}' |
  xargs kill -9
  • functions

    function lsps() {                          # [l]i[s]t [p]roces[s]
      (date; ps -ef) |
      fzf --bind='ctrl-r:reload(date; ps -ef)' \
          --header=$'Press CTRL-R to reload\n\n' --header-lines=2 \
          --preview='echo {}' --preview-window=down,3,wrap \
          --layout=reverse --height=80% |
      awk '{print $2}'
    }
    
    function killps() {                        # [kill] [p]roces[s]
      (date; ps -ef) |
      fzf --bind='ctrl-r:reload(date; ps -ef)' \
          --header=$'Press CTRL-R to reload\n\n' --header-lines=2 \
          --preview='echo {}' --preview-window=down,3,wrap \
          --layout=reverse --height=80% |
      awk '{print $2}' |
      xargs kill -9
    }

kubectl

  • kns

    # kns - kubectl set default namesapce
    # @author      : marslo
    # @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
    # @description : using `fzf` to list all available namespaces and use the selected namespace as default
    # [k]ubectl [n]ame[s]pace
    function kns() {                           # [k]ubectl [n]ame[s]pace
      local krn=$(kubecolor config get-contexts --no-headers $(kubectl config current-context) | awk "{print \$5}" | sed "s/^$/default/")
      kubectl get -o name namespace |
              sed "s|^.*/|  |;\|^  $(krn)$|s/ /*/" |
              fzf -e |
              sed "s/^..//" |
              xargs -i bash -c "echo -e \"\033[1;33m~~> {}\\033[0m\";
                                kubecolor config set-context --current --namespace {};
                                kubecolor config get-contexts;
                               "
    }
    
    # or limited with `kubectl get namespace`
    function kns() {
      echo 'namepsace-1 namespace-2 namespace-3 ...' |
            fmt -1 |
            fzf -1 -0 --no-sort +m --prompt='namespace> ' |
            xargs -i bash -c "echo -e \"\033[1;33m~~> {}\\033[0m\";
                              kubectl config set-context --current --namespace {};
                              kubecolor config get-contexts;
                             "
    }
  • kcani

    # kcani - kubectl check permission (auth can-i)
    # @author      : marslo
    # @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ffunc.sh
    # @description : check whether an action is allowed in given namespaces. support multiple selection
    # [k]ubectl [can]-[i]
    function kcani() {                         # [k]ubectl [can]-[i]
      local namespaces=''
      local actions='list get watch create update delete'
      local components='sts deploy secrets configmap ingressroute ingressroutetcp'
    
      namespaces=$( echo 'namepsace-1 namespace-2 namespace-3 ...' |
                        fmt -1 |
                        fzf -1 -0 --no-sort --prompt='namespace> ' \
                            --bind 'ctrl-y:execute-silent(echo -n {+} | pbcopy)+abort' \
                            --header 'Press CTRL-Y to copy name into clipboard'
                 )
      [[ -z "${namespaces}" ]] && echo "$(c Rs)ERROR: select at least one namespace !$(c)" && return
    
      while read -r namespace; do
        echo -e "\n>> $(c Ys)${namespace}$(c)"
        k-p-cani ${namespace}                  # for pods component
    
        for _c in ${components}; do
          local res=''
          echo -e ".. $(c Ms)${_c}$(c) :"
          for _a in ${actions}; do
            r=$(_can_i -n "${namespace}" "${_a}" "${_c}")
            res+="${r} ";
          done;                                # in actions
          echo -e "${actions}\n${res}" | "${COLUMN}" -t | sed 's/^/\t/g'
        done;                                  # in components
    
      done< <(echo "${namespaces}" | fmt -1)
    }
    
    # _can_i - kubectl check permission (auth can-i) for pods component
    # @author      : marslo
    # @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ifunc.sh
    # @description : check whether given action is allowed in given namespaces; if namespace not provide, using default namespace
    function _can_i() {
      local namespace
      namespace=$(command kubectl config view --minify -o jsonpath='{..namespace}')
    
      while [[ $# -gt 0 ]]; do
        case "$1" in
          -n | --namespace ) namespace="$2" ; shift 2 ;;
                         * ) break                    ;;
        esac
      done
    
      [[ 0 = "$#" ]] && echo -e "$(c Rs)>> ERROR: must provide action to check with$(c)\n\nUSAGE$(c Gis)\n\t\$ $0 list pod\n\t\$ $0 -n <namespace> create deploy$(c)" && return
      checker=$*
      r="$(kubectl auth can-i ${checker} -n ${namespace})";
      [[ 'yes' = "${r}" ]] && r="$(c Gs)${r}$(c)" || r="$(c Rs)${r}$(c)";
      echo -e "${r}";
    }
    
    # k-p-cani - kubectl check permission (auth can-i) for pods component
    # @author      : marslo
    # @source      : https://github.com/marslo/mylinux/blob/master/confs/home/.marslo/bin/ifunc.sh
    # @description : check whether certain action is allowed in given namespaces
    # [k]ubectl [can]-[i]
    function k-p-cani() {
      local components='pods'
      declare -A pactions
      pactions=(
                 ['0_get']="get ${components}"
                 ['1_get/exec']="get ${components}/exec"
                 ['2_get/sub/exec']="get ${components} --subresource=exec"
                 ['2_get/sub/log']="get ${components} --subresource=log"
                 ['3_list']="list ${components}"
                 ['4_create']="create ${components}"
                 ['5_create/exec']="create ${components}/exec"
                 ['6_get/sub/exec']="get ${components} --subresource=exec"
               )
    
      while read -r pnamespace; do
        local headers=''
        local pres=''
        while read -r _a; do
          headers+="$(sed -rn 's/^[0-9]+_(.+)$/\1/p' <<< ${_a}) "
          r=$(_can_i -n "${pnamespace}" "${pactions[${_a}]}")
          pres+="${r} "
        done < <( for _act in "${!pactions[@]}"; do echo "${_act}"; done | sort -h )
        echo -e ".. $(c Ms)pods$(c) :"
        echo -e "${headers}\n${pres}" | "${COLUMN}" -t | sed 's/^/\t/g'
      done< <(echo "$*" | fmt -1)
    }
  • Log tailing : pods

    pods() {
      : | command='kubectl get pods --all-namespaces' fzf \
        --info=inline --layout=reverse --header-lines=1 \
        --prompt "$(kubectl config current-context | sed 's/-context$//')> " \
        --header $'╱ Enter (kubectl exec) ╱ CTRL-O (open log in editor) ╱ CTRL-R (reload) ╱\n\n' \
        --bind 'start:reload:$command' \
        --bind 'ctrl-r:reload:$command' \
        --bind 'ctrl-/:change-preview-window(80%,border-bottom|hidden|)' \
        --bind 'enter:execute:kubectl exec -it --namespace {1} {2} -- bash > /dev/tty' \
        --bind 'ctrl-o:execute:${EDITOR:-vim} <(kubectl logs --all-containers --namespace {1} {2}) > /dev/tty' \
        --preview-window up:follow \
        --preview 'kubectl logs --follow --all-containers --tail=10000 --namespace {1} {2}' "$@"
    }

  • bip

    # Install (one or multiple) selected application(s)
    # using "brew search" as source input
    # mnemonic [B]rew [I]nstall [P]ackage
    bip() {
      local inst=$(brew search "$@" | fzf -m)
    
      if [[ $inst ]]; then
        for prog in $(echo $inst);
        do; brew install $prog; done;
      fi
    }
  • bup

    # Update (one or multiple) selected application(s)
    # mnemonic [B]rew [U]pdate [P]ackage
    bup() {
      local upd=$(brew leaves | fzf -m)
    
      if [[ $upd ]]; then
        for prog in $(echo $upd);
        do; brew upgrade $prog; done;
      fi
    }

config

FZF_CTRL_R_OPTS="--preview 'echo {}'"
FZF_CTRL_R_OPTS+=" --preview-window up:3:hidden:wrap"
FZF_CTRL_R_OPTS+=" --bind 'ctrl-/:toggle-preview'"
FZF_CTRL_R_OPTS+=" --bind 'ctrl-y:execute-silent(echo -n {2..} | pbcopy)+abort'"
FZF_CTRL_R_OPTS+=" --color header:italic"
FZF_CTRL_R_OPTS+=" --header 'Press CTRL-Y to copy command into clipboard'"
export FZF_CTRL_R_OPTS

# or
export FZF_CTRL_R_OPTS="
  --preview 'echo {}' --preview-window up:3:hidden:wrap
  --bind 'ctrl-/:toggle-preview'
  --bind 'ctrl-y:execute-silent(echo -n {2..} | pbcopy)+abort'
  --color header:italic
  --header 'Press CTRL-Y to copy command into clipboard'"
  • __fzf_history__

$ type __fzf_history__
__fzf_history__ is a function
__fzf_history__ ()
{
    local output opts script;
    opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort ${FZF_CTRL_R_OPTS-} +m --read0";
    script='BEGIN { getc; $/ = "\n\t"; $HISTCOUNT = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCOUNT - $. . "\t$_" if !$seen{$_}++';
    output=$(set +o pipefail
builtin fc -lnr -2147483648 | last_hist=$(HISTTIMEFORMAT='' builtin history 1) command perl -n -l0 -e "$script" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) --query "$READLINE_LINE") || return;
    READLINE_LINE=${output#*' '};
    if [[ -z "$READLINE_POINT" ]]; then
        echo "$READLINE_LINE";
    else
        READLINE_POINT=0x7fffffff;
    fi
}

$ fcf __fzf_history__
/usr/local/Cellar/fzf/0.42.0/shell/key-bindings.bash

[!NOTE|label:references:]

  • to disable/reset osx default ⌃+↑ and ⌃+↓

# preview file content using bat (https://github.com/sharkdp/bat)
FZF_CTRL_T_OPTS="--preview 'bat -n --color=always {}'"
FZF_CTRL_T_OPTS+=" --bind 'ctrl-/:change-preview-window(down|hidden|)'"
FZF_CTRL_T_OPTS+=" --bind 'ctrl-p:preview-up,ctrl-n:preview-down'"
FZF_CTRL_T_OPTS+=" --header 'CTRL-N/CTRL-P or CTRL-↑/CTRL-↓ to view contents'"
export FZF_CTRL_T_OPTS

# or
export FZF_CTRL_T_OPTS="
  --preview 'bat -n --color=always {}'
  --bind 'ctrl-p:preview-up,ctrl-n:preview-down'
  --bind 'ctrl-/:change-preview-window(down|hidden|)'"
  • __fzf_select__

    $ type __fzf_select__
    __fzf_select__ is a function
    __fzf_select__ ()
    {
        local cmd opts;
        cmd="${FZF_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune     -o -type f -print     -o -type d -print     -o -type l -print 2> /dev/null | command cut -b3-"}";
        opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse --scheme=path ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-} -m";
        eval "$cmd" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) "$@" | while read -r item; do
            printf '%q ' "$item";
        done
    }
    
    $ fcf __fzf_select__
    __fzf_select__ 19 /Users/marslo/.marslo/utils/fzf/shell/key-bindings.bas
    
    $ mdfind key-bindings.bas
    /Users/marslo/iMarslo/tools/git/marslo/mbook/docs/devops/adminTools.md
    /usr/local/Cellar/fzf/0.42.0/shell/key-bindings.bash
  • __fzf_select_dir()

    # another ctrl-t script to select a directory and paste it into line
    __fzf_select_dir () {
      builtin typeset READLINE_LINE_NEW="$(
        command find -L . \( -path '*/\.*' -o -fstype dev -o -fstype proc \) \
                -prune \
                -o -type f -print \
                -o -type d -print \
                -o -type l -print 2>/dev/null \
        | command sed 1d \
        | command cut -b3- \
        | env fzf -m
      )"
    
      if [[ -n $READLINE_LINE_NEW ]]; then
        builtin bind '"\er": redraw-current-line'
        builtin bind '"\e^": magic-space'
        READLINE_LINE=${READLINE_LINE:+${READLINE_LINE:0:READLINE_POINT}}${READLINE_LINE_NEW}${READLINE_LINE:+${READLINE_LINE:READLINE_POINT}}
        READLINE_POINT=$(( READLINE_POINT + ${#READLINE_LINE_NEW} ))
      else
        builtin bind '"\er":'
        builtin bind '"\e^":'
      fi
    }
    
    builtin bind -x '"\C-x1": __fzf_select_dir'
    builtin bind '"\C-t": "\C-x1\e^\er"'

# junegunn/seoul256.vim (dark)
export FZF_DEFAULT_OPTS='--color=bg+:#3F3F3F,bg:#4B4B4B,border:#6B6B6B,spinner:#98BC99,hl:#719872,fg:#D9D9D9,header:#719872,info:#BDBB72,pointer:#E12672,marker:#E17899,fg+:#D9D9D9,preview-bg:#3F3F3F,prompt:#98BEDE,hl+:#98BC99'

# junegunn/seoul256.vim (light)
export FZF_DEFAULT_OPTS='--color=bg+:#D9D9D9,bg:#E1E1E1,border:#C8C8C8,spinner:#719899,hl:#719872,fg:#616161,header:#719872,info:#727100,pointer:#E12672,marker:#E17899,fg+:#616161,preview-bg:#D9D9D9,prompt:#0099BD,hl+:#719899'

# morhetz/gruvbox
export FZF_DEFAULT_OPTS='--color=bg+:#3c3836,bg:#32302f,spinner:#fb4934,hl:#928374,fg:#ebdbb2,header:#928374,info:#8ec07c,pointer:#fb4934,marker:#fb4934,fg+:#ebdbb2,prompt:#fb4934,hl+:#fb4934'

# arcticicestudio/nord-vim
export FZF_DEFAULT_OPTS='--color=bg+:#3B4252,bg:#2E3440,spinner:#81A1C1,hl:#616E88,fg:#D8DEE9,header:#616E88,info:#81A1C1,pointer:#81A1C1,marker:#81A1C1,fg+:#D8DEE9,prompt:#81A1C1,hl+:#81A1C1'

# tomasr/molokai
export FZF_DEFAULT_OPTS='--color=bg+:#293739,bg:#1B1D1E,border:#808080,spinner:#E6DB74,hl:#7E8E91,fg:#F8F8F2,header:#7E8E91,info:#A6E22E,pointer:#A6E22E,marker:#F92672,fg+:#F8F8F2,prompt:#F92672,hl+:#F92672'
  • generating fzf color theme from vim color schemes

    let g:fzf_colors =
    \ { 'fg':         ['fg', 'Normal'],
      \ 'bg':         ['bg', 'Normal'],
      \ 'preview-bg': ['bg', 'NormalFloat'],
      \ 'hl':         ['fg', 'Comment'],
      \ 'fg+':        ['fg', 'CursorLine', 'CursorColumn', 'Normal'],
      \ 'bg+':        ['bg', 'CursorLine', 'CursorColumn'],
      \ 'hl+':        ['fg', 'Statement'],
      \ 'info':       ['fg', 'PreProc'],
      \ 'border':     ['fg', 'Ignore'],
      \ 'prompt':     ['fg', 'Conditional'],
      \ 'pointer':    ['fg', 'Exception'],
      \ 'marker':     ['fg', 'Keyword'],
      \ 'spinner':    ['fg', 'Label'],
      \ 'header':     ['fg', 'Comment'] }
    
    :echo fzf#wrap()
    :call append('$', printf('export FZF_DEFAULT_OPTS="%s"', matchstr(fzf#wrap().options, "--color[^']*")))

tips

  • move cursor to position

    [!TIP]

    $ seq 100 | fzf --sync --bind 'start:pos(N)'
  • select by column

    [!NOTE]

    $ seq 20 | pr -ts' ' --column 4
    1 6 11 16
    2 7 12 17
    3 8 13 18
    4 9 14 19
    5 10 15 20
    
    $ seq 20 | pr -ts' ' --column 4 | fzf --sync --bind 'start:select-all+become:cat {+f2}'
    6
    7
    8
    9
    10
    
    $ seq 20 | pr -ts' ' --column 4 | fzf --sync --bind 'start:select-all+become:echo {+2}'
    6 7 8 9 10
  • --with-nth

    [!NOTE]

    $ echo {a..z} | fzf --with-nth='1..3'
    # https://github.com/junegunn/fzf/issues/1323#issuecomment-499615418
    # for git comment show
    $ git log --pretty=oneline |
      fzf --ansi --delimiter=' ' --no-multi --preview='git show --color=always {1}' --with-nth=2.. |
      cut --delimiter=' ' --field=1
    $ echo -e 'first line\tfirst preview\nsecond line\tsecond preview' |
      fzf --delimiter='\t' --with-nth=1 --preview='echo {2}'
  • --track

    $ git log --oneline --graph --color=always |
      nl |
      fzf --ansi --track --no-sort --layout=reverse-list
  • select-all

    $ seq 3 | fzf --multi --sync --bind start:last+select-all
  • select-n

    [!TIP]

    # select top 2 and move down
    $ seq 10 | fzf --multi --sync --reverse --bind start:select+down+select+down
    
    # select top 2
    $ seq 10 | fzf --multi --sync --reverse --bind start:select+down+select
  • for git

    [!NOTE]

    # cannot be used to grab a sha as an argument.
    fshow() {
      git log --color=always \
          --format="%C(auto)%h%d %s %C(black)%C(bold)%cr" "$@" |
      fzf --ansi --no-sort --reverse --tiebreak=index --bind=ctrl-s:toggle-sort \
          --bind "ctrl-o:execute(git show --color=always {1})"
    }
    • view history by fzf, show revision number finally

      $ git log --pretty=oneline \
            | fzf --ansi --delimiter=' ' --no-multi --preview='git show --color=always {1}' --with-nth=2.. \
            | cut --delimiter=' ' --field=1
      # or
      $ git log --pretty=oneline --all \
            | fzf --ansi --no-multi --preview='git show --color=always {1}' --with-nth=2.. \
            | awk '{print $1}'
    • pr checkout

      [!NOTE]

      function pr-checkout() {
        local jq_template pr_number
      
        jq_template='"'\
          '#\(.number) - \(.title)'\
          '\t'\
          'Author: \(.user.login)\n'\
          'Created: \(.created_at)\n'\
          'Updated: \(.updated_at)\n\n'\
          '\(.body)'\
          '"'
      
        pr_number=$(
          gh api 'repos/:owner/:repo/pulls' |
          jq ".[] | $jq_template" |
          sed -e 's/"\(.*\)"/\1/' -e 's/\\t/\t/' |
          fzf \
            --with-nth=1 \
            --delimiter='\t' \
            --preview='echo -e {2}' \
            --preview-window=top:wrap |
          sed 's/^#\([0-9]\+\).*/\1/'
        )
      
        if [ -n "$pr_number" ]; then
          gh pr checkout "$pr_number"
        fi
      }
  • creating feature branches from JIRA issues

    function create-branch() {
      # The function expectes that username and password are stored using secret-tool.
      # To store these, use
      # secret-tool store --label="JIRA username" jira username
      # secret-tool store --label="JIRA password" jira password
    
      local jq_template query username password branch_name
    
      jq_template='"'\
        '\(.key). \(.fields.summary)'\
        '\t'\
        'Reporter: \(.fields.reporter.displayName)\n'\
        'Created: \(.fields.created)\n'\
        'Updated: \(.fields.updated)\n\n'\
        '\(.fields.description)'\
        '"'
      query='project=BLOG AND status="In Progress" AND assignee=currentUser()'
      username=$(secret-tool lookup jira username)
      password=$(secret-tool lookup jira password)
    
      branch_name=$(
        curl \
          --data-urlencode "jql=$query" \
          --get \
          --user "$username:$password" \
          --silent \
          --compressed \
          'https://jira.example.com/rest/api/2/search' |
        jq ".issues[] | $jq_template" |
        sed -e 's/"\(.*\)"/\1/' -e 's/\\t/\t/' |
        fzf \
          --with-nth=1 \
          --delimiter='\t' \
          --preview='echo -e {2}' \
          --preview-window=top:wrap |
        cut -f1 |
        sed -e 's/\. /\t/' -e 's/[^a-zA-Z0-9\t]/-/g' |
        awk '{printf "%s/%s", $1, tolower($2)}'
      )
    
      if [ -n "$branch_name" ]; then
        git checkout -b "$branch_name"
      fi
    }

[!NOTE|label:references:]

  • install

# osx
$ brew install fd
# v9.0.0
$ ln -sf $(brew --prefix fd)/share/bash-completion/completions/fd /usr/local/etc/bash_completion.d/fd

# debine
$ sudo apt install fd-find                  # ubuntu 22.04 : fd 8.3.1
$ curl -fsSL -O http://ftp.osuosl.org/pub/ubuntu/pool/universe/r/rust-fd-find/fd-find_9.0.0-1_amd64.deb
$ sudo dpkg -i fd-find_9.0.0-1_amd64.deb    # ubuntu any: fd 9.0.0
$ ln -s $(which fdfind) ~/.local/bin/fd
$ export PATH=~/.local:$PATH

# centos
$ sudo dnf install fd-find
  • from source

    [!NOTE]

    • install rust via

      $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
      $ source "$HOME/.cargo/env"
      $ cargo --version
      cargo 1.74.1 (ecb9851af 2023-10-18)
    $ git clone https://github.com/sharkdp/fd && cd fd
    
    # osx
    $ brew install rust
    $ cargo install amethyst_tools
    # wsl/ubuntu
    $ sudo apt install cargo
    
    $ cargo build                     # build
    $ cargo test                      # run unit tests and integration tests
    $ cargo install --debug --path .  # install in osx
    $ cargo install --path .          # install in ubuntu/wsl
    $ ln -sf /home/marslo/.cargo/bin/fd /home/marslo/.local/bin/fd
    
    # completion ( >= 9.0.0 )
    # wsl/ubuntu/centos
    $ fd --gen-completions | sudo tee /usr/share/bash-completion/completions/fd
    # or centos
    $ fd --gen-completions | sudo tee /etc/bash_completion.d/fd
    # osx
    $ fd --gen-completions | sudo tee /usr/local/etc/bash_completion.d/fd
  • verify

    $ fd --version
    fd 9.0.0
  • usage

    $ fd --hidden ^.env$
    .env
    
    $ fd --type f --strip-cwd-prefix --hidden --follow --exclude .git --exclude node_modules ifunc
    bin/ifunc.sh

advanced usage

  • crontab for delete '.DS_'

    /usr/local/bin/fd -IH --glob '*\.DS_*' $HOME | xargs -r -i rm '{}'
    # or
    /usr/local/bin/fd -Iu --glob '*\.DS_*' $HOME | xargs -r -i rm '{}'
    # or
    /usr/local/bin/fd --type f --hidden --follow --unrestricted --color=never --exclude .Trash --glob '*\.DS_*' $HOME  | xargs -r -i rm '{}'
  • ffs

    # [f]ind [f]ile and [s]ort
    function ffs() {
      local opt=''
      while [[ $# -gt 0 ]]; do
        case "$1" in
              -g ) opt+="$1 "   ; shift   ;;
             -fg ) opt+="$1 "   ; shift   ;;
              -f ) opt+="$1 "   ; shift   ;;
             --* ) opt+="$1 $2 "; shift 2 ;;
              -* ) opt+="$1 "   ; shift   ;;
               * ) break                  ;;
        esac
      done
    
      local path=${1:-~/.marslo}
      local num=${2:-10}
      num=${num//-/}
      local depth=${3:-}
      depth=${depth//-/}
      local option='--type f'
    
      if [[ "${opt}" =~ '-g ' ]]; then
        # git show --name-only --pretty="format:" -"${num}" | awk 'NF' | sort -u
        # references: https://stackoverflow.com/a/54677384/2940319
        git log --date=iso-local --first-parent --pretty=%cd --name-status --relative |
            awk 'NF==1{date=$1}NF>1 && !seen[$2]++{print date,$0}' FS=$'\t' |
            head -"${num}"
      elif [[ "${opt}" =~ '-fg ' ]]; then
        # references: https://stackoverflow.com/a/63864280/2940319
        git ls-tree -r --name-only HEAD -z |
            TZ=PDT xargs -0 -I_ git --no-pager log -1 --date=iso-local --format="%ad | _" -- _ |
            sort -r |
            head -"${num}"
      elif [[ "${opt}" =~ '-f ' ]]; then
        option=${option: 1}
        [[ -n "${depth}" ]] && option="-maxdepth ${depth} ${option}"
        # shellcheck disable=SC2086
        find "${path}" ${option} \
                       -not -path '*/\.git/*' \
                       -not -path '*/node_modules/*' \
                       -not -path '*/go/pkg/*' \
                       -not -path '*/git/git*/*' \
                       -not -path '*/.marslo/utils/*' \
                       -not -path '*/.marslo/.completion/*' \
                       -printf "%10T+ | %p\n" |
        sort -r |
        head -"${num}"
      else
        if [[ "${opt}}" =~ .*-t.* ]] || [[ "${opt}" =~ .*--type.* ]]; then
          option="${option//--type\ f/}"
        fi
        option="${opt} ${option} --hidden --follow --unrestricted --ignore-file ~/.fdignore"
        [[ -n "${depth}"    ]] && option="--max-depth ${depth} ${option}"
        [[ '.' != "${path}" ]] && option="${path} ${option}"
        # shellcheck disable=SC2086,SC2027
        eval """ fd . "${option}" --exec stat --printf='%y | %n\n' | sort -r | head -"${num}" """
      fi
    }
  • search for multiple pattern

    [!NOTE]

    $ fd --unrestricted '^*\.(png|gif|jpg)$'
    $ fd --unrestricted --extension png --extension jpg --extension gif

rg the faster mg

[!NOTE]

  • install

    # with cargo
    $ cargo install ripgrep
    
    # osx
    $ brew install ripgrep
    
    # rhel/centos
    $ sudo yum install -y yum-utils
    $ sudo yum-config-manager --add-repo=https://copr.fedorainfracloud.org/coprs/carlwgeorge/ripgrep/repo/epel-7/carlwgeorge-ripgrep-epel-7.repo
    $ sudo yum install ripgrep
    # or via epel: https://marslo.github.io/ibook/linux/basic.html#tools-installation
    $ sudo yum install -y yum-utils epel-release
    $ sudo yum install ripgrep
    
    # ubuntu
    $ curl -LO https://github.com/BurntSushi/ripgrep/releases/download/14.0.3/ripgrep_14.0.3-1_amd64.deb
    $ sudo dpkg -i ripgrep_14.0.3-1_amd64.deb
    # or
    $ sudo apt install -y ripgrep
    
    # from source
    $ git clone https://github.com/BurntSushi/ripgrep && cd ripgrep
    $ cargo build --release
    $ ./target/release/rg --version
    0.1.3
    
    # completion
    # wsl/ubuntu/centos
    $ rg --generate complete-bash | sudo tee /usr/share/bash-completion/completions/rg
    # or centos
    $ rg --generate complete-bash | sudo tee /etc/bash_completion.d/rg
    # osx
    $ rg --generate complete-bash | sudo tee /usr/local/etc/bash_completion.d/rg

usage

  • crontab for delete '.DS_'

    /usr/local/bin/rg --hidden --smart-case --files "$HOME" -g  '*\.DS_*' | xargs -r -i rm '{}'
  • search in dotfiles with ripgrep

    [!NOTE|label:references:]

    $ rg --hidden 'alias cat'
    
    # or https://github.com/BurntSushi/ripgrep/issues/623#issuecomment-659909044
    alias rg="rg --hidden --glob '!.git'"
    
    # or `.ignore` or `.rgignore`
    $ cat ~/.rgignore
    .git/
    .tox/
    .github/
  • info

    $ tree -a .
    .
    ├── .no-hidden
    └── no-hidden
    
    1 directory, 2 files
    
    $ rg --files --debug
    DEBUG|globset|crates/globset/src/lib.rs:416: glob converted to regex: Glob { glob: "**/.DS_Store?", re: "(?-u)^(?:/?|.*/)\\.DS_Store[^/]$", opts: GlobOptions { case_insensitive: false, literal_separator: true, backslash_escape: true }, tokens: Tokens([RecursivePrefix, Literal('.'), Literal('D'), Literal('S'), Literal('_'), Literal('S'), Literal('t'), Literal('o'), Literal('r'), Literal('e'), Any]) }
    DEBUG|globset|crates/globset/src/lib.rs:416: glob converted to regex: Glob { glob: "**/._*", re: "(?-u)^(?:/?|.*/)\\._[^/]*$", opts: GlobOptions { case_insensitive: false, literal_separator: true, backslash_escape: true }, tokens: Tokens([RecursivePrefix, Literal('.'), Literal('_'), ZeroOrMore]) }
    DEBUG|globset|crates/globset/src/lib.rs:421: built glob set; 0 literals, 7 basenames, 0 extensions, 0 prefixes, 0 suffixes, 0 required extensions, 2 regexes
    DEBUG|ignore::walk|crates/ignore/src/walk.rs:1741: ignoring ./.no-hidden: Ignore(IgnoreMatch(Hidden))
    no-hidden
    
    $ rg --files --debug --hidden
    DEBUG|globset|crates/globset/src/lib.rs:416: glob converted to regex: Glob { glob: "**/.DS_Store?", re: "(?-u)^(?:/?|.*/)\\.DS_Store[^/]$", opts: GlobOptions { case_insensitive: false, literal_separator: true, backslash_escape: true }, tokens: Tokens([RecursivePrefix, Literal('.'), Literal('D'), Literal('S'), Literal('_'), Literal('S'), Literal('t'), Literal('o'), Literal('r'), Literal('e'), Any]) }
    DEBUG|globset|crates/globset/src/lib.rs:416: glob converted to regex: Glob { glob: "**/._*", re: "(?-u)^(?:/?|.*/)\\._[^/]*$", opts: GlobOptions { case_insensitive: false, literal_separator: true, backslash_escape: true }, tokens: Tokens([RecursivePrefix, Literal('.'), Literal('_'), ZeroOrMore]) }
    DEBUG|globset|crates/globset/src/lib.rs:421: built glob set; 0 literals, 7 basenames, 0 extensions, 0 prefixes, 0 suffixes, 0 required extensions, 2 regexes
    no-hidden
    .no-hidden

ag the faster mg

  • install

    # osx
    $ brew install the_silver_searcher
    
    # ubuntu >= 13.10
    $ apt-get install silversearcher-ag

  • install

    # osx
    $ brew install fzy
    
    # debine
    $ sudo apt install fzy
    
    # source code
    $ git clone git@github.com:jhawthorn/fzy.git && cd fzy
    $ make
    $ sudo make install
  • verify

    $ fzy --version
    fzy 1.0 © 2014-2018 John Hawthorn

[!NOTE|label:references:]

  • install

    # osx
    $ brew install bat
    ## extra
    $ brew install bat-extras
    
    # ubuntu
    $ sudo apt intall bat -y
    $ ln -s /usr/bin/batcat ~/.marslo/bin/bat
    
    # centos
    $ dnf install bat
    ## extra
    $ sudo dnf install dnf-plugins-core
    $ sudo dnf copr enable awood/bat-extras
    $ sudo dnf install bat-extras
    
    # ubuntu latest version
    $ sudo apt instal -y https://github.com/sharkdp/bat/releases/download/v0.23.0/bat-musl_0.23.0_amd64.deb
    $ ln -s /usr/bin/batcat ~/.marslo/bin/bat
    
    # from release package
    $ curl -fsSL https://github.com/sharkdp/bat/releases/download/v0.23.0/bat-v0.23.0-x86_64-unknown-linux-musl.tar.gz |
           tar xzf - -C ${iRCHOME}/utils/bat-v0.23.0
    $ ln -sf ${iRCHOME}/utils/bat-v0.23.0/bat ${iRCHOME}/bin/bat
    # or
    $ V=$(curl --silent "https://api.github.com/repos/sharkdp/bat/releases/latest" | grep -Eo '"tag_name": "v(.*)"' | sed -E 's/.*"([^"]+)".*/\1/') &&
        curl -sOL "https://github.com/sharkdp/bat/releases/download/$V/bat-$V-x86_64-unknown-linux-musl.tar.gz" &&
        tar xzvf "bat-$V-x86_64-unknown-linux-musl.tar.gz" -C . &&
        sudo sh -c "cp ./bat-$V-x86_64-unknown-linux-musl/bat /usr/local/bin/bat" &&
        rm bat-$V-x86_64-unknown-linux-musl.tar.gz &&
        unset V
    
    # from source
    $ git clone git@github.com:sharkdp/bat.git && cd bat
    $ git submodule update -f --init --recursive
    # or
    $ git clone --recurse-submodules git@github.com:sharkdp/bat.git && cd bat
    
    $ cargo install --locked bat
    
    # build a bat binary with modified syntaxes and themes
    $ bash assets/create.sh
    $ cargo install --path . --locked --force
  • completion

    $ sed 's/{{PROJECT_EXECUTABLE}}/bat/'                                     "${iRCHOME}/utils/bat/assets/completions/bat.bash.in" | sudo tee /etc/bash_completion.d/bat
    # or
    $ sed 's/{{PROJECT_EXECUTABLE}}/-o nosort -o bashdefault -o default bat/' "${iRCHOME}/utils/bat/assets/completions/bat.bash.in" | sudo tee /etc/bash_completion.d/bat
    
    # or
    $ sed 's/{{PROJECT_EXECUTABLE}}/bat/'                                     -i "${iRCHOME}/utils/bat/assets/completions/bat.bash.in"
    $ sed 's/{{PROJECT_EXECUTABLE}}/-o nosort -o bashdefault -o default bat/' -i "${iRCHOME}/utils/bat/assets/completions/bat.bash.in"
    $ sudo ln -sf "${iRCHOME}/utils/bat/assets/completions/bat.bash.in" /usr/share/bash-completion/completions/bat
    
    # or without modify `bat.bash.in` and add complete into bashrc
    $ sudo ln -sf "${iRCHOME}/utils/bat/assets/completions/bat.bash.in" /usr/share/bash-completion/completions/bat
    $ echo 'type -t _bat >/dev/null 2>&1 && complete -F _bat -o nosort -o bashdefault -o default bat' >> ~/.bashrc
  • verify

    $ bat --version
    bat 0.24.0 (28990bc-modified)

usage

  • help()

    # in your .bashrc/.zshrc/*rc
    alias bathelp='bat --plain --language=help'
    help() { "$@" --help 2>&1 | bathelp }
    
    # calling in bash:
    # $ help bat
    • for zsh

      $ alias -g -- -h='-h 2>&1 | bat --language=help --style=plain'
      $ alias -g -- --help='--help 2>&1 | bat --language=help --style=plain'
  • cygwin path issue

    bat() {
      local index
      local args=("$@")
      for index in $(seq 0 ${#args[@]}) ; do
        case "${args[index]}" in
          -* ) continue;;
           * ) [ -e "${args[index]}" ] && args[index]="$(cygpath --windows "${args[index]}") ";;
        esac
      done
      command bat "${args[@]}"
    }

config

  • config-file

    $ bat --config-file
    /Users/marslo/.config/bat/config
    
    # if need modify
    $ export BAT_CONFIG_PATH="$(bat --config-file)"
    
    # generate standard config-file
    $ bat --generate-config-file
    Success! Config file written to /Users/marslo/.config/bat/config
  • sample content

    $ bat $(bat --config-file) | sed -r '/^(#.*)$/d;/^\s*$/d'  | bat --language ini
           STDIN
       1   --theme="gruvbox-dark"
       2   --style="numbers,changes,header"
       3   --italic-text=always
       4   --pager="less --RAW-CONTROL-CHARS --quit-if-one-screen --mouse"
       5   --map-syntax "*.ino:C++"
       6   --map-syntax ".ignore:Git Ignore"
       7   --map-syntax='*.conf:INI'
       8   --map-syntax='/etc/apache2/**/*.conf:Apache Conf'

theme

# list themes
$ bat --list-themes

# modify them
$ export BAT_THEME='gruvbox-dark'
# or
$ echo '--theme="gruvbox-dark"' >> $(bat --config-file)
  • new theme

    $ mkdir -p "$(bat --config-dir)/themes"
    $ cd "$(bat --config-dir)/themes"
    
    # download a theme in '.tmtheme' format, for example:
    $ git clone https://github.com/greggb/sublime-snazzy
    
    # Update the binary cache
    $ bat cache --build

cheat.sh

[!NOTE]

# install
$ curl -fsSL https://cht.sh/:cht.sh --create-dirs -o ~/.local/bin/cht.sh
$ chmod +x ~/.local/bin/cht.sh

# completion
$ curl -fsSL https://cheat.sh/:bash_completion --create-dirs -o ~/.marslo/.completion/cht.sh
$ source ~/.marslo/.completion/cht.sh

# added in .bashrc
$ [[ -f "${iRCHOME}"/.completion/cht.sh ]] && source "${iRCHOME}"/.completion/cht.sh

[!TIP]

# deps install
$ sudo snap install zig --class --beta
$ sudo apt install libncurses-dev

$ make
$ sudo make install PREFIX=/usr/local

# verify
$ ncdu --version
ncdu 2.3

[!NOTE|label:Alternatives:]

theme and colors

[!TIP]

  • flags

    ┌───────┬────────────────┬─────────────────┐   ┌───────┬─────────────────┬───────┐
    │ Fg/Bg │ Color          │ Octal           │   │ Code  │ Style           │ Octal │
    ├───────┼────────────────┼─────────────────┤   ├───────┼─────────────────┼───────┤
    │  K/k  │ Black          │ \e[ + 3/4  + 0m │   │  s/S  │ Bold (strong)   │ \e[1m │
    │  R/r  │ Red            │ \e[ + 3/4  + 1m │   │  d/D  │ Dim             │ \e[2m │
    │  G/g  │ Green          │ \e[ + 3/4  + 2m │   │  i/I  │ Italic          │ \e[3m │
    │  Y/y  │ Yellow         │ \e[ + 3/4  + 3m │   │  u/U  │ Underline       │ \e[4m │
    │  B/b  │ Blue           │ \e[ + 3/4  + 4m │   │  f/F  │ Blink (flash)   │ \e[5m │
    │  M/m  │ Magenta        │ \e[ + 3/4  + 5m │   │  n/N  │ Negative        │ \e[7m │
    │  C/c  │ Cyan           │ \e[ + 3/4  + 6m │   │  h/H  │ Hidden          │ \e[8m │
    │  W/w  │ White          │ \e[ + 3/4  + 7m │   │  t/T  │ Strikethrough   │ \e[9m │
    ├───────┴────────────────┴─────────────────┤   ├───────┼─────────────────┼───────┤
    │  High intensity        │ \e[ + 9/10 + *m │   │   0   │ Reset           │ \e[0m │
    └────────────────────────┴─────────────────┘   └───────┴─────────────────┴───────┘
                                                    Uppercase = Reset a style: \e[2*m

[!NOTE|label:references:]

  • install

    $ curl -sL git.io/ansi -o "${iRCHOME}"/utils/ansi && chmod +x $_
    $ ln -sf $(realpath "${iRCHOME}"/utils/ansi) $(realpath "${iRCHOME}"/bin)/ansi

[!NOTE|label:references:]

# ubuntu
$ sudo add-apt-repository ppa:aos1/diff-so-fancy
$ sudo apt update

$ sudo apt install diff-so-fancy

# verify
$ diff-so-fancy --version
Diff-so-fancy: https://github.com/so-fancy/diff-so-fancy
Version      : 1.4.2

utility

[!NOTE|label:references:]

$ elinks https://google.com
  • configure

    [!NOTE|label:references:]

    $ cat ~/.elinks/elinks.conf
    set connection.ssl.cert_verify = 0

[!NOTE|label:references:]

  • install

    $ git clone https://github.com/babarot/enhancd && source enhancd/init.sh
    
    # or
    $ curl -L git.io/enhancd | sh
  • re-mapping cmd

    diff --git a/init.sh b/init.sh
    index 55a9c95..bc3ae89 100644
    --- a/init.sh
    +++ b/init.sh
    @@ -52,8 +52,8 @@ if [[ ! -f ${ENHANCD_DIR}/enhancd.log ]]; then
       touch "${ENHANCD_DIR}/enhancd.log"
     fi
    
    -# alias to cd
    -eval "alias ${ENHANCD_COMMAND:=cd}=__enhancd::cd"
    +# alias to ce
    +eval "alias ${ENHANCD_COMMAND:=ce}=__enhancd::cd"
    
     # Set the filter if empty
     if [[ -z ${ENHANCD_FILTER} ]]; then
  • usage

    $ brew install fzy
    # debine
    $ sudo apt install fzy
    
    $ export ENHANCD_FILTER="fzf --height 35%:fzy"
    $ source /path/to/enhancd/init.sh
    $ ce .

tmux

[!NOTE|label:references]

Last updated