Code Monkey home page Code Monkey logo

grumble's Introduction

Grumble - A powerful modern CLI and SHELL

GoDoc Go Report Card

There are a handful of powerful go CLI libraries available (spf13/cobra, urfave/cli). However sometimes an integrated shell interface is a great and useful extension for the actual application. This library offers a simple API to create powerful CLI applications and automatically starts an integrated interactive shell, if the application is started without any command arguments.

Hint: We do not guarantee 100% backwards compatiblity between minor versions (1.x). However, the API is mostly stable and should not change much.

asciicast

Introduction

Create a grumble APP.

var app = grumble.New(&grumble.Config{
	Name:        "app",
	Description: "short app description",

	Flags: func(f *grumble.Flags) {
		f.String("d", "directory", "DEFAULT", "set an alternative directory path")
		f.Bool("v", "verbose", false, "enable verbose mode")
	},
})

Register a top-level command. Note: Sub commands are also supported...

app.AddCommand(&grumble.Command{
    Name:      "daemon",
    Help:      "run the daemon",
    Aliases:   []string{"run"},

    Flags: func(f *grumble.Flags) {
        f.Duration("t", "timeout", time.Second, "timeout duration")
    },

    Args: func(a *grumble.Args) {
        a.String("service", "which service to start", grumble.Default("server"))
    },

    Run: func(c *grumble.Context) error {
        // Parent Flags.
        c.App.Println("directory:", c.Flags.String("directory"))
        c.App.Println("verbose:", c.Flags.Bool("verbose"))
        // Flags.
        c.App.Println("timeout:", c.Flags.Duration("timeout"))
        // Args.
        c.App.Println("service:", c.Args.String("service"))
        return nil
    },
})

Run the application.

err := app.Run()

Or use the builtin grumble.Main function to handle errors automatically.

func main() {
	grumble.Main(app)
}

Shell Multiline Input

Builtin support for multiple lines.

>>> This is \
... a multi line \
... command

Separate flags and args specifically

If you need to pass a flag-like value as positional argument, you can do so by using a double dash:
>>> command --flag1=something -- --myPositionalArg

Remote shell access with readline

By calling RunWithReadline() rather than Run() you can pass instance of readline.Instance. One of interesting usages is having a possibility of remote access to your shell:

handleFunc := func(rl *readline.Instance) {

    var app = grumble.New(&grumble.Config{
        // override default interrupt handler to avoid remote shutdown
        InterruptHandler: func(a *grumble.App, count int) {
            // do nothing
        },
		
        // your usual grumble configuration
    })  
    
    // add commands
	
    app.RunWithReadline(rl)

}

cfg := &readline.Config{}
readline.ListenRemote("tcp", ":5555", cfg, handleFunc)

In the client code just use readline built in DialRemote function:

if err := readline.DialRemote("tcp", ":5555"); err != nil {
    fmt.Errorf("An error occurred: %s \n", err.Error())
}

Samples

Check out the sample directory for some detailed examples.

Projects using Grumble

Known issues

  • Windows unicode not fully supported (issue)

Additional Useful Packages

Credits

This project is based on ideas from the great ishell library.

License

MIT

grumble's People

Contributors

activey avatar idlephysicist avatar k4rian avatar matir avatar moloch-- avatar r0l1 avatar sejust avatar skaldesh 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

grumble's Issues

Sometimes the D key and F key cannot be entered

In the Windows system, sometimes the D and F keys cannot be entered in the terminal, and the B key will also fail. What is the reason for this?

I haven't tried it on Linux, I don't know if there is this bug on Linux.

support required/optional flags

Currently, grumble does not have "required/optional" concept for flags. Hope to have this functionality, so can define required flags and optional flags.

For example, if defined flagA as required flag, the command without flagA is invalid.
if defined flagB as optional flag, the command without flagB is valid, and the value of flagB is the default value.

Have we considered supporting required flag?

	rootCmd.AddCommand(&grumble.Command{
		Name: "add",
		Help: "add a duplications to the table",
		Run: func(c *grumble.Context) error {
			if c.Flags.String("cluster") == "" {
				return fmt.Errorf("cluster cannot be empty")
			}
			return executor.AddDuplication(pegasusClient, useTable, c.Flags.String("cluster"), c.Flags.Bool("freezed"))
		},
		Flags: func(f *grumble.Flags) {
			f.String("c", "cluster", "", "the destination where the source data is duplicated")
			f.Bool("f", "freezed", false, "whether to freeze replica GC when duplication created")
		},
	})

Hi, I'm developing a CLI tool using the fantastic grumble library. I see we can not declare a flag without giving it a default value, like a required flag.

