Why this matters
Most of the typing in a terminal session is repeated. You run the same command with a different file name, you need a long command you ran last week, or you type the same folder path five times in one afternoon. Three features remove that repetition. History keeps a list of the commands you typed. Tab completion finishes names for you. An alias gives a long command a short name.
Lesson one showed the basic keys, and lesson two showed Tab for folder paths. This lesson covers history in more depth, Tab for command names and names with spaces, and aliases, which that first lesson only mentioned in passing.
Your command history
The shell keeps a list of every command you run. This list is the history. To see it, type history:
$ echo one
one
$ echo two
two
$ ls /nonexistent
ls: cannot access '/nonexistent': No such file or directory
$ history
1 echo one
2 echo two
3 ls /nonexistent
4 historyEach line has a number. The history survives a restart of the terminal. Bash saves it to a file called ~/.bash_history when the session ends and reads it back when the next session starts. (The name starts with a dot, so a plain ls hides it. Lesson two shows ls -a.) Zsh does the same with ~/.zsh_history.
Re-running by number
Type an exclamation mark followed by a number to run that entry again. Using the list above:
$ !2
echo two
twoBash prints the command it is about to run (echo two) before the output. That line is your check: if the printed command is not the one you wanted, press Ctrl+C and nothing runs.
Type !! to run the previous command again. A common use is sudo !!, which runs the previous command again with sudo in front. sudo lets a command run with administrator rights, and lesson five explains it. Bash prints the expanded command before it runs, so you can read it first.
The numbers in the history belong to the list you see right now. Run history before you use !n, because a number you remember from earlier may point at a different command.
Searching with Ctrl+R
Numbers are easy to get wrong. Ctrl+R (reverse search) lets you find a command by typing part of it. Here is a practice run, with two earlier commands in the history: echo beta-one and echo beta-two.
- Press Ctrl+R. The prompt changes to
(reverse-i-search), which means the shell is waiting for search text. - Type
beta. The shell shows the most recent command that containsbeta. In this practice run that isecho beta-two. You do not press Enter yet. - Press Enter. The command runs and prints
beta-two. To change it first, press the Left arrow instead. That stops the search and leaves the command on the line, unrun.
If you want the older match, press Ctrl+R again to move back through earlier matches. Press Ctrl+G to cancel the search and return to the normal prompt.
A common mistake is to press Enter on a match you wanted to edit. Enter runs the match at once, so press the Left arrow first.
Worked example: change one word in an old command
Suppose you ran echo first draft of notes earlier, and now you want the same command with report at the end instead of notes. You do not need to retype the line:
$ echo first draft of notes
first draft of notes- Press Ctrl+R and type
first. - Press the Left arrow. The whole old command is now on the line, unrun.
- Press Ctrl+E. This moves the cursor to the end of the line.
- Press Backspace five times to delete
notes, then typereport. - Press Enter. The command runs:
first draft of reportCtrl+E moves to the end of a line, and Backspace deletes the character before the cursor. Both are useful after a search, because the cursor does not start at the end of the old command.
Tab completion
Completion means the shell finishes a name you have partly typed. You type the start, press Tab, and the shell fills in the rest. It works on folder and file names (lesson two) and on command names. It also prevents typos, because the shell only fills in names that exist.
The shell reacts to Tab in one of three ways:
- One match: the shell fills in the whole name. A folder gets a slash at the end. A command or file gets a space after it.
- Several matches: the shell fills in the part they all share. Press Tab a second time to list every match.
- No match: nothing changes. Check the spelling, or look at the folder with
ls.
Here is a folder with several matches. /etc holds configuration files, and several of them start with host. Your list may differ:
$ ls /etc/host<Tab><Tab>
host.conf hostname hosts hosts.allow hosts.deny
$ ls /etc/hostThe first Tab finds several matches and shows nothing new. The second Tab prints the list, and the line is redrawn with what you typed. Type one more letter, n, and press Tab once. Only hostname starts with hostn, so the shell completes the name:
$ ls /etc/hostn<Tab>
$ ls /etc/hostnameCommand names complete the same way. Type ech and press Tab, and the shell completes it to echo and adds a space after it.
Worked example: a folder with similar names
Suppose the current folder holds report-2025-01, report-2025-02, report-final and notes. Watch what Tab does at each step:
$ ls report-f<Tab>
$ ls report-final/Only one name starts with report-f, so the shell completes it. The slash at the end shows that it is a folder.
$ ls report-2<Tab>
$ ls report-2025-0Two names start with report-2025-, and they differ at the next character. The shell fills in the part they share, report-2025-0, and stops. Type 1 and press Tab again, and the name completes to report-2025-01/. This is the normal pattern: the shell adds as much as it can be sure of, and you add the rest.
$ cd note<Tab>
$ cd notes/Here is the rule to take from these three steps. Tab completes what the shell is sure of. When it stops early, type one more letter that tells the candidates apart, then press Tab again.
Names with spaces
A file or folder name with a space in it is treated by the shell as two words, unless the space is escaped. Tab handles this for you. Suppose you have a folder called Field notes:
$ ls Field<Tab>
$ ls Field\ notes/The backslash tells the shell that the space belongs to the name. You can also wrap the whole name in quotes, such as ls "Field notes/". Both forms refer to the same folder. Tab is the easier choice because you do not have to remember the rule.
Aliases: short names for long commands
An alias is a short name that the shell replaces with a longer command. You define one with the word alias, a name, an equals sign, and the command in quotes:
$ alias ll='ls -lh'
$ type ll
ll is aliased to `ls -lh'
$ ll -d /etc
drwxr-xr-x 112 root root 4.0K Nov 5 10:04 /etcThe -h flag makes ls print sizes such as 4.0K instead of raw byte counts. The date and link count on the /etc line depend on your machine. The quotes around ls -lh are needed because the command contains spaces. Without them, the shell would read -lh as a second alias to define.
The type command explains what a name is. Here it tells you that ll is an alias and shows what it stands for. Use type any time a name behaves in a way you did not expect.
When you run an alias with extra words, the shell adds those words at the end of the long command. So ll -d /etc ran ls -lh -d /etc. The alias cannot place words in the middle. Example 2 below shows how to handle that case.
Listing and removing aliases
Type alias on its own to see every alias you have defined in this session:
$ alias greet='echo hello'
$ alias
alias greet='echo hello'Remove one with unalias:
$ greet Anisha
hello Anisha
$ unalias greet
$ greet Anisha
bash: greet: command not foundOnce the alias is removed, the shell looks for a real program called greet, does not find one, and reports "command not found" (lesson one explains this search).
Aliases exist only on your machine
An alias is part of your own shell setup. It is not part of the computer. If you write ll in a note for a teammate, their shell does not know it:
$ ll -d /etc
bash: ll: command not foundWrite the real command in shared notes, such as ls -lh, and keep aliases for your own typing. The same is true of a script you share: it should not depend on your aliases.
Keeping an alias after you close the window
An alias you type at the prompt lives only in that one shell. Close the window and it is gone. To keep it, put the line in your shell's startup file. For bash that file is ~/.bashrc:
$ echo "alias ll='ls -lh'" >> ~/.bashrc
$ source ~/.bashrc
$ type ll
ll is aliased to `ls -lh'The double quotes around the whole line let the single quotes inside it survive. The >> adds the line to the end of the file. The source command reads the file again, so the alias works in this window without a restart. Lesson 13 explains which startup file each kind of shell reads.
Worked examples, from simple to tricky
Example 1: a short name for a long command
The command du -sh * shows how much space each item in the current folder uses. Suppose you run it often. Give it a name:
$ alias usage='du -sh *'
$ usageThe alias saves typing. The shell replaces usage with du -sh * and runs it.
Example 2: an argument in the middle
Suppose you want a greeting that puts a name inside a sentence:
$ alias welcome='echo hello'
$ welcome Anisha
hello AnishaThe name ends up at the end, after the words of the alias, so the sentence is wrong. An alias cannot put text in the middle of itself. For that you need a function, which is a named block of commands that can take arguments. Inside a function, $1 means the first argument you gave:
$ welcome() { echo "hello $1, welcome to the lab"; }
$ welcome Anisha
hello Anisha, welcome to the lab
$ type welcome
welcome is a function
welcome ()
{
echo "hello $1, welcome to the lab"
}Define a function in ~/.bashrc the same way you define an alias. Use an alias for a short name for a fixed command. Use a function when the input has to go somewhere in the middle.
Example 3: an alias that hides a real program
Some people put options into the name of a common command, so that ls always shows colours:
$ alias ls='ls --color=auto'
$ ls -d /etc
/etcHere --color=auto colours the output when you type in a terminal. The shell now runs the alias every time you type ls. To run the real program without the alias, put a backslash in front of the name, or use command:
$ \ls -d /etc
/etc
$ command ls -d /etc
/etcBoth forms skip the alias. The output is the same here because the example has only one result. In a terminal, the plain ls shows colours and the \ls form does not.
Common mistakes
Putting = with spaces around it. The alias definition has no spaces around the equals sign. With spaces, bash reads each piece as a separate word:
$ alias ll = 'ls -l'
bash: alias: ll: not found
bash: alias: =: not found
bash: alias: ls -l: not foundThe messages say that alias got three words it does not know. The fix is alias ll='ls -l'.
Leaving out the quotes. Without quotes, the shell reads only the first word of the command as the alias body:
$ alias ll=ls -l
bash: alias: -l: not found
$ alias ll
alias ll='ls'The alias ll was created, but it stands for ls alone, and the -l was rejected. The fix is alias ll='ls -l'.
Expecting an alias in a new window. You define ll in one window and then open another window. The new window does not have it. Put the line in ~/.bashrc, as shown above. A script is different again: scripts do not expand aliases by default. The same name fails in a script:
$ bash -c "alias ll='ls -lh'; ll /tmp"
bash: line 1: ll: command not foundThis is not a problem for everyday use at the prompt. It matters when you write scripts and want to use an alias inside them. Use the real command in scripts instead.
An exclamation mark inside double quotes. Bash treats ! as a history command. This happens even inside double quotes, and it happens in your own messages:
$ echo "wow!abc"
bash: !abc: event not found
$ echo 'wow!abc'
wow!abc
$ echo "wow! and more"
wow! and moreThe fix is to use single quotes around text with an exclamation mark. A ! followed by a space is not treated as history, so "wow! and more" works.
Practice
Exercise 1: find a command from ten lines ago
You ran echo hello some time ago, and you want to run it again. You do not remember the number. Name two ways to find it and run it.
Show the answer
Type history to see the numbered list, find the line, and type ! followed by its number. Or press Ctrl+R, type hello, and press Enter to run it.
Exercise 2: what does Tab do?
You type cat /etc/hostn and press Tab. The folder /etc has one file whose name starts with hostn (hostname). What is on the line after Tab?
Show the answer
The line becomes cat /etc/hostname. The shell completes the name because only one match exists. A slash would appear at the end if the match were a folder.
Exercise 3: write an alias
Create an alias called lsa that runs ls -la (a long listing that includes hidden files). Test it on your home folder. Then say which file you would add the line to, so that the alias is available in every new window.
Show the answer
Type alias lsa='ls -la', then lsa ~. To keep it, add the line alias lsa='ls -la' to ~/.bashrc, then run source ~/.bashrc to load it in the current window. The ~ stands for your home folder.
Exercise 4: fix the broken alias
The line alias gs=git status -s gives an error. Write the correct line and say why the fix works.
Show the answer
Write alias gs='git status -s'. Without quotes, the shell defines gs as git and then treats status and -s as more arguments to alias, which reports alias: status: not found and alias: -s: not found. With quotes, the whole command is one value.
Exercise 5: choose alias or function
You want a command mkcd that creates a folder with a name you give it, and then moves into it. Is an alias suitable? If not, what would you write?
Show the answer
An alias is not suitable, because the folder name must go in the middle of the command: mkdir -p NAME and then cd NAME. A function is suitable. Write mkcd() { mkdir -p "$1" && cd "$1"; } in ~/.bashrc. Then mkcd projects creates the folder and moves into it. The && runs the second command only if the first one succeeds.
Summary
historylists the commands you ran.!!runs the last one, and!nruns entry number n.- Ctrl+R searches the history by text, and Enter runs the match. Press the Left arrow first to edit it.
- Tab completes command names and folder names. Two Tabs list the matches, and a name with a space is escaped with a backslash.
- An alias gives a short name to a fixed command, defined as
alias name='command', with quotes around the command. - Extra words after an alias go at the end, so use a function when the input must go in the middle.
- Put aliases and functions in
~/.bashrcto keep them. A script needsshopt -s expand_aliases, and it can use an alias only on a line after the one that defines it.