The terminal is an invaluable companion for developers. With just a few keystrokes, you can effortlessly execute a multitude of tasks, including creating numerous files and folders at once— a task that might be pretty cumbersome using a graphical user interface (GUI). Take a look at this code:
mkdir folder_{1..50}
It will simultaneously generate 50 folders, labelled from folder_1
to folder_50
.
What of this line?
touch file_{a..z}
It would create 26 files named file_a
through to file_z
.
Imagine you're a photographer looking to arrange your photos captured over the past four years based on their respective months. Instead of manually creating 48 folders, starting from "2018-01" for January 2018 photos and progressing to "2022-12" for December 2022 photos, you could do this with Bash:
mkdir {2018..2022}-{01..12}
Impressive, isn't it?
This is an illustration of Brace Expansion (feel free to explore it further, it's not the focus of this article).
However, in some terminal workflows, repetitive tasks can become not only tedious but also monotonous over time. Certain setups necessitate running specific commands, manually creating files, pasting some code snippets in those files, and executing additional commands (hello, Tailwind CSS) — an incredibly tedious process!
Fortunately, Bash provides aliases, functions, and scripts that empower you to automate terminal operations, boosting your productivity.
Bash scripts are sequences of commands housed in an executable text file. These files don't necessarily need an extension, but if you prefer one, go for a .sh (shell) extension.
When you invoke the file, all the commands within the script run in a top-to-bottom fashion. This means you have to be very mindful of the order in which you write the commands inside a script to ensure the automation follows the desired chronological sequence of actions.
On the other hand, Bash aliases are written in a .bashrc file. As is the case with plain English, an alias functions as a nickname for an existing command. For instance, you can assign the alias go
to the clear
command. Subsequently, whenever you type go
in your terminal, it automatically interprets your intention to execute the clear
command.
To try an alias temporarily, run this command in your terminal:
alias go="clear"
Execute the go
command in your terminal; it performs the same function as the clear
command.
Whenever your automation involves intricate logic, employing a Bash script is advisable. For straightforward logic, aliases or functions are more suitable. The key difference between a bach function and an alias is that functions can accept one or more arguments.
Here are a few of the aliases, functions, and scripts implemented in my environment to enhance productivity:
1. Committing code on GitHub.
One of the initial challenges (if not outright annoyance) I encountered in the git workflow was the repetitive need to execute git add
, git commit
, and git push
individually whenever I wanted to update my code on GitHub. The first script I ever wrote was to automate this process:
#!/bin/bash
git add .
git status
echo "Type your commit message in the text editor, Bob"
git commit
git push
git status
Upon execution, this script initially stages all your unstaged code, launches your text editor and awaits your input for the commit message.
Upon completing the message and exiting the editor, the script proceeds to commit the code and pushes it to your GitHub repository.
I incorporated a git status checker both before and after the commit to verify the cleanliness of the working tree.
2. Cloning a repository
Each time you clone a GitHub repository, the routine of navigating into it and opening it in an editor before starting to code can become a bit tiresome. So, why not automate these last two steps?
Here's a Bash function that simplifies the process:
function clone { git clone "$1" && code "$(basename $1 .git)"; }
export -f clone
This function accepts a single argument (indicated by "$1" within the function), which is the link to the repository you wish to clone in your terminal.
Upon executing this command, it clones the repository and automatically opens it in VS Code (the editor used in this case; replace code
with the name of your preferred editor if it's different, like vi
, emacs
, or nano
). There's no need to cd
into the directory manually because the VS Code instance launched will have an integrated terminal that defaults to the repository.
To use this function, execute the following command in the terminal (as an example):
clone https://github.com/oyieroyier/typescript-brad-traversy.git
3. Navigate quickly to a Directory
One challenge of maintaining a well-organized development structure is the potential nesting of directories within a project.
For instance, within your home directory, you may have a development
directory containing both frontend
and backend
directories.
Within the frontend
directory, there are three subdirectories named frontend_1
, frontend_2
, and frontend_3
.
Now, if you need to access a directory inside frontend_2
called react_projects
, which in turn houses six other directories, the file path can become quite complex.
Here's a visualization:
A novice developer might find it necessary to memorize the precise directory path or navigate through it step by step when working on a React project.
However, employing a Bash script like the one below simplifies this process to just a single command:
#!/bin/bash
cd ~/development/frontend/frontend_2/react_projects/ && ls
Executing this script not only takes you directly to the react_projects directory but also provides a listing of all the files and directories within the final directory of the path. This allows you to quickly identify and choose the specific item you intend to work on.
To run this script, I would place an executable file in my home directory, as that's where my terminal opens by default. If the file is named react
, the command to execute it is a period separated from the filename by a space:
. react
4. Setting up a Rails project.
Configuring a Ruby on Rails project involves numerous optional commands and flags that affect the structure of your Rails application. In my approach to Rails projects, I opt to handle the git setup manually, especially when incorporating the Rails API into an existing mono-repository.
Further, I prefer initializing all my projects with Active Model Serializers right from the start. This means that whenever I execute rails g resource
, it automatically generates serializers— which is not Rails' default behaviour.
Additionally, I always add the Faker Gem to generate dummy data during development and lastly omit JavaScript during the installation process.
The script provided below does this for me:
#!/bin/bash
echo "Enter name of your Rails project."
echo -e "Follow the Rails naming conventions:\n\t1. A short but decriptive project name.\n\t2. Project name must be in all lowercase.\n\t3. No underscores. Use hyphens to separate multi-word project names."
read project_name
rails new $project_name --skip-javascript --skip-git
cd $project_name
bundle add active_model_serializers
bundle add faker
code .
rails s
For this script, I also added a helpful paragraph which reminds me to follow the Rails naming conventions any time I create a project. Being a full-stack developer, I have on the not-so-rare occasion found myself becoming a victim of context switching which can sometimes bring unexpected and undesirable effects:
5. Creating a React App with Vite
As you might have seen, my script ideas typically stem from what I term "the next natural step" logic- a straightforward approach of contemplating the subsequent actions that naturally occur after a specific task.
I pose the question to myself: "What follows naturally after action A, and can the subsequent actions be reasonably automated alongside the initial one?"
For instance, when creating a React application, the natural sequence of steps would be:
Navigating into the app's directory.
Installing the relevant React dependencies.
Launching the app in a text editor or integrated development environment (IDE).
Run the local server.
Executing additional setup tasks, such as installing React Router Dom or React Icons.
This "next natural step" logic serves as the foundation for identifying opportunities to streamline workflows and enhance efficiency through automation.
When bootstrapping a React app using Vite, I use this script:
function vite { npm create vite@latest "$1" --template react && cd "$(basename $1)" && npm install && npm i react-icons --save-prod && npm install react-router-dom && code . && npm run dev; }
export -f vite
Not only will it perform the actions I previously listed but will also install React Icons and React Router Dom which I always prefer my projects to start with by default.
When called (with strictly one argument), the function will:
Create a Vite App with that name.
cd
into that project directory.Install all default dependencies.
Install React Icons.
Install the latest available version of React Router DOM.
Open the project in VS Code.
Run the local server.
To create a project called soccer
, run the command:
vite soccer
Conclusion
In conclusion, Bash aliases, scripts, and functions are capable of incredibly enhancing a developer's coding experience. By incorporating them into your environment, you not only infuse a sense of enjoyment into your coding but also significantly elevate your productivity.
In programming, automation of repetitive tasks becomes a cornerstone of efficiency, enabling you to focus more on the creative and strategic aspects of your work.
I hope you found this article enlightening and that it inspires you to incorporate these powerful tools, and more, into your coding routine.
If you found value in this content, consider sharing it with your peers and fellow developers.
Happy coding!