I'd suggest introducing an API for grumble.Flags, it could help much simplify the boilerplate code of empty flag check.

		Flags: func(f *grumble.Flags) {
			f.String("c", "cluster", "", "the destination where the source data is duplicated")
			f.Bool("f", "freezed", false, "whether to freeze replica GC when duplication created")

                        f.MarkFlagRequired("cluster")
		},

What do you think?

Command args accept hex value ?

I need to input some hex value as uint64 args for commands.

But strconv.ParseUint() in args.go uses the base 10, which causes error when pass hex number.

When use strconv.ParseUint() with base 0, there will be no error.

Maybe the base 0 is better?

how do i delete a command?

How do I delete a command?
To do this, I added a del function to the source code

func (c *Commands)Del(name string) { for i,cmd := range c.list { if cmd.Name == name { c.list = append(c.list[:i],c.list[i+1:]...) } } }

Provide the option to not completely override the original completer

Thanks for the library! This issue is really a feature request.

Request

It would be useful to me and potentially others to not have the default completer completely overriden once a non-nil completer is provided. This is because I'd still like to use original flag and sub-command completion if the parent command has a custom completer.

Proposal

I propose that the decision in the completion function here is modified to:

if cmd.Completer != nil {
	words = cmd.Completer(prefix, rest)
	for _, w := range words {
		suggestions = append(suggestions, []rune(strings.TrimPrefix(w, prefix)))
	}
        if cmd.OverrideCompleter {
                return suggestions, len(prefix)
        }
}

This means that grumble.Command would have a new field like OverrideCompleter (which could be set to 'true' by default to preserve original behaviour). If I'm not mistaken, the following change would mean that the custom completion results as well as the default completion results would be suggested when TAB is hit.

