Git Product home page Git Product logo

sub's Introduction

sub: a delicious way to organize programs

Sub 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.

A sub program is run at the command line using this style:

$ [name of program] [subcommand] [(args)]

Here's some quick examples:

$ rbenv                    # prints out usage and subcommands
$ rbenv versions           # runs the "versions" subcommand
$ rbenv shell 1.9.3-p194   # runs the "shell" subcommand, passing "1.9.3-p194" as an argument

Each subcommand maps to a separate, standalone executable program. Sub programs are laid out like so:

.
├── bin               # contains the main executable for your program
├── completions       # (optional) bash/zsh completions
├── libexec           # where the subcommand executables are
└── share             # static data storage

Subcommands

Each 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.

Here's an example of adding a new subcommand. Let's say your sub is named rush. Run:

touch libexec/rush-who
chmod a+x libexec/rush-who

Now open up your editor, and dump in:

#!/usr/bin/env bash
set -e

who

Of course, this is a simple example... but now rush who should work!

$ rush who
qrush     console  Sep 14 17:15 

You 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!

What's on your sub

You get a few commands that come with your sub:

  • commands: Prints out every subcommand available.
  • completions: Helps kick off subcommand autocompletion.
  • help: Document how to use each subcommand.
  • init: Shows how to load your sub with autocompletions, based on your shell.
  • shell: Helps with calling subcommands that might be named the same as builtin/executables.

If 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.

Here's an example subcommand you could drop into your libexec directory to show this in action: (make sure to correct the name!)

#!/usr/bin/env bash
set -e

echo $_RUSH_ROOT

You 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.

Self-documenting subcommands

Each subcommand can opt into self-documentation, which allows the subcommand to provide information when sub and sub help [SUBCOMMAND] is run.

This is all done by adding a few magic comments. Here's an example from rush who (also see sub commands for another example):

#!/usr/bin/env bash
# Usage: sub who
# Summary: Check who's logged in
# Help: This will print out when you run `sub help who`.
# You can have multiple lines even!
#
#    Show off an example indented
#
# And maybe start off another one?

set -e

who

Now, when you run sub, the "Summary" magic comment will now show up:

usage: sub <command> [<args>]

Some useful sub commands are:
   commands               List all sub commands
   who                    Check who's logged in

And running sub help who will show the "Usage" magic comment, and then the "Help" comment block:

Usage: sub who

This will print out when you run `sub help who`.
You can have multiple lines even!

   Show off an example indented

And maybe start off another one?

That's not all you get by convention with sub...

Autocompletion

Your 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:

  1. Automatic autocompletion to find subcommands (What can this sub do?)
  2. Opt-in autocompletion of potential arguments for your subcommands (What can this subcommand do?)

Opting into autocompletion of subcommands requires that you add a magic comment of (make sure to replace with your sub's name!):

# Provide YOUR_SUB_NAME completions

and then your script must support parsing of a flag: --complete. Here's an example from rbenv, namely rbenv whence:

#!/usr/bin/env bash
set -e
[ -n "$RBENV_DEBUG" ] && set -x

# Provide rbenv completions
if [ "$1" = "--complete" ]; then
  echo --path
  exec rbenv shims --short
fi

# lots more bash...

Passing 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!

Run the init subcommand after you've prepared your sub to get your sub loading automatically in your shell.

Shortcuts

Creating shortcuts for commands is easy, just symlink the shorter version you'd like to run inside of your libexec directory.

Let's say we want to shorten up our rush who to rush w. Just make a symlink!

cd libexec
ln -s rush-who rush-w

Now, rush w should run libexec/rush-who, and save you mere milliseconds of typing every day!

Prepare your sub

Clone this repo:

git clone [email protected]:qrush/sub.git [name of your sub]
cd [name of your sub]
./prepare.sh [name of your sub]

The 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.

Install your sub

So 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:

cd
git clone [YOUR GIT HOST URL]/sub.git .sub

For bash users:

echo 'eval "$($HOME/.sub/bin/sub init -)"' >> ~/.bash_profile
exec bash

For zsh users:

echo 'eval "$($HOME/.sub/bin/sub init -)"' >> ~/.zshenv
source ~/.zshenv

You 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.

License

MIT. See LICENSE.

sub's People

Contributors

kant avatar markstos avatar mlafeldt avatar packagethief avatar qrush avatar spadin avatar underpantsgnome avatar youpinadi avatar z0mbix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sub's Issues

eval "$(sub init -)" in .bash_profile throws errors

When I add the proper line from sub init to my bash_profile it spews the following error:
-bash: eval: line 31: syntax error near unexpected token )' -bash: eval: line 31: )'

This is definitely coming from the sub call. I also tried this format instead, but get the same error.
subb=sub init -
eval "$subb"

I can manually call this sequence and it will not produce the same error.
sub init- > foo; eval < foo; rm foo

This is all on OSX, in case that is relevant.

Auto Documenting Syntax For Node Commands

