# bash-my-aws: short AWS commands that pipe into each other

> bash-my-aws wraps the AWS command line interface in a set of short, discoverable shell commands, two of them: plural nouns that list resources, and hyphenated action commands. The clever part is not the shortening, it is that the commands read identifiers from standard input, so a list can be filtered and then acted on without anyone typing a resource name.

**mbailey/bash-my-aws** — Bash-my-AWS provides simple but powerful CLI commands for managing AWS resources

- Repository: https://github.com/mbailey/bash-my-aws
- Website: https://bash-my-aws.org/
- Stars: 981 · Forks: 225
- Language: Shell
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/mbailey-bash-my-aws

## Two naming shapes, chosen for completion

The command set has two shapes, and the naming is the interface design.

The first type are resource listing commands, which generally consist of the pluralised form of the resource name. A listing of buckets prints the bucket names with their creation timestamps, a listing of CloudFormation stacks prints the stack name, its status, its creation time and its update and nesting state, and a listing of key pairs prints the name and the fingerprint.

The second type are resource detail and action commands, which generally consist of a resource name and an action separated by a hyphen, so a key pair deletion is one word. The readme gives the reason for the hyphen explicitly: it makes discovering the commands via shell completion simple.

That is a good reason, and it is the kind of detail that separates a tool that stays usable from one that does not. If the actions were invoked with a space, the completion would have to guess where the resource ended and the action began. With a hyphen, tab completion can offer the list of actions for the resource you have typed so far.

The project describes itself as harnessing the power of the AWS CLI while abstracting away verbosity, and as implementing some innovative patterns while remaining simple, beautiful and readable. The verbosity it removes is real: the AWS CLI expresses a listing as a service call with a JSON output query, and this turns it into a noun.

Some of those action commands change things and some only retrieve information, and the readme says so plainly, which is a useful distinction for a tool that makes destructive operations feel like ordinary shell commands.

## Completion queries AWS instead of guessing

The completion behaviour is the part that makes the tool feel more than a wrapper.

In the readme's example, shell autocompletion retrieved the existing EC2 key pair names from AWS. So when you type the beginning of a key pair name, the completion is querying the account and offering the real names, and when you accept one, the command receives a valid identifier rather than something you remembered or pasted from a document.

The stated benefit is avoiding reliance on human memory or terminal copy and paste, and that is exactly the failure mode this tool is aimed at. In an account with a hundred stacks, you do not remember their names, and a long name in a chat message is easy to mistype. Completion fixes the class of problem rather than one instance of it.

There is a second layer. All of the bash-my-aws commands are available as completions for a single command, so pressing tab twice on that command produces the list of every available command. That is the discovery mechanism for the whole tool, and it is worth contrasting with the usual approach of reading a long reference page and hoping you remember the right word later.

The completion scripts are generated rather than written by hand, which the build section of the repository explains, and the generated files are checked into the repository, which is why the Makefile has a check target that fails if they drift out of date with the source.

## The standard input contract that makes the commands compose

This is the mechanism that the readme calls an innovative pattern, and it is a single sentence: the first token from each line of standard input is taken to be a resource identifier, and the rest is discarded.

That rule is what makes the commands work with each other rather than merely with other Unix tools. A listing command prints a table, and the action command reads the first field of every line, so the pipeline below lists the stacks, filters for postgres, and deletes what is left:

```shell
$ stacks | grep postgres | stack-delete
```

The output confirms which resources were selected, and then asks for confirmation before acting. The readme notes that some users have compared the experience to functionality in Windows PowerShell, which is a compliment aimed at people who know what object pipelines feel like.

Two details of the contract are worth internalising. The rest of the line is discarded, so the columns of the listing do not have to be parsed or matched. And the confirmation step is part of the action command, so a piped bulk operation still stops and asks.

That last point is what makes the pattern usable in anger. The most common way a tool like this hurts someone is a command that acts on everything it was handed without a checkpoint, and this one has the checkpoint built into the same command that does the acting.

## A filter argument instead of a grep

The listing commands accept a filter argument, which removes the need to pipe into grep for the common case.

The readme motivates it with a stack whose name is long enough to push the columns out of alignment. When a name is long, the table becomes ragged, and grepping the output of a ragged table is awkward to read and easy to get wrong.

```shell
$ stacks postgres
```

The filter is applied before the output is formatted into columns, so the result is the same set of rows with clean alignment. The readme says the resource listing command can filter the output before applying the column formatting, which is the precise description of what a filter argument buys you.

The column formatting itself is configurable, and the installation snippet shows an environment variable set to make the tool columnise the output only when a terminal is present. That is a small touch with a real purpose, because when the output is being piped into something else, the padding is noise, and when a human is reading it, the padding is the whole point.

Together with the standard input contract, the filter argument completes a coherent philosophy: the output of these commands is meant to be read by a person and by a program, and the tool tries to be legible in both.

## Installing means cloning and sourcing

There is no package to install. You clone the repository, or fork it if you want to keep your own changes, and you source the files.

```Shell
git clone https://github.com/bash-my-aws/bash-my-aws.git ${BMA_HOME:-$HOME/.bash-my-aws}
```

Then three lines go in your shell startup file, one adding the bin directory to the path, one setting the columnise-only-when-terminal-present variable, and one sourcing the aliases file. The completion script is sourced separately, and for zsh users there are two lines to uncomment that initialise the completion system.