Backtick (`) in input causes error

When a backtick is the first rune of a word, shlex seems to choke on it. If you run sample/simple:

go run .
foo » `ls`
error: invalid args: Unknown rune: 96

Might be a shlex bug, but there's no GitHub issues enabled there, so I decided to report it here.

[Q]how to implement persistence configuration with grumble?

Is there any solution to implement persistence configuration with grumble? I've use vtysh in FRR before, and vtysh can save the configuration into a local file, and reload them when restart, the procedure looks like this:

  1. app start
  2. use vtysh to config app
  3. vtysh write configuration into local files
  4. app restart
  5. vtysh will re-config again using config commands in the local file.
    Is there any solution can implement this to make grumble act like vtysh?
    Thanks a lot!

grumble.NewWithReadline()?

Hi,

Just wondering, would it be much of a hassle to implement grumble constructor with already initialized readline?

Thanks 😃

Println arbitrarily prints line after the prompt

Hello,
Sometimes when using Println, Printf, etc... the text gets printed after the prompt instead of in a new line.
This also happens if I use grumble.Println.
I have a whole program using grumble right now which has this issue.
Link to my program: https://github.com/elleven11/pantegana

I think that the issue rises from the fact that the println calls come from a separate goroutine.
How would I be able to fix this issue? thank you.

Tab Completion: Add one more whitespace

If by pressing Tab a command can be completed, and this command is not a prefix for another command, add one more whitespace to the result, to allow for quick completion of the next.

Example:

  • commands: "Command1", "OtherCommand" -> user types "Comm" -> hits Tab -> "Command1 " (! mind the whitespace)
  • commands: "Command1", "Command2" -> user types "Comm" -> hits Tab -> "Command" (! no whitespace, as multiple results possible)

Improve Args parsing

I would appreciate finer control over the allowed arguments of a command, similar to how flags are handled.
I find myself do a lot checking for arguments in the Run methods of each command and converting the string arguments to the data types I actually need.

I'd suggest to do it very similar and replace "AllowArgs: bool" with a func(a *Args) {...}
This new type Args would bundle all allowed arguments similar to the flags package, but of course just restrict the format for each argument.
E.g. you could specify a String argument, optionally providing a regexp to check the format of it.

What do you think of this? I'd happily do a PR for this!

Println arbitrarily prints line after the prompt

#39

test code

var app = grumble.New(&grumble.Config{
	Name:              "foo",
	Description:       "An awesome foo bar",
})
func init() {
	app.AddCommand(&grumble.Command{
		Name:    "listen",
		Aliases: []string{"l"},
		Help:    "listen",
		Args: func(a *grumble.Args) {
			a.String("host", `a host to listen to (for ipv6 put in square brackets. eg: "[::1]")`, grumble.Default("0.0.0.0"))
		},
		Run: func(c *grumble.Context) error {
			go Pr()
			return nil
		},
	})
}

func Pr()  {
	app.Println("[-] A listener is already running.")
}

in windwos10 cmd

image

in windwos10 powershell

image

Undefined behaviour when prompt has newline

`
// This Code Work's Fine
var App = grumble.New(&grumble.Config{
Name: "Cli",
Prompt: "Main Menu : ",
})

terminal output :
Main Menu :

// This Code Does Not Work
var App = grumble.New(&grumble.Config{
Name: "Cli",
Prompt: "User james Kelly \n >> : ",
})

terminal output :
User james Kelly

//But when pressed 'enter' or typing this happens :
// When Pressed Enter :
User james Kelly
User james Kelly
User james Kelly
">>"
User james Kelly
">>"

// While Typing :
User james Kelly
User james Kelly
User james Kelly
User james Kelly
User james Kelly
">> abcd"

// Expected Same As Single Line Prompt
// When Pressed Enter :
User james Kelly
">>"
User james Kelly
">>"

// While Typing :
User james Kelly
">> abcd"

Note : While typing it prints promt every single key press.
Is there a quick fix to this? I need to deliver to the client asap.
`

Use of `DisableAutoSaveHistory: true` in readline impacts history behaviour

Disabling readline's built-in history at

grumble/app.go

Line 369 in 4afcb1c

DisableAutoSaveHistory: true,
and manually registering history at

grumble/app.go

Lines 446 to 451 in 4afcb1c

// Save command history.
err = a.rl.SaveHistory(line)
if err != nil {
a.PrintError(err)
continue Loop
}
has the negative consequence of not storing the most recently executed command as the most recent history item.

Step 1:

prompt> command1
ran command1
prompt> command2
ran command2

Expected: up arrow shows command2
Actual: up arrow shows command2

Step 2:

prompt> command1
bck-i-search: comm
ran command1

Expected: up arrow shows command1
Actual: up arrow shows command2

By disabling/removing the code above we get the expected behaviour which matches history from bash, zsh, etc.

enable or disable commands during execution

Hello guys

I'm developing a tool for internal use and I need some commands to be available only after some security checks like (login, password, captcha).

Could someone tell me if it is possible to enable or disable commands during execution?

Bring README up-to-date

After #25 has been merged, I want to update the README to the current grumble interface. Some things have changed since the last updates. I think it is more convenient to update everything in this one issue then

Abort running command, if App.Close() is called

Right now, it is not possible to properly close the application, since the Run() command runs the shell and waits for the next readline() to finish. This requires that, if the App has been closed from outside, the user still needs to press enter in order for the close to succeed

Feature Request: Default Help Flag

Having been tinkering around with this tool, to create a utility program for a project I am working on, I was wondering if it would be possible to have the flag "--help" added across all commands, to bring up their individual help fields?
Just as "Help command1 subcommand1" would bring up the help for subcommand1, could it be made so "command1 subcommand1 --help" would achieve the same, by default?

Crash when using with search + complete

Hi,

We are using grumble on our CLI, when mixing the back-search (CTRL+R) with the complete from Grumble we get a crash:

sliver > panic: runtime error: index out of range [-1] win32_service"'
bck-i-search: shar
goroutine 7 [running]:
github.com/desertbit/grumble.(*completer).Do(0xc0000b0728, 0xc001499100, 0x3d, 0x3d, 0x0, 0x3d, 0xc0080a6900, 0xc00681bcb0, 0x100c5f8)
	/Users/xxx/go/pkg/mod/github.com/desertbit/[email protected]/completer.go:52 +0x16ea
github.com/desertbit/readline.(*opCompleter).OnComplete(0xc0072ce1c0, 0xc00685d9e0)
	/Users/xxx/go/pkg/mod/github.com/desertbit/[email protected]/complete.go:87 +0x142
github.com/desertbit/readline.(*Operation).ioloop(0xc0072ce0e0)
	/Users/xxx/go/pkg/mod/github.com/desertbit/[email protected]/operation.go:178 +0x10a4
created by github.com/desertbit/readline.NewOperation
	/Users/xxx/go/pkg/mod/github.com/desertbit/[email protected]/operation.go:88 +0x2b1

Steps to reproduce:

  • CTRL+R and start writing "sharp"
  • Press tab to autocomplete

I fixed that changing the line 52 from completer.go to:

if len(words) > 0 && pos > 1 && len(line) > pos && line[pos-1] != ' ' {

This fix should avoid negative indexes on any case. What do you think? Should I do a PR? I'm not sure if the fix is good enough, maybe could be better to parse "TAB" or something like that.

Thanks

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.