The previous five lessons have given you the arsenal of commands; this one teaches you to wield it fast. It may look like a minor topic next to permissions or pipes, but it is not: in operations, the difference between someone who types every command from scratch and someone who reuses, corrects and chains in seconds is measured in hours of work per week. When srv-veloz-01 is down at three in the morning and you need to iterate over a chain of filters until you find the cause, that speed stops being a convenience and becomes real response capability.
Contents
- The command history and
history - History expansion:
!!,!$and company - Incremental search with
Ctrl-R - Configuring the history:
HISTSIZE,HISTCONTROLand friends - Do not leave passwords in your history
- Line editing with readline
- Tab completion
- Job control: first contact
- Aliases for the Veloz Envíos day-to-day
- The command history and
history
historyBash stores every command you run in a numbered list, which it dumps into ~/.bash_history when the session closes.
Those numbers are the key to reusing commands. Common operations on the history:
| Command | Effect |
|---|---|
history |
Lists the whole numbered history |
history 20 |
Only the last 20 |
history | grep chmod |
Search earlier commands containing chmod |
history -d 514 |
Delete one specific entry (for example, one that carried a key) |
history -c |
Clear the history of the current session |
history -a |
Flush to the file right now, without waiting for the session to close |
A detail that surprises people: the history is written when the session closes, and if you have several terminals open, the last one to close can overwrite what the others wrote. The solution is in section 4.
- History expansion:
!!, !$ and company
!!, !$ and companyBash can insert pieces of earlier commands into the current line through history expansion, which is triggered with !.
| Shortcut | Inserts |
|---|---|
!! |
The whole previous command |
!n |
Command number n from the history |
!-2 |
The second-to-last command |
!str |
The last command that started with str |
!?str? |
The last command that contained str |
!$ |
The last argument of the previous command |
!^ |
The first argument of the previous command |
!* |
All the arguments of the previous command |
The star use of !! is recovering a command that failed for lack of privileges:
systemctl restart veloz-api # → Failed to restart veloz-api.service: Access denied
sudo !! # expands to: sudo systemctl restart veloz-apiAnd !$ saves you retyping long paths, which is where most mistakes are made:
ls -l /srv/veloz/data/archive/2026/08/shipments-2026-08-03.csv
head -3 !$ # head -3 /srv/veloz/data/archive/2026/08/shipments-2026-08-03.csvThere is also the quick substitution ^old^new, which repeats the previous command changing the first occurrence of a string:
It is perfect for fixing a typo without rewriting the line. A safety tip: with destructive commands, add the :p modifier (for print), which shows the expanded command instead of running it and leaves it in the history ready to be recalled with the up arrow. That way, !rm:p shows you the last command that started with rm without actually launching it.
- Incremental search with
Ctrl-R
Ctrl-RExpansion with ! is powerful, but it requires remembering. Ctrl-R does not: it opens an incremental backwards search that shows the match as you type.
| Key | Action |
|---|---|
Ctrl-R |
Start the search, or jump to the previous match |
Ctrl-S |
Go to the next match (forwards) |
Enter |
Run the command found |
Right arrow or Ctrl-E |
Edit it before running |
Ctrl-G |
Cancel and go back to an empty line |
This is probably the shortcut you will use the most times in your whole professional life: typing three letters of a pipeline you wrote two days ago and getting it back whole.
A classic warning about Ctrl-S: in many terminals that combination is captured by XON/XOFF flow control and freezes the screen instead of searching. If that happens to you, Ctrl-Q unfreezes it. To free Ctrl-S permanently, add stty -ixon to your ~/.bashrc.
- Configuring the history:
HISTSIZE, HISTCONTROL and friends
HISTSIZE, HISTCONTROL and friendsThe behavior of the history is tuned with environment variables defined in ~/.bashrc (lesson 01-02):
| Variable | What it controls | Recommended value |
|---|---|---|
HISTSIZE |
Commands kept in memory during the session | 10000 |
HISTFILESIZE |
Commands kept in the file ~/.bash_history |
20000 |
HISTCONTROL |
Which commands are not stored | ignoreboth |
HISTIGNORE |
Command patterns to exclude | "ls:cd:pwd:history:exit" |
HISTTIMEFORMAT |
Timestamp on each entry | "%F %T " |
HISTCONTROL accepts three values that can be combined with ::
ignoredups: does not store a command if it is identical to the previous one.ignorespace: does not store commands that start with a space.ignoreboth: both at once. This is the usual option.
HISTTIMEFORMAT deserves special attention because it changes the output of history by adding the date of each command:
514 2026-08-03 11:22:41 chmod 600 ~/veloz-ops/etc/veloz-ops.conf 515 2026-08-03 11:23:05 ls -l ~/veloz-ops/bin/
Knowing when you ran something is decisive when reconstructing an incident: it lets you correlate your actions with the timestamps in app.log.
And the configuration that solves the several-terminals problem:
shopt -s histappend # append to the file instead of overwriting it
PROMPT_COMMAND='history -a' # flush after each command, not on closeWith those two lines in your ~/.bashrc, each terminal adds its commands to the shared file immediately and none of them tramples the others.
- Do not leave passwords in your history
This section is short and critical. Everything you type ends up in ~/.bash_history, a plain text file. If you type mysql -u veloz -pMySecretKey123, that password is saved on disk, synced into the backups, and visible to anyone who gets into your account. Three defenses:
# 1. Prefix a space (requires HISTCONTROL with ignorespace)
curl -H "Authorization: Bearer TOKEN123" https://api.veloz.local/envios
# 2. Delete the entry if you already typed it
history -d 514 && history -a
# 3. Better still: never have the credential on the command line
source ~/veloz-ops/etc/veloz-ops.conf # the file with 600 permissions from lesson 02-03Notice the leading space on the first line: it is deliberate, and it only works if HISTCONTROL includes ignorespace. The third option is the right one in the long run, and it is the reason why in 02-03 we protected veloz-ops.conf with 600 permissions: secrets live in a restricted file, not on the command line.
- Line editing with readline
Bash uses the readline library to edit the current line, with the same shortcuts as Emacs. No more moving character by character with the arrows:
| Shortcut | Action |
|---|---|
Ctrl-A / Ctrl-E |
Go to the beginning / end of the line |
Alt-B / Alt-F |
Move back / forward one word |
Ctrl-U |
Delete from the cursor to the beginning |
Ctrl-K |
Delete from the cursor to the end |
Ctrl-W |
Delete the previous word |
Alt-D |
Delete the next word |
Ctrl-Y |
Paste the last thing deleted with U, K or W |
Ctrl-_ |
Undo the last change |
Ctrl-L |
Clear the screen without losing the line you were writing |
Ctrl-X Ctrl-E |
Open the current line in your editor ($EDITOR) |
The three that really change your life:
Ctrl-W+Ctrl-Ywork as cut and paste: you delete a long path, type something else and bring it back wherever you want.Ctrl-Uis the correct way to abort a half-written line without losing the prompt, and it also serves to avoid running by mistake a dangerous command you have already typed.Ctrl-X Ctrl-Eis the emergency exit when the pipeline you are composing no longer fits on one line: it opens invimornano, you edit it comfortably and when you save and quit, Bash runs it.
- Tab completion
Tab completes what you are typing. If there is a single possibility, it completes it entirely; if there are several, it completes the common part and waits.
Double Tab shows all the possible options when there is ambiguity. Pressing Tab twice on ls .../shipments-2026-08-0, Bash lists the three matches (shipments-2026-08-01.csv, -02 and -03) and leaves your line intact so you can keep typing.
Completion is not limited to paths: at the start of the line it completes command names (including those in ~/veloz-ops/bin, thanks to it being in the PATH since 01-02), and it also completes variables ($HIST<Tab>) and users (~jo<Tab>).
By installing the bash-completion package the system learns to complete options and arguments for each program: git che<Tab> proposes checkout, systemctl restart vel<Tab> proposes the services that exist.
Always use Tab, even when you know how to type the path: besides being faster, it is a verification. If Tab does not complete, that path does not exist, and you have just caught a typo before running an rm.
- Job control: first contact
When a command takes too long or you want your prompt back without killing it, job control comes into play:
| Action | Effect |
|---|---|
Ctrl-C |
Interrupts the foreground process (sends it the SIGINT signal) |
Ctrl-Z |
Suspends the process and gives you the prompt back |
jobs |
Lists the session's jobs and their status |
fg %1 |
Brings job 1 to the foreground |
bg %1 |
Resumes it in the background |
command & |
Launches the command directly in the background |
tail -f /var/log/veloz/app.log
# you press Ctrl-Z → [1]+ Stopped tail -f /var/log/veloz/app.log
bg %1 # keeps watching the log, but gives you the prompt back
jobs # [1]+ Running tail -f /var/log/veloz/app.log &
fg %1 # bring it back so you can stop it with Ctrl-CThe essential distinction is that Ctrl-C terminates the process and Ctrl-Z only pauses it: a suspended job still exists and consumes memory, and if you simply close the terminal it may be left half interrupted. Signals, orphan processes, nohup and serious process management are studied in 05-02.
- Aliases for the Veloz Envíos day-to-day
An alias is a short name for a long command. Defined in ~/.bashrc, they are available in every session:
# --- Navigation, listings and safety net (lesson 02-01) ---
alias ll='ls -lh --color=auto'
alias la='ls -lha --color=auto'
alias ..='cd ..'
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
# --- Veloz Envíos ---
alias vlogs='cd /var/log/veloz'
alias vdata='cd /srv/veloz/data'
alias vops='cd ~/veloz-ops'
alias applog='tail -f /var/log/veloz/app.log'
alias errors='grep -c ERROR /var/log/veloz/app.log'
alias cities='cut -d, -f3 /srv/veloz/data/shipments.csv | tail -n +2 | sort | uniq -c | sort -rn'After editing ~/.bashrc, remember to reload it with source ~/.bashrc (01-02). Check what is behind an alias with type cities, and run the original version by prefixing a backslash: \rm file ignores the rm -i alias.
Two limits worth knowing right away: aliases do not accept arguments in the middle (that is what functions are for, in 04-02) and they do not exist inside scripts, which is exactly why the next module starts where it starts. An alias is a personal shortcut; a script is a tool that can be shared, versioned and scheduled.
Common Mistakes and Tips
- Retyping a long command by hand. If you ever typed it, it is in the history:
Ctrl-Rand three letters. - Leaving credentials in
~/.bash_history. Use the leading space withignorespace, delete the entry withhistory -dand, better still, keep secrets in a file with600permissions. - Losing the history when closing several terminals. Add
shopt -s histappendandPROMPT_COMMAND='history -a'. Ctrl-Sfreezing the terminal. It is flow control;Ctrl-Qreleases it andstty -ixondisables it permanently.- Using
!!without looking. With destructive commands, check first with!!:p. - Confusing
Ctrl-CwithCtrl-Z. The first terminates, the second suspends; suspended jobs are still there. - Depending on defensive aliases like
rm -i. They do not exist in scripts or on other machines. They are a convenience, not a security policy.
Exercises
Exercise 1 — Working without retyping. Starting from ls -l /srv/veloz/data/archive/2026/08/shipments-2026-08-03.csv, achieve with the fewest keystrokes: (a) see the first three lines of that same file; (b) repeat the ls as root; (c) repeat the ls changing 08 for 07 in the first occurrence.
Exercise 2 — A professional history. Write the block you would add to ~/.bashrc so that the history keeps 10000 commands in memory and 20000 in the file, does not record duplicates or commands starting with a space, ignores ls, cd, pwd and history, shows date and time, and is not lost when working with several terminals. Explain each line.
Exercise 3 — Fast diagnosis. veloz-api is running slow and you want, in the same terminal, to watch the log live and at the same time check how many issues there are in the CSV. Describe the sequence of keys and commands, and explain why Ctrl-Z is preferable to Ctrl-C in this scenario.
Solutions
Solution to Exercise 1
head -3 !$ # (a) !$ inserts the last argument of the previous command
sudo !! # (b) !! inserts the whole previous command
^08^07 # (c) repeats the previous one changing the FIRST occurrence of 08Watch out with (c): ^08^07 replaces only the first match. In the path .../2026/08/shipments-2026-08-03.csv the first occurrence of 08 is the directory, so the result would be .../2026/07/shipments-2026-08-03.csv, which probably does not exist. To change every occurrence you need the global modifier: !!:gs/08/07/. It is a good reminder that history expansions are literal and it pays to check them with :p before running something important.
Solution to Exercise 2
HISTSIZE=10000 # commands in memory during the session
HISTFILESIZE=20000 # commands kept in ~/.bash_history
HISTCONTROL=ignoreboth # ignoredups + ignorespace
HISTIGNORE="ls:ll:cd:pwd:history:exit" # noise that adds nothing to the history
HISTTIMEFORMAT="%F %T " # date and time on each history entry
shopt -s histappend # append to the file, do not overwrite it
PROMPT_COMMAND='history -a' # flush after each command, not on closeignoreboth is the double piece: ignoredups stops twenty consecutive ls commands from filling the history, and ignorespace enables the leading-space trick for commands with credentials. histappend and PROMPT_COMMAND go together and solve the classic simultaneous-sessions problem: without them, the last terminal to close overwrites the file with its history and wipes out everyone else's.
Solution to Exercise 3
tail -f /var/log/veloz/app.log # 1. watch the log live
# Ctrl-Z → suspends and gives the prompt back
bg %1 # 2. resume it in the background
grep -c ',issue,' /srv/veloz/data/shipments.csv # 3. query while it keeps running
jobs # 4. check the job's status
fg %1 # 5. bring it back and stop it with Ctrl-C when the time comesWhy Ctrl-Z and not Ctrl-C: Ctrl-C would kill the tail -f, and with it you would lose the continuity of the watch; the lines written while you were not looking would not appear when you relaunched it, because tail -f starts from the file's current end. Ctrl-Z followed by bg keeps the process alive and following the log without interruptions, while you get the prompt back to run the query. The only drawback is that in the background its lines get mixed into your session; in practice, for long watches a second terminal or tmux is preferred, and for processes that must survive the session closing, nohup (05-02).
Conclusion
With this lesson you close Module 2 and, with it, your command-line competence. You know how to reuse the history with !!, !$ and ^old^new, and above all how to search it with Ctrl-R, the shortcut that will save you the most time in your whole career. You have a history configured like a professional's: large, timestamped, free of noise, free of duplicates, safe from simultaneous sessions and —most importantly— free of credentials. You edit the line with readline instead of with the arrows, you use tab completion as a verification as well as a shortcut, you tell suspending a process apart from terminating it, and you have aliases that turn the most repeated Veloz Envíos queries into a single word.
Take stock of the whole module: you create and organize files with judgment; you interrogate app.log, access.log and shipments.csv with grep, cut, sort and uniq; you understand and adjust permissions; you compose filters with redirections and pipes that already write reports into ~/veloz-ops/logs; you select files with wildcards without fear; and you move fast. Everything you needed from the terminal is here.
And that is precisely why the limit arrives. An alias does not accept arguments, a five-filter pipeline cannot be documented or versioned, and nothing you have written today will run tomorrow at seven in the morning without you sitting there. In Module 3 we make the jump: you will save those lines into a file, learn to run it, to give it variables, conditions and arguments, and that chain you type every morning will become the first real command in ~/veloz-ops/bin. Scripting begins.
Bash Programming Course
Module 1: Introduction to Bash
- What Is Bash?
- Setting Up Your Environment
- Basic Command Line Navigation
- Understanding the Shell
- Finding Help: man, help and --help
Module 2: Basic Bash Commands
- File and Directory Operations
- Text Processing Commands
- File Permissions and Ownership
- Redirection and Piping
- Wildcards and Path Expansion
- History and Keyboard Shortcuts
Module 3: Scripting Fundamentals
- Creating and Running a Script
- Variables and Constants
- Basic Operators
- Conditional Statements
- Arguments and User Input
- Quoting, Expansion and Substitution
Module 4: Intermediate Scripting
- Loops in Bash
- Functions in Bash
- Arrays and Associative Arrays
- String Manipulation
- The case Statement and Interactive Menus
- Arithmetic and Numeric Calculations
Module 5: Advanced Scripting Techniques
- Advanced File Operations
- Process Management
- Error Handling and Debugging
- Regular Expressions
- Advanced I/O: Descriptors and Here-Documents
- Modular Scripts and Reusable Libraries
Module 6: Working with External Tools
Module 7: Automation and Scheduling
- Cron Jobs
- Automating Tasks
- Backup and Restore Scripts
- Monitoring and Logging
- Services and Timers with systemd
- Remote Automation with SSH
Module 8: Best Practices and Optimization
- Writing Readable Code
- Optimizing Bash Scripts
- Security Considerations
- Version Control with Git
- Static Analysis with ShellCheck and shfmt
- Automated Testing with Bats
- Portability: POSIX sh versus Bashisms
