Skip to content

bin-cli/bin-cli

Repository files navigation

Bin CLI – A simple task/script runner for any language

Bin CLI is a simple task runner, designed to be used in code repositories, with scripts written in any programming language.

It automatically searches in parent directories, so you can run scripts from anywhere in the project tree. It also supports aliases, unique prefix matching and tab completion, reducing the amount you need to type.

It is implemented as a self-contained Bash script, small enough to bundle with your dotfiles or projects if you want to. It only requires Bash 4+ and a small number of coreutils commands, so it should work on almost any Unix-like system (Linux, macOS with Homebrew, etc.). On Windows, it can be used on WSL, Git Bash, Cygwin or MSYS2.

Collaborators / contributors who choose not to install bin can run the scripts directly, so you can enjoy the benefits without adding a hard dependency or extra barrier to entry.

How It Works

A project just needs a bin/ folder and some executable scripts - for example:

repo/
└── bin/
    ├── build
    ├── deploy
    └── hello

The scripts can be written in any language, or can even be compiled binaries, as long as they are executable (chmod +x). To demonstrate, here is a very simple bin/hello shell script:

#!/bin/sh
echo "Hello, ${1:-World}!"

Running Commands

To execute it, run:

$ bin hello
Hello, World!

Any additional parameters are passed through to the script/executable:

$ bin hello Dave
Hello, Dave!

Now you may be thinking why not just run it directly, like this:

$ bin/hello

And that would do the same thing - but Bin will also search in parent directories, so you can use it from anywhere in the project:

$ cd app/Http/Controllers/
$ bin/hello                # Doesn't work :-(
$ ../../../bin/hello       # Works, but is rather tedious to type :-/
$ bin hello                # Still works :-)

Warning

Bin CLI executes arbitrary commands/scripts in the current working directory (or the directory specified by --dir) - the same as if you executed them directly. You should not run commands from untrusted sources.

Listing Commands

If you run bin on its own, it will list all available commands:

$ bin
Available Commands
bin build
bin deploy
bin hello

Subcommands

If you have multiple related commands, you may want to group them together and make subcommands. To do that, just create a subdirectory:

repo/
└── bin/
    └── deploy/
        ├── production
        └── staging

Now bin deploy production will run bin/deploy/production, and bin deploy will list the available subcommands:

$ bin deploy
Available Subcommands
bin deploy production
bin deploy staging

Unique Prefix Matching

Any unique prefix is enough to run a command - so if bin/hello is the only script starting with h, all of these will work too:

$ bin hell
$ bin hel
$ bin he
$ bin h

This also works with subcommands - e.g. bin dep prod might run bin/deploy/production.

If you type a prefix that isn't unique, Bin will display a list of matches instead:

$ bin hel
Matching Commands
bin hello
bin help

Installation

Bin CLI is a single script - simply download bin to anywhere in your $PATH. For example:

mkdir -p ~/.local/bin
wget https://github.com/bin-cli/bin-cli/releases/latest/download/bin -O ~/.local/bin/bin
chmod +x ~/.local/bin/bin

Then test it:

bin --version

If $HOME/.local/bin is not already in your $PATH, so you get command not found, you may need to add it:

echo 'PATH="$HOME/.local/bin:$PATH"' >> ~/.profile
PATH="$HOME/.local/bin:$PATH"
What are the system requirements?

The requirements are minimal:

How to install it system-wide (for all users)?

I recommend installing it in /usr/local/bin/:

sudo wget https://github.com/bin-cli/bin-cli/releases/latest/download/bin -O /usr/local/bin/bin
sudo chmod +x /usr/local/bin/bin
How to download it curl instead of wget?
curl https://github.com/bin-cli/bin-cli/releases/latest/download/bin -Lo ~/.local/bin/bin

Tab Completion

To configure tab completion in Bash, run:

mkdir -p $HOME/.local/share/bash-completion/completions
bin --completion > $HOME/.local/share/bash-completion/completions/bin
How to install tab completion system-wide (for all users)?

I recommend configuring it in /etc/bash_completion.d/:

bin --completion | sudo tee /etc/bash_completion.d/bin >/dev/null
What about other shells (Zsh, Fish, etc)?

Only Bash is supported at this time. I will add other shells if there is demand for it, or gladly accept pull requests.

Per-Project Setup

In the root of the repository, create a bin/ directory. For example:

mkdir bin

Then create some scripts inside it, in the language of your choice, using the text editor of your choice:

nano bin/sample

And make them executable:

