我正在寻找一种支持简单正则表达式(或仅支持多个匹配)的反向增量搜索工具。例如,如果我想查找命令“foo bar baz”,我可以执行以下操作来快速找到该命令:
CRTL-R(开始搜索)输入“foo”(匹配使用 foo 的最新命令)继续输入“foo|baz”(匹配包含“foo”和“baz”的最新命令。
有这样的事吗?如果没有,我该如何自己实现它?
答案1
请注意,这个答案已经过时了, Sergey Romanovsky 给出了更好的答案。我无法删除这个,因为它已被标记为已接受,但请注意,它现在更多地作为 zsh 小部件编程的基本说明。
自定义小history-incremental-multi-search
部件zsh
设置
创建一个目录并将其包含在您的$fpath
例如,我创建了一个目录~/.zsh/functions
,并且fpath=($HOME/.zsh/functions $fpath)
我的.zshrc
.
history-incremental-multi-search
将以下内容放入该目录中命名的文件中。
emulate -L zsh
setopt extended_glob
local oldbuffer=$BUFFER
local -i oldcursor=$CURSOR
local dir # search direction
local chars # input buffer
local -a words # search terms
local -a found # all history items that match first term
local -i hindex=$HISTNO # current
local -i lmatch # last matched history item (for prev/next)
if [[ $WIDGET == *forward* ]]; then
dir=fwd
else
dir=bck
fi
function find-next {
# split the input buffer on spaces to get search terms
words=(${(s: :)chars})
# if we have at least one search term
if (( $#words )); then
# get all keys of history items that match the first
found=(${(k)history[(R)*$words[1]*]})
if (( $#found )); then
# search in widget direction by default
# but accept exception in $1 for "prev match"
search-${1:-$dir}
else
# no matches
lmatch=$HISTNO
fi
else
# no search terms
lmatch=$HISTNO
BUFFER=$oldbuffer
CURSOR=$oldcursor
fi
}
function search-fwd {
# search forward through matches
local -i i
for (( i = $#found; i > 0; i-- )); do
# but not before hindex as we're searching forward
if [[ $found[$i] -gt $hindex ]]; then
set-match $found[$i]
fi
done
}
function search-bck {
# search backward through matches
local -i i
for (( i = 1; i <= $#found; i++ )); do
# but not beyond hindex as we're searching backward
if [[ $found[$i] -lt $hindex ]]; then
set-match $found[$i]
fi
done
}
function set-match {
# match history item against all terms and select it if successful
local match=1
local -i i
for (( i = 2; i <= $#words; i++ )); do
if [[ $history[$1] != *$words[$i]* ]]; then
match=0
break
fi
done
if [[ $match -ne 0 ]]; then
lmatch=$1
BUFFER=$history[$1]
CURSOR=$#BUFFER
break
fi
}
# display sub prompt
zle -R "${dir}-i-search-multi:"
# handle input keys
while read -k; do
case $REPLY in
# next
$'\C-n' )
hindex=$lmatch
find-next
;;
# prev
$'\C-p' )
hindex=$lmatch
if [[ $dir == fwd ]]; then
find-next bck
else
find-next fwd
fi
;;
# break
$'\e' | $'\C-g' )
BUFFER=$oldbuffer
CURSOR=$oldcursor
break
;;
# accept
$'\C-m' | $'\C-j' )
if [[ $lmatch -eq $HISTNO ]]; then
BUFFER=$oldbuffer
CURSOR=$oldcursor
else
HISTNO=$lmatch
fi
break
;;
# erase char
$'\C-h' | $'\C-?' )
chars=$chars[1,-2]
hindex=$HISTNO
find-next
;;
# erase word
$'\C-w' )
if [[ $chars =~ \ ]]; then
chars=${chars% *}
else
chars=
fi
hindex=$HISTNO
find-next
;;
# kill line
$'\C-u' )
chars=
hindex=$HISTNO
find-next
;;
# add unhandled chars to buffer
* )
chars=${chars}${REPLY}
hindex=$HISTNO
find-next
;;
esac
zle -R "${dir}-i-search-multi: $words"
done
将其放入或从您的来源获取.zshrc
:
autoload -U history-incremental-multi-search
# make new widgets from function
zle -N history-incremental-multi-search-backward history-incremental-multi-search
zle -N history-incremental-multi-search-forward history-incremental-multi-search
# bind the widgets to keys
bindkey '^Xr' history-incremental-multi-search-backward
bindkey '^Xs' history-incremental-multi-search-forward
使用
现在您应该能够使用 , 启动向后增量搜索Ctrl+X,使用r, 启动向前增量搜索Ctrl+X。s
输入搜索词,中间用空格隔开。以下键可用于控制它:
← Backspace:删除字符
Ctrl+W:删除单词
Ctrl+U:删除行
Ctrl+N:下一场比赛
Ctrl+P:上一场比赛
Ctrl+G/ Esc:取消搜索
Enter: 接受
这个解决方案可能还可以简化很多。它更像是一个功能性概念验证,还有很大的改进空间。
答案2
你可以通过 grep 来查看你的历史记录:
history | egrep '(foo|baz)'
我希望这能有所帮助。
答案3
基于@peth 的回答:
Zsh 现在自带了history-incremental-pattern-search-backward
,你不需要自己定义它。只需添加键绑定即可。我更喜欢^R
通过在 中添加以下行来覆盖.zshrc
:
bindkey '^R' history-incremental-pattern-search-backward
现在您可以使用全局(原文如此!不是正则表达式)运算符。
答案4
我更喜欢使用 vim,例如
vim -u /root/.vimrc -M + <(history)
这种方式的优点是:搜索时会显示整个历史列表,并且 vim 的正则表达式引擎非常好。
但请注意:每次在历史列表中搜索时,vim 通常会在退出时写入 .viminfo 文件。这可能不是我们想要的。可以通过 vim 选项 -i NONE 来防止这种情况,例如
vim -u /root/.vimrc -i NONE -M + <(history)