Not quite sure how to tap in to the auto documentation when creating a node based command. Hash bangs are illegal characters and js comments aren't picked up by sub.

Tried:

#!/usr/bin/env node
// # Usage: diy notify
// # Summary: Send a notification to any maker.
// # Help: Be careful!

and

#!/usr/bin/env node
// Usage: diy notify
// Summary: Send a notification to any maker.
// Help: Be careful!

Support for files that shouldn't be under version control

This is more of a question than an issue, but is the share directory intended for files that may contain machine specific passwords or other files that you wouldn't want to be in git?

If not, do you have a standard way to do this in your own commands?

Your sub can't be called 'sub'

sub fails with the command mv: rename share/sub to share/sub/sub: Invalid argument if you name your sub 'sub'. I'm not sure of your intentions but if that name is not allowed, it should fail early with an error message

Reliable way to display/format an error during autocomplete

One of my scripts requires that the user have a valid properties file in their share directory. I've written a function to check fo the existence of the file and the property but the error is being chopped up during auto-complete. Is there any way to tell the auto-completer to keep my error on one line?

PROPS_FILE=$_SUB_ROOT/share/sub/mysql.propertie
check_for_props_and_load() {
if [ ! -f $PROPS_FILE ];
then
  echo "MySQL properties file not found. Please create a file in share/sub directory with at least a PASSWORD property. You can copy the mysql.properties.default file"
  exit
fi
source $PROPS_FILE
if [ -z "$PASSWORD" ];
then
  echo "Properties file exists but does not contain PASSWORD. Please add a MySQL password to your properties file."
  exit
fi
}

check_for_props_and_load

if [ "$1" = "--complete" ]; then
  echo "show databases;" | mysql -u root --password=$PASSWORD
  exit
fi

Maybe list notable forks?

Why you might want to use `tome` instead

The sub project is stable and effective as it is, but it also several issues that haven't been addressed over the past 10 years and there's been no activity at all for a couple of years.

tome was inspired by sub and addresses several of outstanding issues with sub including:

  • Support for sub-sub-commands
  • Fixed ZSH completion
  • Implemented Fish completion
  • Ability to take advantage of updates to the upstream project (because it doesn't use code-generation).

Tome is written in Rust, which means it starts up and runs faster as well. If the above issues are important to you, tome may be better choice. But there are also some issues with Tome. I've linked to issues discussing their possible resolution with Tome:

Despite the lack of activity in this repo, if the current feature set works for you, sub continues to work, as it only uses rather stable bash features which will continue to work for years to come.

Keeping up with updates to sub project

I know the instructions say to run prepare.sh and then blow away the repo history and start fresh when initializing your own sub.

Would it make sense to:

  1. run prepare.sh
  2. Un-delete all of the sub-* files.
  3. maintain the git history
  4. keep a remote pointed to this project so be able to pull in the latest changes and potentially apply them to my own sub-command's scripts

Is there any easier way to do this that I'm not considering?

Thanks!

zsh: occasionally seeing "command not found: complete" when loading subs

I'm using:

  • ITerm2(Build 1.0.0.20120203)
  • zsh
  • oh-my-zsh

and am sometimes seeing the following errors when opening new terminal windows or tabs:

/Users/bradley/Dropbox/bin/csd/libexec/../completions/csd.bash:14: command not found: complete
/Users/bradley/Dropbox/bin/sp/libexec/../completions/sp.bash:14: command not found: complete
/usr/local/Cellar/rbenv/0.3.0/libexec/../completions/rbenv.bash:14: command not found: complete

I'm assuming rbenv is also a sub so this is probably related.

The only thing I've noticed is that when my terminal profile is set to change into a particular directory on load, this tends to happen.

Here's the relevant portion of my .zshrc file:

... stuff before the end of the file ...
plugins=()

. ~/Dropbox/bin/z/z.sh
source ~/Dropbox/bin/ssh.bash
source ~/Dropbox/bin/mysql.bash
source ~/Dropbox/bin/du.bash

eval "$(/Users/bradley/Dropbox/bin/csd/bin/csd init -)"
eval "$(/Users/bradley/Dropbox/bin/sp/bin/sp init -)"
if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi # rbenv, ruby-build

source $ZSH/oh-my-zsh.sh

Any ideas?

mysub:10: command not found: my sub

My sub is called cmal and is installed at ~/Dropbox/Dropbox/Config/sub/cmal

My .zshenv contains:

PATH=$PATH:$HOME/.rvm/bin # Add RVM to PATH for scripting
eval "$($HOME/Dropbox/Config/sub/cmal/bin/cmal init -)"

Whenever I open a new terminal, and run cmal I get cmal:10: command not found: cmal

If I then run source ~/.zshenv followed by cmal I correctly get the usage.

But every time I open a new terminal window I'm back to square one. Any idea what could be going on?

New command

I've put together a sub-new command for generating the template for a new sub command. It is written in ruby, because I wanted to make use of the ERB handling - the alternate language generators are just ERB templates. I've got simple examples for bash and ruby. There are no dependencies on outside gems, because our concept was to limit this type of dependency.

Not sure if a pull request is appropriate, since this would add a dependency for ruby, but for the people I know, this isn't a problem.

Any ideas? Should I pass it up?

John

How to autocomplete file paths?

One of my commands simply accepts a file path as a parameter but I can't seem to figure out how to re-enable native autocompletion. Is this possible?

prepare.sh fails if arguments starts with 'sub'

Hi,
weird bug, but somebody else may start playing around with this by naming his first sub with something starting with 'sub'.

Tried this a couple of times:

./prepare.sh subfoo
Preparing your 'subfoo' sub!
rm: bin/sub: No such file or directory

zsh: error "command not found:" when loading shell

I've created an example that reproduces this here

I have the following in my .zshrc:

eval "$(/Users/bradley/Dev/zsh-sub-test/bin/zsh-sub-test init -)"

and when I load a new shell, I get the following:

eval "$(/Users/bradley/Dev/zsh-sub-test/bin/zsh-sub-test init -)"(eval):16: command not found: zsh-sub-test=_zsh-sub-test_wrapper

Issue with "kiva init -"

I still get this (again) when i setup the script:
-bash: eval: line 12: syntax error near unexpected token )' -bash: eval: line 12: )'