chmod +x bin/*

Which creates this directory structure:

repo/
└── bin/
    └── sample

That's all there is to it. Now you can run them:

bin sample

Upgrading

To upgrade to the latest version at any time, just repeat the same wget command as above.

If you want to be notified when a new version is released, watch this repo (select Watch > Custom > Releases, or Watch > All Activity if you prefer).

Uninstalling

To remove it again, just delete the bin script and tab completion script, for example:

rm ~/.local/bin/bin ~/.local/share/bash-completion/completions/bin

Other Features

Aliases

You can define aliases by creating symlinks between scripts within the bin/ directory - for example:

$ cd bin
$ ln -s deploy publish

Which creates this directory structure (symlink targets indicated by ->):

repo/
└── bin/
    ├── deploy
    └── publish -> deploy

This means bin publish is an alias for bin deploy. Aliases are listed alongside the commands when you run bin with no parameters (or with a non-unique prefix). For example:

$ bin
Available Commands
bin artisan (alias: art)
bin deploy (aliases: publish, push)

Be sure to use relative targets, not absolute ones, so they work in any location. (Absolute targets will be rejected to prevent mistakes.)

If a symlink points to a script outside bin/, or a script within a subdirectory, it will be displayed as a regular command.

Can I define aliases for commands that have subcommands (i.e. directories)?

Yes - for example, given this directory structure:

repo/
└── bin/
    ├── deploy
    │   └── live
    └── push -> deploy

bin push would be an alias for bin deploy, so bin push live would be an alias for bin deploy live, and so on.

How do aliases affect unique prefix matching?

Aliases are checked when looking for unique prefixes. In this example:

repo/
└── bin/
    ├── deploy
    ├── publish -> deploy
    └── push -> deploy
  • bin pub would match bin publish, which is an alias for bin deploy, which runs the bin/deploy script
  • bin pu would match both bin publish and bin push - but since both are aliases for bin deploy, that would be treated as a unique prefix and would therefore also run bin/deploy

Custom Script Directory

You can override the directory name at the command line:

$ bin --dir scripts

This is mostly useful to support repositories you don't control. You will probably want to use an alias such as:

alias scr='bin --exe scr --dir scripts'

Or perhaps a wrapper function:

bin() {
    if [[ $PWD/ == /path/to/repo/* ]]; then
        command bin --dir scripts "$@"
    else
        command bin "$@"
    fi
}

You can also use an absolute path - for example, you could put your all generic development tools in ~/.local/bin/dev/ and run them as dev <script>:

alias dev="bin --exe dev --dir $HOME/.local/bin/dev"

Environment Variables To Use in Scripts

Bin will set the environment variable $BIN_COMMAND to the command that was executed, for use in help messages:

echo "Usage: ${BIN_COMMAND-$0} [...]"

For example, if you ran bin sample -h, it would be set to bin sample, so would output:

Usage: bin sample [...]

But if you ran the script manually with bin/sample -h, it would output the fallback from $0 instead:

Usage: bin/sample [...]

There is also $BIN_EXE, which is set to the name of the executable (typically just bin, but that may be overridden).

Aliasing the bin Command

If you prefer to shorten the script prefix from bin to b, for example, you can create an alias in your shell's config. For example, in ~/.bashrc:

alias b='bin --exe b'

The --exe parameter is used to override the executable name used in the environment variables ($BIN_COMMAND, $BIN_EXE) and the list of commands:

$ b
Available Commands
b hello

You can skip it (i.e. use alias b='bin') if you prefer it to say bin.

Alternatively, you can use a symlink
$ ln -s bin ~/.local/bin/b
How to use tab completion with a custom alias?

Add --exe <name> to the command and update the filename to match - for example:

mkdir -p ~/.local/share/bash-completion/completions
bin --completion --exe b > ~/.local/share/bash-completion/completions/b

If you are using an alias with a custom script directory, add the same parameter here:

mkdir -p ~/.local/share/bash-completion/completions
bin --completion --exe scr --dir scripts > ~/.local/share/bash-completion/completions/scr

# Using single quotes around $HOME here so it's not expanded until runtime:
bin --completion --exe dev --dir '$HOME/.local/bin/dev' > ~/.local/share/bash-completion/completions/dev

Automatic Exclusions

Files/directories starting with . (dot / period) are always ignored and cannot be executed with Bin.

Files that are not executable (not chmod +x) are not listed, nor considered for unique prefix matching or tab completion, and will error if you try to run them.

The directories /bin, /snap/bin, /usr/bin, /usr/local/bin, $HOME/bin and $HOME/.local/bin are ignored when searching parent directories, because they are common locations for global executables (typically in $PATH).

CLI Reference

Usage: bin [OPTIONS] [--] [COMMAND] [ARGUMENTS...]

Options that can be used with a command:
  --dir DIR             Specify the directory name to search for (absolute or relative path)
  --exe NAME            Override the executable name displayed in the command list

Options that do something special:
  --completion          Output a tab completion script for the current shell
  --help, -h            Display this help
  --version, -v         Display the current version number

Any options must be given before the command, because everything after the command will be passed as parameters to the script.

For more details see https://github.com/bin-cli/bin-cli/tree/main#readme

License

MIT License

About

A simple task/script runner for any programming language

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •  

Languages