How to create and use custom shell commands?

Article autor
September 9, 2025
How to create and use custom shell commands?
Elixir Newsletter
Join Elixir newsletter

Subscribe to receive Elixir news to your inbox every two weeks.

Oops! Something went wrong while submitting the form.
Elixir Newsletter
Expand your skills

Download free e-books, watch expert tech talks, and explore open-source projects. Everything you need to grow as a developer - completely free.

Table of contents

Each of us had a situation, where we had to invoke a few, same commands each time. Making it once is not that problematic, but when we are supposed to repeat given operations, it may be better just to create one function that will handle it all.

Create own functions

To create our own functions, firstly we need to open a new terminal. By default, we ought to be in the root directory (~), but to be sure you can execute the following command:

cd ~

and hit enter.

Right now, we can create a new file, in which we will store our function with commands. To do it, execute this:

touch .custom_bash_commands.sh

The naming convention is up to you, in this case, the file will be named .custom_bash_commands.sh , where '.' at the beginning means it is a hidden file. To see all of the hidden and visible files, you can just do ls -a.

Once we have created the file, we can open it. To do so, you can use VIM or your favorite text editor. I will be using Visual Studio Code. For that, I will execute the following command in my current directory:

open -a 'Visual Studio Code' .custom_bash_commands.sh

Now, we can create our function by following:

#!/bin/bash

function mix_all(){
  echo "mix format"
  mix format
  echo "mix static.credo"
  mix static.credo
  echo "mix dialyzer"
  mix dialyzer
  echo "mix test"
  mix test
}

The first line is just a convention, which is used while creating Scripts. Next, we have function declaration, which is called mix_all .

We use echo command to print out the following state. Of course, this is just a sample function, you can use whatever you want to up here.

Remember about saving the file now!

Source it and use it!

The next step is to source it into Bash/ZSH main file here. Why? Right now, the function is already created and can be used everywhere on our computer (but, in the following example, we have to have installed all needed dependencies in our project).

If you source it by executing:

source ~/.custom_bash_commands.sh

and invoke the function name:

mix_all

You will be prompted with the output of commands inside our mix_all , each by each.

But, it will be only available for the given directory and current session. If you will try to open a new terminal tab in the same directory and execute the function again, it will fail.

To omit that, let's come back to the root directory and open the .zshrc or .bashrc file (the file is dependent on your current OS version). In my case, I am going to open the .zshrc file:

open -a 'Visual Studio Code' .zshrc

And to make things work, we just need to source the previously created file here:

source ~/.custom_bash_commands.sh

And that's that. Right now we instruct our terminal to load our file on each terminal session. You can obtain your own command from any place you want to, without sourcing it.

Another example

The following function is just a really basic example. Let's create one more function, that will hold other operations. In the root directory, I am going to open the file, which I created at the very beginning. Inside of file, I am going to create another function:

#!/bin/bash

function mix_all(){
  # Previously created function
}

function create_user(){
  echo "User creation function"
  sudo dscl . -create /Users/$1
  sudo dscl . -create /Users/$1 UserShell /bin/bash
  sudo dscl . -create /Users/$1 RealName $2
  sudo dscl . -create /Users/$1 UniqueID $3
  sudo dscl . -create /Users/$1 PrimaryGroupID 1000
  sudo dscl . -create /Users/username NFSHomeDirectory /Local/Users/$1
  sudo dscl . -passwd /Users/username $4
  if dscl . list /Users | grep $1 ;
  then
    echo "User has been created!"
  else
    echo "Failed."
  fi
}

This function will allow us to omit 7 commands of user creation, for just one. The numbers prepend with a dollar sign are variables (eg. $1), and each of them stores a given value. The only things we need to remember are the name of our function and the order of passed arguments.

We do not have to source it now, because we have already done it previously. Just remember about saving the file! In our example, the order is:

function_name user_name users_real_name unique_user_id user_password

Just be sure, that unique_user_id is unique.

To execute the following function and create a user, let's move back to the terminal and just execute:

create_user curiosum Curiosum 1001 JustStayCuorius!

And that's that! For now, the user creation operation is automated. It is just a basic function for that usage, we could handle more sophisticated things here, such as error handler, or password validator.

Summary

In a very undemanding and fast way, we were able to omit invoking four and seven commands for just one function. The following code is just an example, and you can use your own created functions in many more ways! Bash is userfriendly, and helps us a lot in automating our workflow. It's good to get to know it well!

Related posts

Dive deeper into this topic with these related posts

No items found.

You might also like

Discover more content from this category

How to check if a set contains exact values with Jest in JS?

TLDR: With jest-extended package you can write: expect([...set]).toIncludeSameMembers([value1, value2]);. If you are looking to a native, but longer solution scroll down a bit.

Using Logger.info and Logger.debug in ExUnit tests

By default in the test env, Phoenix doesn't show Logger.debug/Logger.info outputs in the console.

Skip file changes tracking in git

So, you’re changing this one file for local development purposes only. Maybe it’s config, maybe some source file, but one thing is certain - you don’t want those changes to be committed. And what’s worse, .gitignore doesn’t work.