Essential Bash Tricks and Shortcuts for Developers
If you spend a lot of time in the terminal, you know that every little shortcut can save you time and make your life easier. Bash is full of hidden gems that can help speed up your work and make things more efficient. In this post, we’ll explore some of these Bash commands and tricks that can help you get things done faster.
Terminal Shortcuts and Tricks
These shortcuts are super important if you want to move around and edit commands quickly while working in the terminal. They can save you a lot of time, especially when you're typing out long commands or jumping between different parts of your workflow. Mastering these will help you navigate through your tasks with less effort, so you can focus on getting things done faster.
Navigate without the arrow keys
While executing commands on the command line, sometimes we miss a part at the beginning, or we forget to add certain flags or arguments towards the end. It's common for users to use the Left and Right arrow keys to move through a command to make edits.
But wait, did you know there’s a better way to do that? We can move the cursor to the beginning of the line with CTRL + a
, and to move the cursor to the end of the line, we can use CTRL + e
. If we want to move the cursor forward one character at a time, we can use CTRL + f
, and if we want to move the cursor backward one character, we can use CTRL + b
. To move the cursor forward one word at a time, use ALT + f
, and to move it backward one word at a time, use ALT + b
.
Let’s see an example.
Let’s say we have typed the following long command:
commit -m "fix: ad new funtionality for user managment"
Here’s what happens:
- First, we realize we forgot to include the
git
command at the beginning. PressCtrl + a
to move the cursor to the start of the line and addgit
. - Now, the command looks like this:
git commit -m "fix: ad new funtionality for user managment"
- Next, we notice two typos in the commit message:
ad
should beadd
.funtionality
should befunctionality
.
PressAlt + b
twice to move the cursor backward tofuntionality
, fix the typo tofunctionality
, and then pressAlt + b
again to jump toad
and fix it toadd
.
Now, your command looks like this:
git commit -m "fix: add new functionality for user management"
Finally, if you want to add the --verbose
flag at the end of the command, press Ctrl + e
to jump to the end of the line. Then, type --verbose
.
The final command will be:
git commit -m "fix: add new functionality for user management" --verbose
Other useful shortcut commands
Here are some additional shortcuts that can help speed up your work:
- Ctrl + u: Clear the line from the cursor to the beginning of the line.
- Ctrl + k: Clear the line from the cursor to the end of the line.
- Ctrl + w: Delete the previous word.
Imagine you’re working on a Git commit, and you've typed out a message, but then you realize that it's full of typos and needs some tweaks. Here’s the command you’ve written:
git commit -m "fix: implementd a cool new feautre in user management"
At this point, you’ve hit a bit of a snag. You spot two things that need fixing:
- The word
implementd
should beimplemented
. - The word
feautre
should befeature
.
You could backspace through the entire message and retype it, but that feels like a waste of time, right? Well, there are some cool keyboard shortcuts that can help speed this process up!
So, you move your cursor right after the word fix:
(maybe using the arrow keys or a shortcut like Ctrl + a
or Alt + b
to get there faster). You realize the whole commit message isn’t great, and you'd like to rewrite it from scratch. Instead of painstakingly hitting the delete key over and over again, just press Ctrl + k
. This magical shortcut will clear everything from the cursor to the end of the line.
In a split second, your command looks like this:
git commit -m "fix:
Now, you can quickly type a fresh and improved commit message without all that typing fatigue:
git commit -m "fix: implemented a cool new feature in user management"
But wait, there’s more! What if while typing, you accidentally typed the wrong word—say, feautre
again—before noticing it? Instead of backspacing each letter, simply move your cursor to the end of the word and press Ctrl + w
. This will delete the entire word before the cursor. In our case, it will remove the word feautre
instantly.
Now you’re ready to replace it with the correct word, feature
, and finish your commit message. Your final, typo-free commit looks like this:
git commit -m "fix: implemented a cool new feature in user management"
CTRL + u will remove everything from the cursor to the beginning of the line. For example, let’s say you have this command:
git commit -m "fix: implemented a cool new feature in user management"
If your cursor is sitting right after the word fix:
, and you press Ctrl + u
, the line will look like this:
implemented a cool new feature in user management"
It cleared everything before the cursor! Now you can type a fresh message from the middle of the command.
These shortcuts can really save time and make your terminal workflow more efficient. Whether you're editing long commit messages or just fixing minor typos, you can speed up the process without losing your patience!
Switching Between Emacs and Vi Keybindings
By default, Bash uses Emacs-style keybindings, but if you’re trying the shortcuts and they’re not working as expected, it’s possible your terminal is using Vi-style keybindings. If that’s the case, you may want to switch back to Emacs-style keybindings for consistency with common Bash shortcuts.
Switching between these two keybinding styles is simple. Let’s see how to configure them:
Emacs-Style Keybindings
To use Emacs-style keybindings, you can configure it by typing the following command:
set -o emacs
With Emacs-style keybindings activated, you can use familiar shortcuts like Ctrl + a
to jump to the beginning of the line and Ctrl + e
to jump to the end of the line. Here’s an example:
Example:
set -o emacs
git commit -m "fix: added feature to app"
# Now use `Ctrl + a` to quickly go to the start and `Ctrl + e` to jump to the end of the line.
Vi-Style Keybindings
Alternatively, if you prefer Vi keybindings, simply switch to Vi mode by typing:
set -o vi
Once you’re in Vi mode, you can use Vi-style commands to navigate and edit your commands. For example, you can move to a specific word and change it with cw
(change word). Here’s an example:
Example:
set -o vi
git commit -m "fix: added new features to the app"
# Move to the word "features" and type `cw` to change it.
# Now you can replace "features" with the correct word.
Search and Navigation
Being able to search and navigate your command history efficiently is a crucial skill when working in the terminal. As you run commands, your terminal keeps a history of everything you’ve typed, which can save you time when you need to repeat, modify, or reference previous commands. Instead of retyping long commands, you can simply search for them or navigate through your history. Mastering search and navigation commands helps improve your workflow, saves time, and reduces the chance of errors, especially when working with complex or repetitive tasks.
In this section, we'll explore some key techniques to help you quickly search for past commands and move around the terminal history with ease.
Ctrl + r
: Reverse Search in Command History
When working in the terminal, especially in Bash
or Zsh
, it’s common to need to run a command you’ve used before. If you remember part of it but not the exact command, using the up arrow key to scroll through your entire history can be slow and inefficient. Fortunately, there's a faster way: reverse search.
With Ctrl + r
, you can search through your command history and find commands you’ve used in the past. This saves you time, especially when you remember part of the command but can’t recall the full details.
Let’s say you're working on a project and recently ran a git commit
command, but now you need to make an update. Instead of pressing the up arrow multiple times, you can quickly search for the exact command you need with Ctrl + r
.
Here’s a real-life scenario:
- You realize you need to rerun a
git commit
command you typed earlier but you can’t remember the exact message you used. - You press
Ctrl + r
to start the reverse search, and typegit
. - The terminal instantly shows the most recent command that includes
git
:git commit -m "fix: added new login functionality"
- This is close, but you need to update the commit message, so you press
Ctrl + r
again and typeadded new
. Now the terminal shows:git commit -m "fix: added new user authentication"
- This is almost perfect, but you realize that the word
authentication
isn’t quite right. You want to change it toauthorization
. - Instead of retyping the entire command, press the right arrow key to move the cursor, make the edit, and then hit Enter.
At this point, you've found your previous command, edited it, and run it again in just a few steps!
Now, if at any point during the reverse search, you decide you don’t need the command you found, you can exit reverse search without executing it. For example, let's say you're halfway through searching and you realize you need a completely different command:
You press Ctrl + g
to cancel the reverse search and return to the command prompt. This clears the search and allows you to start fresh.
This way, Ctrl + r
helps you quickly navigate through your command history, and Ctrl + g
ensures that you can exit search mode anytime you want to start over.
Miscellaneous Bash Hacks
Bash offers more than just basic commands—it’s a powerful tool that can significantly boost your productivity once you know how to leverage its lesser-known features. In this section, we’ll explore a few useful tricks and shortcuts that can streamline your workflow, save time, and make your work in the terminal more efficient. Whether you’re tackling complex tasks or just looking to improve your daily tasks, these Bash hacks will help you get the job done faster and with less effort.
!!
: Repeat the Last Command
The !!
command is a quick and efficient way to repeat the last command you ran without retyping it. It's especially useful when you need to repeat a command with slight modifications or add something like sudo
at the beginning.
Let’s say you just ran a command, like:
git push origin main
but you forgot to use sudo
for permissions. Instead of retyping the whole command, you can simply type sudo !!
and it will run:
sudo git push origin main
Here’s another example:
You’ve been working on a project and just ran a Python script like:
python my_script.py
But then you realize you forgot to activate the virtual environment. To fix this, activate the environment first and then repeat the previous command by typing:
source env/bin/activate && !!
Now it’ll run the same command as before, but this time with the virtual environment active:
(env) python my_script.py
!n
: Run Command at Line Number n
The !n
command allows you to run a specific command from your command history by specifying its line number. This is helpful when you want to repeat a command without scrolling or searching through your history manually.
How to Find the Line Number (n
):
To see the line numbers of the commands you’ve run, you can type history
in the terminal. It will show a list of your previous commands along with their line numbers.
For example:
history
This will display a list like:
1 git clone https://github.com/example/project.git
2 cd project
3 vim app.py
4 python app.py
5 sudo apt-get update
6 sudo apt-get install curl
Let's see an example
Let’s say you're working on a project and you've just run the following command to start your app:
python app.py
Then, you realize you forgot to activate your virtual environment before running the script. So, you go ahead and activate the environment first with:
source env/bin/activate
Now, instead of typing out the python app.py
command again, you can find its line number from the history
command output, which shows that the python app.py
command was line number 4.
Now, to run it again with the virtual environment active, just type:
!4
This will instantly run the 4th command from your history (python app.py
), this time in the correct environment. No need to scroll or retype the command—saving you time!
!string
: Repeat the Last Command That Starts with string
The !string
shortcut allows you to rerun the most recent command that started with a specific string. It’s perfect for recalling long commands without manually typing or searching through your history.
Imagine this workflow:
npm run build --prod
You just ran a production build for your application. But then you realize you forgot to clean up your old build files before running the build. Instead of typing out the whole npm run build --prod
command again, you can use !npm
to quickly repeat it:
rm -rf dist/
!npm
This will execute:
npm run build --prod
As a result, you’ve cleaned your build directory and re-ran your production build without retyping the entire command.
!$
: Repeat the Last Argument of the Previous Command
The !$
shortcut allows you to quickly reuse the last argument from the previous command. It's especially handy when you need to apply the same file or directory name to another command without retyping it.
Imagine this workflow:
nano /var/log/syslog
You opened a log file for editing. But then, after making your changes, you want to view the same log file with cat
instead of editing it. Instead of retyping the file path, you can use !$
to repeat the last argument:
cat !$
This will execute:
cat /var/log/syslog
As a result, you've used the same file path without needing to type it out again, saving you time and effort.
!!:gs/old/new/
: Modify the Last Command’s Arguments
You might run a long command, and later realize you need to adjust a specific argument or option. Instead of typing the entire command again, you can use the !!:gs/old/new/
shortcut to replace just the part you need.
Let’s say you ran the following command to start a Docker container with a specific port and database name:
docker run -d -p 3000:3000 --name my-app --env DB_NAME=mydb --env DB_USER=root my-app-image
But now, you realize that you made a mistake in the database name and need to change it from mydb
to production_db
. Instead of retyping the entire command, you can simply replace mydb
with production_db
:
!!:gs/mydb/production_db/
This will modify your previous command, changing the database name argument without having to type everything again. The result will be:
docker run -d -p 3000:3000 --name my-app --env DB_NAME=production_db --env DB_USER=root my-app-image
Now, the only thing that changed is the database name, and you didn't need to retype the whole command. This is incredibly helpful when dealing with commands that are long, especially when they involve multiple flags and arguments.
The !!:gs/old/new/
shortcut is a powerful tool that helps you quickly modify specific parts of your last command without needing to retype the entire thing. It's especially useful when you have complex commands with multiple arguments, allowing you to save time and reduce the chance of errors. Whether you're fixing a typo, changing an argument, or adjusting a flag, this shortcut makes command line editing more efficient and streamlined.
More Customization Options
Customizing your Bash environment can make your terminal more efficient and tailored to your needs.
Customizing Your Command Prompt with PS1
and PS2
The PS1
and PS2
variables allow you to customize your terminal prompts, making them more informative and tailored to your workflow. By modifying these variables, you can enhance the appearance of your terminal and make it easier to work with.
PS1
: Customize Your Primary Command Prompt
The PS1
variable defines how the primary command prompt looks in your terminal. It can display details like the current directory, username, or even your Git branch.
Imagine this scenario: You’re juggling multiple projects in your terminal and want your command prompt to help you keep track of where you are and what you’re doing. Here’s how PS1
can save the day:
Let’s say you start with a basic prompt that only shows the $
symbol:
$
You realize this isn’t helpful because you often forget which directory or project you’re working in. To fix this, you decide to add more context to your prompt.
-
Step 1: Add your username and the current directory by customizing
PS1
:export PS1="\u@\h:\w$ "
After running this command, your prompt changes to:
user@hostname:/current/directory$
-
Step 2: You’re working on a Git repository and want to display the current Git branch in your prompt. Update
PS1
to include Git branch information:export PS1="\u@\h:\w \$(git branch 2>/dev/null | grep '^*' | sed 's/^* //')$ "
Now, your prompt looks something like this when you’re inside a Git repository:
user@hostname:/project-directory main$
PS2
: Customize Your Continuation Prompt
The PS2
variable controls how the continuation prompt looks. This is the prompt that appears when a command spans multiple lines (e.g., when a command is incomplete). By default, it’s set to >
, but you can change it to something more noticeable or fun.
Let’s see a practical example:
-
Step 1: You’re writing a long
for
loop in Bash, but the default continuation prompt doesn’t stand out:for file in *.txt > do > echo "$file" > done
-
Step 2: Change
PS2
to something more eye-catching by running:export PS2="... "
Now, when you write a multi-line command, the continuation prompt changes to:
for file in *.txt ... do ... echo "$file" ... done
if you want this change to persist, add the export PS1
and export PS2
command to your ~/.bashrc
file.
Command Chaining
Command chaining allows you to run multiple commands in sequence or control when a command should run based on the success of another command. Here’s how to use different chaining operators in bash:
Use ;
to run commands sequentially: command1; command2
The semicolon (;
) allows you to execute multiple commands sequentially, one after the other, regardless of whether the previous command was successful or not.
Example:
mkdir my_directory; cd my_directory; touch myfile.txt
This will execute the following:
- Create a directory called
my_directory
. - Change the working directory to
my_directory
. - Create an empty file named
myfile.txt
insidemy_directory
.
Even if one of these commands fails (for example, if my_directory
already exists), the next command will still be executed.
Use &&
to run the second command only if the first succeeds: command1 && command2
The double ampersand (&&
) allows you to run the second command only if the first command succeeds (returns an exit status of 0).
Example:
mkdir my_directory && cd my_directory && touch myfile.txt
This will execute the following:
- Create a directory called
my_directory
. - Change into
my_directory
if the directory was created successfully. - Create an empty file named
myfile.txt
only if the directory change (cd
) was successful.
If any command in this chain fails, the subsequent commands will not be executed.
Redirect standard output to a file: command > output.txt
You can redirect the standard output of a command (stdout) to a file using the >
operator. This will overwrite the file if it already exists.
Example:
echo "Hello, World!" > output.txt
This will write the string Hello, World!
to a file named output.txt
. If output.txt
already exists, it will be overwritten.
Append output to a file: command >> output.txt
If you want to append the output of a command to a file instead of overwriting it, use the >>
operator.
Example:
echo "New line added" >> output.txt
This will append the text New line added
to the output.txt
file, leaving its previous content intact.
Redirect standard error: command 2> error.txt
You can redirect the standard error (stderr) of a command to a file using the 2>
operator. This is helpful when you want to capture error messages separately.
Example:
ls non_existing_directory 2> error.txt
This will attempt to list a directory that doesn’t exist, and the error message (ls: cannot access 'non_existing_directory': No such file or directory
) will be written to error.txt
.
By mastering these chaining and redirection techniques, you can significantly improve your command-line workflow, streamline scripts, and manage outputs and errors more efficiently.
Useful Command-Line Features and Techniques
Alias
An alias allows you to create a shortcut for a command or a series of commands. This is especially helpful when you frequently use long or complex commands, as you can replace them with a simple alias.
Example:
alias ll='ls -l'
This creates an alias ll
that runs the command ls -l
. Now, instead of typing ls -l
every time you want to list files with detailed information, you can simply type ll
.
You can view all your current aliases with:
alias
To remove an alias, you can use:
unalias ll
fc
(Fix Command)
The fc
(fix command) command is used to open and edit the last executed command. It can be useful when you need to correct or modify a command you just ran.
Example:
fc
This will open the last executed command in your default text editor (usually vi
or nano
). After editing, saving, and exiting, the edited command will be executed again.
You can also specify a range of commands to edit:
fc 10 20
This will open the commands from history numbered 10 through 20 in your editor for editing.
fg
(Foreground)
The fg
command is used to bring a background job to the foreground. This is useful when you've started a command in the background (using &
), and you want to interact with it.
Example:
sleep 100 & # Run the command in the background
fg # Bring it to the foreground
This will run sleep 100
in the background and then bring it to the foreground when you use fg
.
Process Substitution <()
Process substitution allows you to use the output of a command as if it were a file. This is useful when a command expects a file as input but you want to provide the output of another command instead.
Example:
diff <(ls /dir1) <(ls /dir2)
This will compare the file listings of /dir1
and /dir2
by treating the outputs of ls /dir1
and ls /dir2
as files for the diff
command.
Another common use is with commands that accept file inputs, like grep
:
grep "world" <(echo "hello world")
This runs grep
on the output of the echo
command, as if the text "hello world"
were in a file.
Braces {}
Braces {}
are used to group commands or repeat patterns. It's often used to execute multiple commands with similar structures without repeating parts of the command.
Example 1: Running multiple commands in one line
echo "File 1" > file1.txt; echo "File 2" > file2.txt; echo "File 3" > file3.txt
Using braces, we can write this more efficiently:
{ echo "File 1" > file1.txt; echo "File 2" > file2.txt; echo "File 3" > file3.txt; }
Example 2: Generating files with similar names
touch file{1..3}.txt
This will create the files file1.txt
, file2.txt
, and file3.txt
in one command.
Braces can also be used in loops or for creating sequences of commands to execute.
Conclusion
Mastering these Bash tricks and shortcuts will help you work faster and more efficiently in the terminal. As a developer, learning to leverage your shell can improve your productivity and save valuable time. Practice using these commands, and soon you'll be navigating and editing with ease!