```Shell
export PATH="$PATH:${BMA_HOME:-$HOME/.bash-my-aws}/bin"
export BMA_COLUMNISE_ONLY_WHEN_TERMINAL_PRESENT=true
source ${BMA_HOME:-$HOME/.bash-my-aws}/aliases
```

```Shell
source ${BMA_HOME:-$HOME/.bash-my-aws}/bash_completion.sh
```

The prerequisite list is short: the AWS CLI, bash, and jq at version 1.4 or later, and the jq requirement is noted as being for one specific command, the stack diff. Two optional packages handle colour for that command, one for plain colour and one for colour with a side by side diff.

The whole of this is a clone and a couple of sourced files, which is a genuinely different proposition from installing a Python tool with its own virtual environment. For a machine you ssh into, that difference matters.

## Why aliases rather than sourced functions

There is a section of the readme devoted to a design decision that most users would never think about, and it is the most interesting part of the installation instructions.

Bash-my-AWS began as a collection of bash functions sourced into your shell. More recently, the default suggestion has been to load aliases that execute a small wrapper script which loads the functions and executes the desired one.

The reason is shell compatibility. After years of zsh users asking for support, one contributor stepped up and identified a change that would remove any shell compatibility problems without compromising the functionality, the simplicity and the discoverability of the project, and the readme thanks them by name. The practical effect is that the functions no longer live in your interactive shell, so a zsh user and a bash user both get the same behaviour.

Bash users who prefer the old way can still source the functions directly, with a loop over the function files in the lib directory, and the readme includes that snippet so the choice is documented rather than implied.

The other thing worth noting is the discoverability claim. Sourcing a large number of functions into an interactive shell has a cost, because every function name and every completion is loaded at startup and pollutes the namespace. Wrapping them keeps the shell clean and keeps the startup fast, and the completion script is what brings the discovery back.

## The build regenerates the shell files, and the check enforces it

The repository layout explains how the tool is put together, and the Makefile explains how it stays consistent.

The build target runs a build script and reports how many functions and how many aliases were produced, and then it checks whether the generated files differ from what is committed, printing a diff stat if they do. The verbose variant runs the same script without suppressing its output.

The check target is the interesting one. It runs the build and then compares the result with the committed files, printing a message that the generated files are up to date or that they are out of date and should be regenerated, and it exits with a failure in the second case. So a pull request that changes a source file without regenerating the aliases, the functions and the completion script fails the check.

That is a well designed guard for a project whose source of truth is a generator. The clean target reverses it, restoring the generated files from the git head, and the install-hooks target symlinks a pre-push hook into the git hooks directory so the same check can run before a push rather than only in continuous integration.

The test suite is a set of shell specifications, a shared one, one for CloudFormation stacks and one for the parameter store, each run as a script. The rest of the tree is conventional for a documentation project: a docs directory with a mkdocs configuration, a cloudformation directory for the templates the tests use, a contrib directory, a scripts directory, and conventions, a contributor instructions file, a todo file, and a codeowners file.

The project is MIT licensed, the last push was on 2026-06-19, and the repository has no GitHub releases, so the master branch is the thing to install.

## Conclusion

bash-my-aws suits an engineer who lives in the AWS command line and wants the common listings and actions in a form that composes with grep, awk and the rest of the shell, and it is a good way to teach someone the resource model of an account. It is a poor fit if you want reproducible infrastructure, since everything is still one-off commands against the live API, and it is a poor fit if you are on a platform where sourcing shell functions into your profile is unwelcome. Before you install it, read the command reference and check that the commands you need exist, source the alias file rather than the function file if you use zsh, and remember that the destructive commands prompt for confirmation and that the listing commands return whatever your credentials can see.

## FAQ

### What does bash-my-aws need installed before it works?

The AWS CLI, bash, and jq at version 1.4 or later for the stack-diff command. Two optional packages add colour to that command: colordiff for plain colour and icdiff for colour with a side by side diff.

### How do I install bash-my-aws?

Clone the repository into a directory of your choice, or fork it if you want customisations, then add the bin directory to your PATH, source the aliases file, and source the bash_completion.sh file. The readme also shows a loop for bash users who prefer sourcing the function files directly instead of the aliases.

### How do bash-my-aws commands take input from each other?

The first token from each line of standard input is taken as a resource identifier and the rest of the line is discarded. That is why a listing can be filtered and piped straight into an action command, for example stacks piped through grep into stack-delete, with the action still asking for confirmation before it changes anything.

### Does shell completion in bash-my-aws need the resource names typed out?

No. The completion retrieves the existing resource names from AWS, so tab completion offers the real identifiers for the account you are connected to, and all of the bash-my-aws commands are also completions for a single command, so completing that command twice lists every available command.

## Sources

- [Issues](https://github.com/mbailey/bash-my-aws/issues)
- [License: MIT](https://github.com/mbailey/bash-my-aws/blob/master/LICENSE)
- [mbailey/bash-my-aws on GitHub](https://github.com/mbailey/bash-my-aws)
- [Project website](https://bash-my-aws.org/)
- [README](https://github.com/mbailey/bash-my-aws/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mbailey-bash-my-aws
