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.
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}!"
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.
If you run bin
on its own, it will list all available commands:
$ bin Available Commands bin build bin deploy bin hello
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
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
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:
- Bash 4.x or above (macOS users can upgrade via Homebrew)
- Core Utilities, BusyBox or equivalent (specifically
basename
,env
,readlink
,sort
)
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
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.
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
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).
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
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 forbin deploy
, sobin push live
would be an alias forbin 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 matchbin publish
, which is an alias forbin deploy
, which runs thebin/deploy
scriptbin pu
would match bothbin publish
andbin push
- but since both are aliases forbin deploy
, that would be treated as a unique prefix and would therefore also runbin/deploy
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"
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).
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/bIf 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
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
).
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