Bash & the POSIX Shell Module 13 – The interactive shell
History, Ctrl-R and readline editing
Reuse commands with Ctrl-R and history designators, set history size, duplicates and timestamps, and edit the command line with readline in emacs or vi mode.
What you will learn
- Reuse past commands with Ctrl-R, !!, !$ and the history list
- Configure history size, duplicate handling and timestamps
- Edit the command line efficiently with readline keys and an inputrc
Before you start
What you’ll learn · Before you start
What you will learn
- Reuse past commands with Ctrl-R, !!, !$ and the history list
- Configure history size, duplicate handling and timestamps
- Edit the command line efficiently with readline keys and an inputrc
Before you start
On this page
You ran a 90-character rsync command ten minutes ago and need it again. Retyping it invites a typo; scrolling up
through dozens of lines is slow. Bash keeps a history list of every command, and a few keystrokes bring any of them
back exactly.
Here you learn to search and reuse history with Ctrl-R and the history designators !!, !$ and !n, to set how
much history is kept and what is left out of it, and to move around a command line with readline, the line editor
built into Bash.
Reuse commands with Ctrl-R and history expansion
Press Ctrl-R and type a few letters: Bash searches your
history list: The numbered list of commands Bash keeps for the session and saves to the history file; browse it with the history builtin and search it with Ctrl-R. backwards and shows the most recent command that contains them, which you
run with Enter or edit first. For quick reuse without searching, Bash offers
history expansion: Shorthand starting with ! (such as !!, !$ and !n) that Bash replaces with a past command or part of one before running the line; interactive shells only., words starting with ! that the shell replaces before running
the line. This example feeds a sequence to an interactive Bash and prints what each designator did:
from session import run
# History expansion in an interactive Bash reading a file of commands: !! repeats the last command, ^old^new fixes a
# typo in it, !$ is its last word and !1 re-runs entry 1. The history list stores each command in its expanded form.
run("""
printf '%s\\n' 'echo alpha beta gamma' '^beta^BETA' '!!' 'echo "last arg: !$"' '!1' 'history 6' > demo.bash
bash --norc --noprofile -i demo.bash 2>/dev/null
""") Output
$ printf '%s\n' 'echo alpha beta gamma' '^beta^BETA' '!!' 'echo "last arg: !$"' '!1' 'history 6' > demo.bash
$ bash --norc --noprofile -i demo.bash 2>/dev/null
alpha beta gamma
alpha BETA gamma
alpha BETA gamma
last arg: gamma
alpha beta gamma
1 echo alpha beta gamma
2 echo alpha BETA gamma
3 echo alpha BETA gamma
4 echo "last arg: gamma"
5 echo alpha beta gamma
6 history 6
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 history_expansion_session.py
echo alpha beta gamma ran first. ^beta^BETA re-ran it with the first beta changed to BETA, printing
alpha BETA gamma. !! repeated that whole command, !$ stood for its last word (gamma), so
echo "last arg: !$" printed last arg: gamma, and !1 re-ran entry 1 by its number. The history list at the end
shows each command stored in its expanded form.
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- Type a command. You run echo alpha beta gamma. It goes into the history list as entry 1, and becomes the "last command" for the next designator.
- Fix a typo in the last command. ^beta^BETA re-runs the previous command with the first beta changed to BETA. It printed alpha BETA gamma, so a quick typo fix needs no retyping.
- Repeat the last command. !! stands for the whole previous command, now echo alpha BETA gamma, and runs it again. Common as sudo !! to repeat a command with sudo.
- Reuse its last word. !$ is the last word of the previous command, here gamma, so echo "last arg: !$" printed last arg: gamma. !^ is the first argument.
- Run an entry by number. !1 re-runs history entry 1, echo alpha beta gamma. history shows the numbers, and !-2 counts back from the current line.
A few designators cover most use: !! the last command (often sudo !!), !$ its last argument, !^ its first,
!n entry number n, !-2 two back, and !abc the most recent command starting with abc. History expansion works
only in interactive shells, so a script is never surprised by a !.
An exclamation mark in double quotes
In an interactive shell, echo "it works!" can fail, because !" starts a history expansion. Use single quotes,
echo 'it works!', or a backslash. Scripts are not affected, as history expansion is off there.
Outside that one quoting trap, the designators are safe and save a lot of typing once they are habit.
Key idea
Ctrl-R searches history as you type, and the ! designators (!!, !$, !n) pull a past command or one of its
words into the current line before it runs.
Checkpoint
Check: Ctrl-R and history designators
Shape your history: size, duplicates and timestamps
By default the history file can fill with near-duplicates and secrets. A few variables, set in ~/.bashrc, shape
what is kept. The most useful is HISTCONTROL=ignoreboth, which drops a command that repeats the one before it and
any command that begins with a space:
from session import run
# HISTCONTROL=ignoreboth drops a command that repeats the one before it and a command that starts with a space. Both
# commands still run; they just do not go into the history list. The leading space is the way to keep a secret out.
run("""
printf '%s\\n' 'HISTCONTROL=ignoreboth' 'echo build' 'echo build' ' echo token=secret' 'echo deploy' 'history 5' > ctrl.bash
bash --norc --noprofile -i ctrl.bash 2>/dev/null
""") Output
$ printf '%s\n' 'HISTCONTROL=ignoreboth' 'echo build' 'echo build' ' echo token=secret' 'echo deploy' 'history 5' > ctrl.bash
$ bash --norc --noprofile -i ctrl.bash 2>/dev/null
build
build
token=secret
deploy
1 HISTCONTROL=ignoreboth
2 echo build
3 echo deploy
4 history 5
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 history_control_session.py
Both echo build lines ran, but the repeat was not stored, so the history list holds echo build once. The
space-prefixed echo token=secret ran and printed its output, yet never entered the list: starting a command with a
space is the usual way to keep a one-off secret out of history. echo deploy was stored normally.
Setting (in ~/.bashrc) |
What it does |
|---|---|
HISTSIZE=5000 |
how many commands are kept in memory for this session |
HISTFILESIZE=5000 |
how many lines are kept in the history file on disk |
HISTCONTROL=ignoreboth |
skip duplicates and commands that start with a space |
HISTTIMEFORMAT='%F %T ' |
show a date and time before each entry in history |
shopt -s histappend |
append to the history file on exit instead of overwriting it |
shopt -s histappend matters when you keep several terminals open: without it, the last one to close overwrites the
file with only its own history, losing the rest. With it, each shell appends, so no session’s commands are thrown
away.
Checkpoint
Check: Shaping the history
Edit the line with readline
Everything you type at the prompt is handled by readline: The line-editing library inside Bash that handles typing at the prompt, key bindings and history search; configured in ~/.inputrc and shared with other programs., the line editor inside Bash. Its default keys come from the Emacs editor, so moving and deleting is fast once the keys are habit:
| Key | What it does |
|---|---|
| Ctrl-A / Ctrl-E | jump to the start / end of the line |
| Alt-B / Alt-F | move back / forward one word |
| Ctrl-W / Ctrl-U | delete the word before the cursor / to the start of the line |
| Ctrl-K | delete from the cursor to the end of the line |
| Ctrl-Y | paste back what you last deleted |
| Ctrl-R | search history as you type |
Prefer vi keys? set -o vi switches readline to a vi-style mode, with a command mode reached by Escape. Readline is
configured in ~/.inputrc: for example, set completion-ignore-case on makes filename completion ignore case, and
you can bind keys to actions such as searching history with the up and down arrows. Because readline is a separate
library, these settings also apply to other programs that use it, such as python3 and psql.
Set the mode where every shell sees it
Put set -o vi (or your readline set lines) in ~/.bashrc so every interactive shell starts the same way.
Changes to ~/.inputrc take effect in new shells, or at once with Ctrl-X then Ctrl-R, which re-reads the file.
Checkpoint
Check: Readline editing
Practise: history and readline
Each question shows a history list, a setting or a readline key like the ones above. Decide what happens, then check your answer:
Practice that adapts to you
Practise this lesson
7 questions about 3 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher to 2–5 years): how do you rerun your last command, and reuse just its last argument?
!! expands to the whole previous command, so sudo !! repeats it with sudo in front. !$ is the last word of the
previous command, handy as mkdir reports && cd !$. Both are history expansion, done by the shell before the line
runs, and both work only in an interactive shell. To find an older command, press Ctrl-R and type part of it; Bash
shows the most recent match, which you run with Enter or edit first. The history list is numbered, so !n re-runs
entry n, and history shows the numbers.
Two harder questions follow, about a history file that keeps losing commands and about a password that ended up in the history. Decide what you would check first, then open each first step:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to mid
A developer keeps several terminals open all day, and by evening most of the morning's commands are missing from history. Each terminal seemed fine on its own. What is happening, and how do you fix it?
-
Mid to senior
A code review finds an API token in a developer's shell history, pasted once as an argument to a command. Explain how to remove it and how to keep secrets out of history in future.
Flashcards · 5 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
How do you find and rerun an older command at the prompt?
Press Ctrl-R and type part of it: Bash shows the most recent matching command as you type, and Enter runs it.
-
What do !!, !$ and !n expand to?
!! is the whole previous command, so sudo !! repeats it with sudo; !$ is its last word, as in mkdir reports && cd !$; and !n is command number n of the history list.
-
Where does history expansion with ! happen?
Only in interactive shells, before the line runs. An ! in a script is harmless, but at the prompt an ! inside double quotes needs care.
-
What do HISTCONTROL=ignoreboth and shopt -s histappend change?
ignoreboth keeps out of the history a command that repeats the one before it and every command that starts with a space, and histappend makes each terminal append to the history file instead of overwriting the others.
-
Which keys does readline use to edit the command line, and where is it configured?
Emacs keys by default, or vi keys after set -o vi. ~/.inputrc configures readline for Bash and every other program that uses the library.
Key takeaways
- Ctrl-R searches the history list as you type, and
!!,!$and!npull a past command or a word of it into the current line before it runs. - History expansion works only in interactive shells, so an
!in a script is harmless but an!in double quotes at the prompt needs care. HISTCONTROL=ignorebothskips duplicates and space-prefixed commands, andshopt -s histappendkeeps several terminals from overwriting each other’s history.- Readline edits the line with Emacs keys by default (
set -o vifor vi keys), and~/.inputrcconfigures it for Bash and every other program that uses readline.
Terms in this lesson
- history expansion
- Shorthand starting with ! (such as !!, !$ and !n) that Bash replaces with a past command or part of one before running the line; interactive shells only.
- history list
- The numbered list of commands Bash keeps for the session and saves to the history file; browse it with the history builtin and search it with Ctrl-R.
- readline
- The line-editing library inside Bash that handles typing at the prompt, key bindings and history search; configured in ~/.inputrc and shared with other programs.
References
- Bash Reference Manual: Using History Interactively (Chet Ramey (GNU Bash maintainer))
- Bash Reference Manual: Bash History Facilities (Chet Ramey (GNU Bash maintainer))
- Bash Reference Manual: Command Line Editing (Chet Ramey (GNU Bash maintainer))
- Bash Reference Manual: Readline Init File (Chet Ramey (GNU Bash maintainer))
Report a problem with this lesson
Kept only in this browser. Your Learn progress