I've tracked it down to "kiva commands --sh" not returning anything. I should be spitting out "shell".

I can fix it by replacing ${commands[*]}) with shell)

Add argument chain to --complete to make multiple level compeltion possible

I want to be able to tab complete the list of servers in this example:
sub setup setupServers (foo, bar baz)

I dont see any easy way to do this, but I cold be wrong. If the list of completed arguments was passed to --complete then I would be able to parse down them and return completions to an arbitrary depth.

:)

zsh throws error when sub is symlinked from _SUB_ROOT

Instead of using the symlink from bin/sub to ../libexec/sub I wanted to make the symlink from _SUB_ROOT/sub to libexec/sub.

The following error in ZSH was produced as a result:

sub: line 13: cd: sub: Not a directory

In Bash this same command is cd ., which produces no error.

Documentation and opt-in autocomplete for other languages

I'm not sure there's much you can do here, but I've set up sub and created a javascript subcommand. To get automatic documentation and opt-in autocomplete, I had to make weird comments like

/*
# Usage: blah
*/

/*
# Provide sn completions
*/

because the comment syntax (#) is hard-coded. I realize this isn't a simple fix, but I thought I'd bring it up anyway.

Ideally, the language's comment syntax would be used so in javascript, I could just write:

/*
Usage: blah
*/

// Provide sub completions

Autocomplete on 3rd level argument?

First thanks a lot for this, besides being a well thought out framework, it gives us that little push to organize our scripts and share them with the team.

Now for my issue, I'm struggling with the autocompletion, I can make it work for the first level like:

❯ sub mage <tab>
foo        bar 

But I cant make it work on the second level, this is what happens:

❯ sub mage foo <tab>
foo        bar 

When I expected:

❯ sub mage foo <tab>
baz        fubar 

I've organised the files like:

libexec/sub-mage
libexec/sub-mage-foo
libexec/sub-mage-bar
libexec/sub-mage-foo-baz
libexec/sub-mage-foo-fubar

And put the --complete like this:

# Provide sub completions
if [ "$1" = "--complete" ]; then
    { echo foo; echo bar; } | sort | uniq
    exit;
fi

Thanks in advance for any help

Rename sub

Is there an existing command line to rename a sub?

cd from within sub

This isn't so much an issue as a plea for assistance.

I'm working on a sub script that I can use for managing my various projects. One aspect of that involves navigating to the directory for a project.

I would use it like so:

cmal proj my-project

This command opens the project folder in Sublime Text, opens the dev url for the project in my browser, and ideally would cd into the directory for this project.

That last part is the tricky one, because the cd command is being run in a subshell, so once the cmal proj script finishes running I'm still in the same directory I started in.

Any tips on getting around this within the workings of sub?

In case it's not obvious, I'm a designer, not a command line guru. I know that normally I could get around this by using source my-script or . my-script but that's about all I know. Thanks.

sub-sub-commands

How hard would it be to support sub-sub commands with the same autocompletion and help generation via in-line comments?

I am thinking of something like sub-folders

├── bin                   # contains the main executable for your program
├── libexec               # where the subcommand executables are
   ├── sub-command        # a sub-folder, containing 'sub-command-subcommand' scripts

An example where this would be useful is a script subcommand: One could stash away specialized stuff that should not clutter the commands output.
It would also allow a simple hierarchy without glueing together the script manually.

In the blog post you have the line 37 script basecamp file_usage_summary.
Is this the same kind of thinking or just is it just one subcommand which does more or less the same thing with different arguments which happens to be called script?

selfupdate

Maybe it would be nice to have a "update this sub" aka git-pull as a built-in feature?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.