zsh-like history in bash - bash

I really like zsh's history autocomplete feature. Namely, when I press up, I get the previous command. When I type emacs and press up, I get the last emacs command I used. When I type git and press up, I get the last git command I used. When I try to do this in bash, it just goes to the last generic command I used. Essentially, I want to be able to half-type a command and press up to get the last command I used that matches what I've typed so far. I don't know how to achieve this in bash. I can't use zsh on this system, so is it possible to replicate this functionality in bash?

The up arrow is bound to the previous-history command. You want to rebind it to history-search-backward (which is unbound by default) instead. You can check which keys previous-history is currently bound to:
$ bind -p | grep previous-history
"\C-p": previous-history
"\eOA": previous-history
"\e[A": previous-history
In my case, the last two both represent up arrow (the exact escape sequence may differ from terminal to terminal, or depending on what mode the terminal is in, but these two are fairly standard). Especially since previous-history will still be available with Control-P, it's safe to change the behavior of the up arrow.
Add this to your .inputrc file (creating the file if necessary):
"\e[A": history-search-backward
"\eOA": history-previous-history
Or, you can add call bind from your .bashrc:
bind '"\e[A": history-search-backward'
bind '"\eOA": history-previous-history'
You may also want to similarly bind history-search-forward to the down arrow key, \e[B and \eOB.

You can search your history with ctrl+R.
When you first press it, the prompt will change and you will be able to enter the characters you want to search for in the history. The new prompt disregards characters that were entered in the previous prompt and will overwrite them when something from the history is matched.
It will display the last command that matches your input and you can press ctrl+R again to navigate the results from the latest to the earliest.
When you've found the entry you're interested in, you can either press Enter to execute it or the left or right arrows to return to the standard prompt to edit the command line. Pressing the up or down arrows will return to the standard prompt but navigate the history one step forward or backward, which I find more confusing than anything.

Related

bash behaviour of pressing <tab> twice for filename completion

As an example, say that I type part of a name:
$ vi partn <= here I press <tab><tab>
partname partnook partnum <= these are the results
This is standard behaviour in bash and that is fine, but I've been working a lot on PowerShell lately where the default behaviour is that if you press tab, it cycles through the names, completing each name and then pressing tab again will go to the next file, so if I pressed tab one time, I see vi ./partname, then press tab again, I see vi ./partnook, then tab again I see vi ./partnum. I tend to prefer this behaviour as it's less typing (you just hit tab to cycle through until you get the filename or command that you want).
Everything is customisable in Linux of course, so can someone advise how I might change the default bash behaviour to follow the above by default?
To persistently make tab-completion not require two Tab keypresses whenever there are multiple matching completions and to instead cycle through them, in-line:
Option A: If you either already have:
an /etc/inputrc file (applies system-wide, modification requires sudo)
and/or a ~/.inputrc file (user-specific)
and/or
you're planning to customize the Readline library extensively and/or want to make customizations effective for scripts too when they call read -e:
Add line:
"\C-i": menu-complete
to either file, depending on whether you want the setting to be effective for all users or the current user (create the file, if necessary).
Note: Alternatively, you can simply turn off the need to press Tab twice and exhibit the standard behavior instantly, which means showing a multi-column list of all possible completions, and then redisplaying the input line, to allow you to type additional characters to either fully complete the argument or to type enough of it so that the next Tab keypress unambiguously completes:
set show-all-if-ambiguous on
Option B: Alternatively, you may add Readline commands to your user-specific initialization file (~/.bash_profile on macOS, ~/.bashrc on Linux and other Unix-like platforms), by passing them as a single argument to the bind builtin:
bind '"\C-i": menu-complete'
If you prefer the show-all-matches behavior:
bind 'set show-all-if-ambiguous on'
Note that bind commands in ~/.bash_profile / ~/.bashrc take precedence over equivalent commands in either /etc/inputrc or ~/.inputrc.
As implied above, configuring Readline this way will not take effect in scripts that call read -e in order to activate Readline support for reading user input.

Paste bash command, but make sure it doesn't run

Sometimes when you ctrl-v with bash it will run the command even though you didn't intend to run it yet - is there a way to paste a command into the bash shell / terminal making sure you don't actually run any of the command(s)?
if you could set what was on the terminal prompt programmatically, you could do this with bash on MacOS:
export BASH_PROMPT="$(pbpaste)"
which ties into my other question that I just asked:
How to change the value that's in the prompt
There is a Readline variable:
enable-bracketed-paste
When set to On, Readline will configure the terminal in a way that will enable it to insert each paste into the editing buffer as a single string of characters, instead of treating each character as if it had been read from the keyboard. This can prevent pasted characters from being interpreted as editing commands. The default is off.
To turn this on, put something like
set enable-bracketed-paste on
into your ~/.inputrc.
This was introduced in Bash 4.4 / Readline 7.0.
Use ^X^E aka Ctrl+X Ctrl+E in bash to open your $EDITOR for command entry.
Paste and/or edit as much as you want, across as many lines as you want. When you're done, save and exit, and bash will run it.
(In vi mode, the shortcut is v)

For further research: terminology about manipulating command before hitting RET in terminal

I just found out in zsh if I have a variable, namely abc="cba", and if I type echo ${!abc} and pressing enter, it doesnt go as command but instead open new prompt below with expanded variable echo ${abc="cba"}.
And probably included that powerful zsh plugin that let us automatically insert sudo just by double tapping Esc.
So for further studying and probably opening new possibilities, what is the right terminology about that typed-command-but-not-entered manipulation?
Thanks...
Those seem to be unrelated. The first is zsh asking you to verify history expansion. The second is a ZLE (Zsh Line Editor) widget with a keybinding to modify the current command line. You can see what this ZLE widget looks like in oh-my-zsh the source code.

Searching your command history on macOS terminal

What is the shortcut to search my command history in macOS terminal?
For how long is the history available for searching? Where is it stored?
How about using Ctrl+R for searching on the Terminal Utility in Mac for searching on the command history,
dudeOnMac: freddy$ whoami
freddy
(reverse-i-search)`who': whoami
Well for controlling how long the history would be retained that depends on a few shell environment variables, HISTFILESIZE which is nothing but number of lines of history you want to retain. Set a huge value for it in .bash_profile for it to take effect
HISTFILESIZE=10000000
Use Ctrl + R for searching a command from history in Terminal.
(reverse-i-search)`':
Type any substring of the command you want to search e.g. grep
(reverse-i-search)`grep': grep "XYZ" abc.txt
It will return the latest command that matches your input. If that is not the command you were searching for, keep pressing Ctrl + R for next match until you find your command.
Once you found your command press Return to execute it.
If you want to exit without running any command, press Ctrl + G
PS: This answer is same as suggested by Inian, just giving more details for easy usage.
The command history is stored under your home folder in a hidden file called .bash_history. To view it's content in nano, use the following command in Terminal:
nano ~/.bash_history
Or open with your text editor (default is TextEdit):
open ~/.bash_history
In my case it's a very long list and as I scroll through seems like the last ~500 command is stored here.
Migrating an answer to SO from this answer on the Unix and Linux Stack Exchange:
Pressing ctrl+R will open the history-search-backward. Now start typing your command, this will give the first match. By pressing ctrl+R again (and again) you can cycle through the history.
If you like to be super lazy you can bind the up/down arrow keys to perform this search, I have the following in my .inputrc to bind the up/down arrow key to history-search-backward and history-search-forward:
# Key bindings, up/down arrow searches through history
"\e[A": history-search-backward
"\e[B": history-search-forward
"\eOA": history-search-backward
"\eOB": history-search-forward
Just type something (optional), then press up/down arrow key to search through history for commands that begin with what you typed.
To do this in .bashrc rather than .inputrc, you can use:
bind '"\e[A": history-search-backward'
Use this command -
history
This works on both OSX and Linux.
History is stored in ~/.zsh_history or ~/.bash_history or ~/.history depending on your shell.
History is stored for 1000 or 2000 lines depending on your system.
echo $HISTSIZE
You can also try the following:
history | grep 'git'
Where 'git' is the command you are looking for.
For those who want to search specific command from history, you can do so with reverse-i-search. Reverse search allow you to type in any key words(any) that is part of the command you are looking for and reverse search navigate back to history, match previous commands incrementally and return the entire command.
It is especially useful as when one cannot remember all handy lengthy commands they use often. To do reverse-search ctrl + R and type any clue you have and that will return your previous commands matching the words you type. Then once found the command, hit Enter to execute it directly from search.
Automation AppleScript
Since you mentioned viewing your history as a quick solution, via the Terminal.app. You might want to automate, or quickly view history, maybe from the dock. You may use the AppleScript application as one alternative. This is an optional approach to create a simple shortcut, as to many others.
Open the AppleScript editor application.
Add your specified commands, for history.
Code
tell application "Terminal"
do script "history"
end tell
Save as application, drag to dock for convenience.
History Storage & Time Stored Details
HISTSIZE Determines how many lines will be written to the history file.
HISTFILESIZE Determines how long the file.
Find out how long history is stored:
echo $HISTSIZE $HISTFILESIZE
Note: You may also increase your command history storage size in the length of two variables. You may achieve this through HISTSIZE and HISTFILESIZE environment variables which are located in your ~/.bash_profile file.
It is possible to achieve this by modifying ~/.bash_profile, the number placeholder with SIZE represent's the number, lines value as example:
export HISTFILESIZE=SIZE # Example 1000
export HISTSIZE=SIZE # Example 10000
Pre macOS 11 Big Sur
cat ~/.bash_history
HISTFILESIZE will only set a maximum history value which is stored to the history file when a session is started. HISTSIZE will determine specifically how many lines will be stored or in other words, written at the end of the session. If the set HISTFILESIZE is determined to be a large value than what HISTSIZE is set, you will not view history larger than your set HISTSIZE. The reason is that the history file is overwritten with the HISTSIZE unless using histappend option turned ON.
You may use also histappend to append history, If the histappend shell option is turned on lines are appended to the history file. Otherwise, the overwritten alternative proceeds.
Bash GNU - histappend
macOS 11 Big Sur
nano ~/.zprofile
Modify history environment variables, set to a value:
export HISTFILESIZE=1000
export HISTSIZE=SIZE=1000
Run the source command can be used to load any functions file into the current shell script or a command prompt.
source ~/.zprofile
echo $HISTSIZE $HISTFILESIZE
Outputs:
1000 1000
Output where some history is stored:
cat ~/.zsh_history
For macOS Big Sur the file is now .zsh_history
If you do vi ~/.zsh_history in the terminal you can use regex by pressing the / and then the search term.
To review or recall recently used commands, you can just press the up arrow key to sequentially read back through the history stored in .bash_history.
To search through history with ease, I advise you to install fzf.
It's an interactive Unix filter for command-line that can be used with any list; files, command history, processes, hostnames, bookmarks, git commits, etc.
Just install it, click ctrl + R, and you'll be to scroll through you shell history, without the need to grep or waiting ages until the command you're waiting for pops up.
It supports Mac OS, Linux and even Windows.
# USAGE: find.history cd
# filter commands in shell history by a search term and execute the selected command
function find.history {
eval $(history | grep "$1" | tail | awk '{$1=""}1' | tail -r | peco)
}
You will need to have peco installed.
https://github.com/peco/peco
[$]> brew install peco

How can I make bash tab completion behave like vim tab completion and cycle through matching matches?

I've been meaning to find a solution for this for YEARS.
I am sooo much more productive in vim when manipulating files than bash for this reason.
If I have
file_12390983421
file_12391983421
file_12340983421
file_12390986421
In bash and type file_1->tab , it obviously lists:
file_12390983421 file_12391983421 file_12340983421 file_12390986421
And this is horrible and painful to work with.
The same sequence in vim will loop through the files one at a time.
Please someone tell me how to do this in bash, or if there is another shell that can do this, I'll switch tomorrow.
By default TAB is bound to the complete readline command. Your desired behavior would be menu-complete instead. You can change your readlines settings by editing ~/.inputrc. To rebind TAB, add this line:
TAB: menu-complete
For more details see the READLINE section in man bash.
For bash >= 4 you might like these settings. You can try them directly on the command-line, and put them in your ~/.bash_profile if you like them.
# If there are multiple matches for completion, Tab should cycle through them
bind 'TAB:menu-complete'
# And Shift-Tab should cycle backwards
bind '"\e[Z": menu-complete-backward'
# Display a list of the matching files
bind "set show-all-if-ambiguous on"
# Perform partial (common) completion on the first Tab press, only start
# cycling full results on the second Tab press (from bash version 5)
bind "set menu-complete-display-prefix on"
This setup is similar to Vim's set wildmode=longest:full:list,full
I pulled these settings from this question on the Unix & Linux site.
By the way, since you are here, here are some other great bindings:
# Cycle through history based on characters already typed on the line
bind '"\e[A":history-search-backward'
bind '"\e[B":history-search-forward'
# Keep Ctrl-Left and Ctrl-Right working when the above are used
bind '"\e[1;5C":forward-word'
bind '"\e[1;5D":backward-word'
This means if you type ssh<Up> it will cycle through previous lines where you ran ssh
If you don't like what you got, you can clear the line with Ctrl-K Ctrl-U
I pulled these settings from this question on AskUbuntu.
On top of
# cycle forward
Control-k: menu-complete
# cycle backward
Control-j: menu-complete-backward
you may also consider adding
# display one column with matches
set completion-display-width 1
This way you would preserve the current Tab functionality and make bash display the possibilities in one column. So instead of
file_12340983421 file_12390983421 file_12390986421 file_12391983421
you would get
file_12340983421
file_12390983421
file_12390986421
file_12391983421
P.S. You can get up to date readline library from this The GNU Readline Library website.
Thanks to #sth I found what works best for me:
To keep normal bash tab completion, and then use ctl-f to cycle through when needed using menu-complete
put this in your .inputrc file:
"\C-f": menu-complete
In my experience, the solution provided in sth's answer has never completely worked for me. TL;DR: Add set -o vi to your ~/.bashrc.
When using menu-complete in conjunction with vi keybindings, I have to make sure that my ~/.bashrc has:
set -o vi
It's never been enough for my ~/.inputrc just to have:
TAB: menu-complete
set editing-mode vi
set keymap vi
My guess is that somehow set editing-mode and set keymap are clobbering the TAB: ... setting, but I haven't looked into the documentation thoroughly to figure out why this is the case.

Resources