When using Ctrl+R to search bash history, is there some way to match two words that aren't next to each other. For example, let's say I wanted to find the last command that contained httpd
and awk
but not next to each other. Is there some better option than resorting to grepping the bash history to match both or pressing Ctrl+R to cycle through entries matching one keyword until the desired command is found?
2 Answers
Bash Reference Manual says:
Readline provides commands for searching through the command history for lines containing a specified string
You want a regexp instead of fixed string.
Try hstr:
1. Installation
2. Configuration
You should see something like this with hstr
:
Update:
Another tool is fzf.
Install:
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf && ~/.fzf/install
Re-source your
~/.bashrc
:source ~/.bashrc
You should see something like this with fzf
:
I dont know of any built-in way to do this, but you can write a small function to do the grep and edit your history file so that the results appear at the end, and you can then easily choose one of the matches by just moving up through the new history. (This does change the order of commands in your history).
Here's such a function. I'm assuming you want to find the 2 (or more) words no matter which order they appear in. You can set the max number of matches you want to accept; here it is 10. I'm also assuming that the initial grep doesnt return a humungous string, or you'll need to use temporary files instead of bash strings.
hist(){
local maxmatches=10 word=$1 matches new nummatches
shift
history -w # write out history to HISTFILE
matches=$(grep -e "$word" $HISTFILE)
for word
do new=$(echo "$matches" | grep -e "$word") || return # if str too big
matches=$new
done
nummatches=$(echo "$matches" | wc -l)
echo "$nummatches matches"
if [ $nummatches -gt 0 -a $nummatches -le $maxmatches ]
then echo "$matches" >>$HISTFILE # add matches to end of file
history -c # clear history
history -r # read history from file
history $nummatches # show last few lines
else echo "zero or too many matches. (max $maxmatches)"
fi
}
If you dont want regexp pattern matching for your words, use fgrep instead of grep. If you want only the given order, use a single word like "httpd.*awk". You could also remove the for loop if only using one word in this way.