fzf - 命令列模糊搜尋神器
fzf是一款使用 GO 語言編寫的互動式的 Unix 命令列工具。
可以用來查詢任何列表內容、檔案、歷史命令、 本機繫結的host、 程序、 Git 分支、程序
等。所有的命令列工具可以生成列表輸出的都可以再通過管道 pipe 到 fzf 上進行搜尋和查詢
專案地址:https://github.com/junegunn/fzf
fzf 使用
fz f預設會從 STDIN 讀入資料,然後將結果輸出到 STDOUT
find * -type f | fzf
使用更快的fd
fd --type f | fzf
fzf預設環境變數
- 環境變數:如下表所示:
name | description | example |
---|---|---|
FZF_DEFAULT_COMMAND |
輸入為 tty 時的預設命令 | export FZF_DEFAULT_COMMAND='fd --type f' |
FZF_DEFAULT_OPTS |
設定預設選項 | export FZF_DEFAULT_OPTS="--height 80% --layout=reverse --inline-info" |
FZF_CTRL_T_COMMAND |
按鍵對映<CTRL-T> |
|
FZF_CTRL_T_OPTS |
按鍵對映<CTRL-T> 選項設定 |
|
FZF_CTRL_R_OPTS |
按鍵對映<CTRL-R> 選項設定 |
|
FZF_ALT_C_COMMAND |
按鍵對映<CTRL-C> 行為設定 |
|
FZF_ALT_C_OPTS |
按鍵對映<CTRL-C> 選項設定 |
- 按鍵繫結:如下表所示:
fzf的安裝指令碼會為 bash,zsh 和 fish 終端設定以下按鍵繫結:
按鍵 | 描述 |
---|---|
CTRL-T |
命令列列印選中內容 |
CTRL-R |
命令列歷史記錄搜尋,並列印輸出 |
ALT-C |
模糊搜尋目錄,並進入(cd |
以zsh為例,.zshrc
source /usr/share/fzf/key-bindings.zsh
預覽視窗
如果使用--preview選項, fzf會自動用外部程式開啟現在條目的檔案, {}會被fzf選中行內容代替
fzf --preview 'cat {}'
高亮輸出:
- bat: https://github.com/sharkdp/bat
- highlight: http://www.andre-simon.de/dok...
- ccat:
- coderay: http://coderay.rubychan.de/
fzf --preview '[[ $(file --mime {}) =~ binary ]] && echo {} is a binary file || (bat --style=numbers --color=always{} || (ccat --color=always {} || highlight -O ansi -l {} || coderay {} || cat {}) 2> /dev/null | head -500'
}
export FZF_CTRL_T_OPTS="--preview '(highlight -O ansi -l {} 2> /dev/null || cat {} || tree -C {}) 2> /dev/null | head -200'"
實用指令碼
以下指令碼新增到fzf.zsh
檔案,.zshrc
中引用
Changing directory
# fd - cd to selected directory
fd() {
local dir
dir=$(find ${1:-.} -path '*/\.*' -prune \
-o -type d -print 2> /dev/null | fzf +m) &&
cd "$dir"
}
# cda - including hidden directories
cda() {
local dir
dir=$(find ${1:-.} -type d 2> /dev/null | fzf +m) && cd "$dir"
}
# cdf - cd into the directory of the selected file
cdf() {
local file
local dir
file=$(fzf +m -q "$1") && dir=$(dirname "$file") && cd "$dir"
}
# cf - fuzzy cd from anywhere
# ex: cf word1 word2 ... (even part of a file name)
# zsh autoload function
cf() {
local file
file="$(locate -Ai -0 $@ | grep -z -vE '~$' | fzf --read0 -0 -1)"
if [[ -n $file ]]
then
if [[ -d $file ]]
then
cd -- $file
else
cd -- ${file:h}
fi
fi
}
Command history
# fh - repeat history
fh() {
eval $( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s --tac | sed -E 's/ *[0-9]*\*? *//' | sed -E 's/\\/\\\\/g')
}
# fh - repeat history
fh() {
print -z $( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s --tac | sed -E 's/ *[0-9]*\*? *//' | sed -E 's/\\/\\\\/g')
}
Replacing eval
with print -z
will push the arguments onto the editing buffer stack, allowing you to edit the command before running it. It also means the command you run will appear in your history rather than just fh
Processes
# fkill - kill process
fkill() {
local pid
pid=$(ps -ef | sed 1d | fzf -m | awk '{print $2}')
if [ "x$pid" != "x" ]
then
echo $pid | xargs kill -${1:-9}
fi
}
# fkill - kill processes - list only the ones you can kill. Modified the earlier script.
fkill() {
local pid
if [ "$UID" != "0" ]; then
pid=$(ps -f -u $UID | sed 1d | fzf -m | awk '{print $2}')
else
pid=$(ps -ef | sed 1d | fzf -m | awk '{print $2}')
fi
if [ "x$pid" != "x" ]
then
echo $pid | xargs kill -${1:-9}
fi
}
Interactive cd
Suggested by @mgild Like normal cd but opens an interactive navigation window when called with no arguments. For ls, use -FG instead of --color=always on osx.
function cdi() {
if [[ "$#" != 0 ]]; then
builtin cd "$@";
return
fi
while true; do
local lsd=$(echo ".." && ls -p | grep '/$' | sed 's;/$;;')
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
builtin cd "$dir" &> /dev/null
done
}
fzf + zsh
配置檔案.zshrc
# fzf
source /usr/share/fzf/key-bindings.zsh
source ~/.config/fzf/fzf.zsh
export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git -E .cache'
export FZF_DEFAULT_OPTS='--height 80% --layout=reverse --preview-window down,30%,border-horizontal --preview "[[ $(file --mime {}) =~ binary ]] && echo {} is a binary file || (ccat --color=always {} || (bat --style=numbers --color=always{} || highlight -O ansi -l {} || cat {}) 2> /dev/null | head -500"'
#export FZF_CTRL_T_COMMAND=$FZF_DEFAULT_COMMAND
export FZF_CTRL_T_COMMAND='fd --type f -t d --hidden --follow --exclude .git -E .cache'
export FZF_ALT_C_COMMAND="fd --type d --hidden --follow"
fzf + ranger
ranger配置檔案commands.py
- 方法一:
class fzf_select(Command):
"""
:fzf_select
Find a file using fzf.
With a prefix argument to select only directories.
See: https://github.com/junegunn/fzf
"""
def execute(self):
import subprocess
import os
from ranger.ext.get_executables import get_executables
if 'fzf' not in get_executables():
self.fm.notify('Could not find fzf in the PATH.', bad=True)
return
fd = None
if 'fdfind' in get_executables():
fd = 'fdfind'
elif 'fd' in get_executables():
fd = 'fd'
if fd is not None:
hidden = ('--hidden' if self.fm.settings.show_hidden else '')
exclude = "--no-ignore-vcs --exclude '.git' --exclude '*.py[co]' --exclude '__pycache__'"
only_directories = ('--type directory' if self.quantifier else '')
fzf_default_command = '{} --follow {} {} {} --color=always'.format(
fd, hidden, exclude, only_directories
)
else:
hidden = ('-false' if self.fm.settings.show_hidden else r"-path '*/\.*' -prune")
exclude = r"\( -name '\.git' -o -iname '\.*py[co]' -o -fstype 'dev' -o -fstype 'proc' \) -prune"
only_directories = ('-type d' if self.quantifier else '')
fzf_default_command = 'find -L . -mindepth 1 {} -o {} -o {} -print | cut -b3-'.format(
hidden, exclude, only_directories
)
env = os.environ.copy()
env['FZF_DEFAULT_COMMAND'] = fzf_default_command
env['FZF_DEFAULT_OPTS'] = '--height=80% --layout=reverse --ansi --preview-window down,30% --preview="{}"'.format('''
(
batcat --color=always {} ||
bat --color=always {} ||
cat {} ||
tree -ahpCL 3 -I '.git' -I '*.py[co]' -I '__pycache__' {}
) 2>/dev/null | head -n 100
''')
fzf = self.fm.execute_command('fzf --no-multi', env=env,
universal_newlines=True, stdout=subprocess.PIPE)
stdout, _ = fzf.communicate()
if fzf.returncode == 0:
selected = os.path.abspath(stdout.strip())
if os.path.isdir(selected):
self.fm.cd(selected)
else:
self.fm.select_file(selected)
- 方法二:
# fzf
lass fzf_select(Command):
"""
:fzf_select
Find a file using fzf.
With a prefix argument select only directories.
See: https://github.com/junegunn/fzf
"""
def execute(self):
import subprocess
import os.path
if self.quantifier:
# match only directories
command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
-o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m"
else:
# match files and directories
command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
-o -print 2> /dev/null | sed 1d | cut -b3- | fzf +m"
fzf = self.fm.execute_command(command, universal_newlines=True, stdout=subprocess.PIPE)
stdout, stderr = fzf.communicate()
if fzf.returncode == 0:
fzf_file = os.path.abspath(stdout.rstrip('\n'))
if os.path.isdir(fzf_file):
self.fm.cd(fzf_file)
else:
self.fm.select_file(fzf_file)
ranger配置檔案rc.conf
設定快捷鍵
# fzf
map <C-f> fzf_select
✎﹏鍵落驚風雨,碼成泣鬼神~~