Shell aliases on steroids
Tags: shell, bash
Published: 2022-02-10 20:31:56

---
title: Shell aliases on steroids
published: true
description: Use functions to create more powerful aliases
tags: shell,bash
//cover_image: https://direct_url_to_image.jpg
---

Aliases, whether you use Bash, ZSH or any other shell, are a really easy way to improve your day to day workflow. The basic syntax for aliases is the following:

```sh
alias l='ls -al'
```

This syntax is pretty simple, but is unfortunately very limited in what you can do:

- it doesn't let you handle custom parameters. What if you want to add a custom `--json` parameter to `curl` that expands to `--header "Content-Type: application/json"`?
- it doesn't let you add subcommands. What if you want to create a custom `docker sh` subcommand that expands to `docker exec -it container_name sh`?

Well the cool thing is that you can do all of that, simply using shell functions! Yep, nothing new in here, but using functions to create advanced aliases is often disregarded or forgotten.

Anyway, let's see how to implement the above aliases!

Note: The following snippets will use Bash specifically. It should not be too hard to adapt them to other shells.

## Add a custom flag to a command

When using `curl`, I often want to send JSON payloads. To do so, the `Content-Type: application/json` header is usually required by servers. I'd like to be able to write this:

```sh
curl --json -d '{"foo": "bar"}' https://example.com/endpoint
```

instead of this:

```sh
curl -H 'Content-Type: application/json' -d '{"foo": "bar"}' https://example.com/endpoint
```

You can add this custom flag with the following function:

```bash
curl() {
  local -a args;
  for arg in "$@"; do
    case $arg in
    --json) args+=("-H" "Content-Type: application/json") ;;
    *) args+=("$arg") ;;
    esac
  done
  command curl "${args[@]}"
}
```

So what happens in this snippet?

- At first, we create a new `curl` function. It will wrap the already existing `curl` command and we'll be able to modify how it's called.
- Then, we loop on all the arguments passed to the command, and add them to a new `args` array.
- When we encounter the `--json` argument, we push the new arguments `"-H" "Content-Type: application/json"` to our list instead.
- Finally, we call the original `curl` command with the modified list of arguments. Make sure to use `command` in front of `curl` to avoid calling the alias function recursively.

And that's it! There's no magic in here, we just cleverly use some basic Bash building blocks.

Using the same techniques, we can easily decide to replace the `-d` argument with `--json` to get the following final command:

```sh
curl --json '{"foo": "bar"}' https://example.com/endpoint
```

This is left as an exercise to the reader.

## Add a custom subcommand to a command

When using `docker`, it's common to get into a container to explore the file hierarchy. The command to do this operation is the following:

```sh
docker exec -it container_name sh
```

I want to write this instead:

```sh
docker sh container_name
```

The principle is the same as above, you can write a function like this:

```sh
docker() {
  if [ $# -ge 1 ]; then
    case "$1" in
      "sh")
        if [ $# -ne 2 ]; then
          echo '"docker sh" requires 1 argument.'
          echo
          echo "Usage:  docker sh NAME|ID"
          echo
          echo "Start an interactive shell inside a running container"
          return 1
        fi
        command docker exec -it "$2" bash
        ;;
    esac
  fi
  command docker "$@"
}
```

The only difference with the previous script is that we create a new `sh` subcommand that we implement ourselves.
We could trivially implement additional flags (`--help`, `--bash`, etc.) to our new subcommand following the same patterns.

---

This article contains no novel concepts, but will helpfully help you get creative in creating more specific aliases to your workflows.

The main thing to remember is the existence of the `command` built-in that allows you to declare a function with the same name as an existing command, and still be able to use the original command.