{"id":13661593,"url":"https://github.com/qrush/sub","last_synced_at":"2025-12-17T20:23:05.263Z","repository":{"id":4756749,"uuid":"5906854","full_name":"qrush/sub","owner":"qrush","description":"a delicious way to organize programs","archived":false,"fork":false,"pushed_at":"2021-08-24T12:00:22.000Z","size":44,"stargazers_count":1751,"open_issues_count":9,"forks_count":148,"subscribers_count":70,"default_branch":"master","last_synced_at":"2025-04-02T04:56:32.294Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://37signals.com/svn/posts/3264-automating-with-convention-introducing-sub","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qrush.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-09-21T20:16:20.000Z","updated_at":"2025-03-22T21:28:26.000Z","dependencies_parsed_at":"2022-08-17T23:05:19.736Z","dependency_job_id":null,"html_url":"https://github.com/qrush/sub","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qrush%2Fsub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qrush%2Fsub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qrush%2Fsub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qrush%2Fsub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qrush","download_url":"https://codeload.github.com/qrush/sub/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247809964,"owners_count":20999816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T05:01:37.626Z","updated_at":"2025-12-17T20:23:05.215Z","avatar_url":"https://github.com/qrush.png","language":"Shell","funding_links":[],"categories":["Shell"],"sub_categories":[],"readme":"# sub: a delicious way to organize programs\n\nSub is a model for setting up shell programs that use subcommands, like `git` or `rbenv` using bash. Making a sub does not require you to write shell scripts in bash, you can write subcommands in any scripting language you prefer.\n\nA sub program is run at the command line using this style:\n\n    $ [name of program] [subcommand] [(args)]\n\nHere's some quick examples:\n\n    $ rbenv                    # prints out usage and subcommands\n    $ rbenv versions           # runs the \"versions\" subcommand\n    $ rbenv shell 1.9.3-p194   # runs the \"shell\" subcommand, passing \"1.9.3-p194\" as an argument\n\nEach subcommand maps to a separate, standalone executable program. Sub programs are laid out like so:\n\n    .\n    ├── bin               # contains the main executable for your program\n    ├── completions       # (optional) bash/zsh completions\n    ├── libexec           # where the subcommand executables are\n    └── share             # static data storage\n\n## Subcommands\n\nEach subcommand executable does not necessarily need to be in bash. It can be any program, shell script, or even a symlink. It just needs to run.\n\nHere's an example of adding a new subcommand. Let's say your sub is named `rush`. Run:\n\n    touch libexec/rush-who\n    chmod a+x libexec/rush-who\n\nNow open up your editor, and dump in:\n\n``` bash\n#!/usr/bin/env bash\nset -e\n\nwho\n```\n\nOf course, this is a simple example... but now `rush who` should work!\n\n    $ rush who\n    qrush     console  Sep 14 17:15 \n\nYou can run *any* executable in the `libexec` directly, as long as it follows the `NAME-SUBCOMMAND` convention. Try out a Ruby script or your favorite language!\n\n## What's on your sub\n\nYou get a few commands that come with your sub:\n\n* `commands`: Prints out every subcommand available.\n* `completions`: Helps kick off subcommand autocompletion.\n* `help`: Document how to use each subcommand.\n* `init`: Shows how to load your sub with autocompletions, based on your shell.\n* `shell`: Helps with calling subcommands that might be named the same as builtin/executables.\n\nIf you ever need to reference files inside of your sub's installation, say to access a file in the `share` directory, your sub exposes the directory path in the environment, based on your sub name. For a sub named `rush`, the variable name will be `_RUSH_ROOT`.\n\nHere's an example subcommand you could drop into your `libexec` directory to show this in action: (make sure to correct the name!)\n\n``` bash\n#!/usr/bin/env bash\nset -e\n\necho $_RUSH_ROOT\n```\n\nYou can also use this environment variable to call other commands inside of your `libexec` directly. Composition of this type very much encourages reuse of small scripts, and keeps scripts doing *one* thing simply.\n\n## Self-documenting subcommands\n\nEach subcommand can opt into self-documentation, which allows the subcommand to provide information when `sub` and `sub help [SUBCOMMAND]` is run.\n\nThis is all done by adding a few magic comments. Here's an example from `rush who` (also see `sub commands` for another example):\n\n``` bash\n#!/usr/bin/env bash\n# Usage: sub who\n# Summary: Check who's logged in\n# Help: This will print out when you run `sub help who`.\n# You can have multiple lines even!\n#\n#    Show off an example indented\n#\n# And maybe start off another one?\n\nset -e\n\nwho\n```\n\nNow, when you run `sub`, the \"Summary\" magic comment will now show up:\n\n    usage: sub \u003ccommand\u003e [\u003cargs\u003e]\n\n    Some useful sub commands are:\n       commands               List all sub commands\n       who                    Check who's logged in\n\nAnd running `sub help who` will show the \"Usage\" magic comment, and then the \"Help\" comment block:\n\n    Usage: sub who\n\n    This will print out when you run `sub help who`.\n    You can have multiple lines even!\n\n       Show off an example indented\n\n    And maybe start off another one?\n\nThat's not all you get by convention with sub...\n\n## Autocompletion\n\nYour sub loves autocompletion. It's the mustard, mayo, or whatever topping you'd like that day for your commands. Just like real toppings, you have to opt into them! Sub provides two kinds of autocompletion:\n\n1. Automatic autocompletion to find subcommands (What can this sub do?)\n2. Opt-in autocompletion of potential arguments for your subcommands (What can this subcommand do?)\n\nOpting into autocompletion of subcommands requires that you add a magic comment of (make sure to replace with your sub's name!):\n\n    # Provide YOUR_SUB_NAME completions\n\nand then your script must support parsing of a flag: `--complete`. Here's an example from rbenv, namely `rbenv whence`:\n\n``` bash\n#!/usr/bin/env bash\nset -e\n[ -n \"$RBENV_DEBUG\" ] \u0026\u0026 set -x\n\n# Provide rbenv completions\nif [ \"$1\" = \"--complete\" ]; then\n  echo --path\n  exec rbenv shims --short\nfi\n\n# lots more bash...\n```\n\nPassing the `--complete` flag to this subcommand short circuits the real command, and then runs another subcommand instead. The output from your subcommand's `--complete` run is sent to your shell's autocompletion handler for you, and you don't ever have to once worry about how any of that works!\n\nRun the `init` subcommand after you've prepared your sub to get your sub loading automatically in your shell.\n\n## Shortcuts\n\nCreating shortcuts for commands is easy, just symlink the shorter version you'd like to run inside of your `libexec` directory.\n\nLet's say we want to shorten up our `rush who` to `rush w`. Just make a symlink!\n\n    cd libexec\n    ln -s rush-who rush-w\n\nNow, `rush w` should run `libexec/rush-who`, and save you mere milliseconds of typing every day!\n\n## Prepare your sub\n\nClone this repo:\n\n    git clone git@github.com:qrush/sub.git [name of your sub]\n    cd [name of your sub]\n    ./prepare.sh [name of your sub]\n\nThe prepare script will run you through the steps for making your own sub. Also, don't call it `sub`, by the way! Give it a better name.\n\n## Install your sub\n\nSo you've prepared your own sub, now how do you use it? Here's one way you could install your sub in your `$HOME` directory:\n\n    cd\n    git clone [YOUR GIT HOST URL]/sub.git .sub\n\nFor bash users:\n\n    echo 'eval \"$($HOME/.sub/bin/sub init -)\"' \u003e\u003e ~/.bash_profile\n    exec bash\n\nFor zsh users:\n\n    echo 'eval \"$($HOME/.sub/bin/sub init -)\"' \u003e\u003e ~/.zshenv\n    source ~/.zshenv\n\nYou could also install your sub in a different directory, say `/usr/local`. This is just one way you could provide a way to install your sub.\n\n## License\n\nMIT. See `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqrush%2Fsub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqrush%2Fsub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqrush%2Fsub/lists"}