Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shell buitins have no effect when running an interactive parser with a custom readline implementation #1100

Closed
sweetbbak opened this issue Oct 7, 2024 · 2 comments

Comments

@sweetbbak
Copy link

sweetbbak commented Oct 7, 2024

for example, if I alias a command to something else:

alias ls='echo this is an alias'

and then if I run ls the alias isn't honored, it just runs the ls binary.

$ alias ls='echo nothing'                                                                                           18:27
$ ls                                                                                                                                18:27
ash_history  bin  cmd  go.mod  go.sum  justfile  pkg
$ alias                                                                                                                           18:27
alias ls='eza'
$                                                                                                                                    18:27

This could be a result of the way I'm using this library, but I've tried every documented way to parse and run shell statements with my custom implementation of readline. This implementation is probably 3500 sloc at minimum and I can't fit it into the given parser.Interactive method.

here is an example of the implementation:

	r, err := interp.New(interp.StdIO(os.Stdin, os.Stdout, os.Stderr))
	if err != nil {
		log.Fatal(err)
	}

	parser := syntax.NewParser(syntax.Variant(syntax.LangBash))

	for {
		line, err := shell.Readline() // for this example this is a black box that returns a string
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}

		if err := parser.Stmts(strings.NewReader(line), func(stmt *syntax.Stmt) bool {
			if parser.Incomplete() {
				return true
			}

                        // pass Ctrl+C to the child process and ignore it
			ctx, cancel := context.WithCancel(context.Background())
			go func() {
				select {
				case <-signals:
					cancel()
					return
				case <-ctx.Done():
					return
				}
			}()

			r.Run(ctx, stmt)
			if r.Exited() {
				return false
			}

			return true
		}); err != nil {
			log.Println(err)
		}
	}

is there some part of the API I am missing here, or is what is necessary to allow this not exposed and what could possibly be causing shell builtins not to work in this context?

exit, read -r VARNAME also doesn't work, but cd , export, pushd popd dirs work just fine.

edit:
upon doing some testing I see that alias also doesn't work with gosh either, so maybe this is a known issue.

mvdan added a commit that referenced this issue Oct 8, 2024
mvdan added a commit that referenced this issue Oct 8, 2024
Otherwise aliases don't get expanded, as they are only on by default for
interactive shells in Bash, and we follow POSIX shell and Bash options.

Updates #1100.
@mvdan
Copy link
Owner

mvdan commented Oct 8, 2024

Thanks for the report. Indeed cmd/gosh did not expand aliases by default, even though it should behave like an interactive shell. The commits above should have fixed that, although note that you will need to use the option like cmd/gosh does now.

Can you clarify what you mean by exit and read not working properly in an interactive shell? They seem to work just fine in cmd/gosh at master, at least:

$ echo $0
/usr/bin/bash
$ gosh
$ echo $0
gosh
$ read -r FOO <<EOF
> here is some text
> EOF
$ echo $FOO
here is some text
$ exit 34
$ echo $0
/usr/bin/bash

@sweetbbak
Copy link
Author

sweetbbak commented Oct 9, 2024

I'm glad it was a simple fix. My problem is that the parser.Interactive method only allows for very rudimentary forms of line editing. So I, and the two other gosh implementations I found in the wild, were trying to re-implement parser.Interactive but with better readline capabilities:

(u-root) https://github.com/u-root/u-root/tree/main/cmds/core/gosh
(gonix) https://git.mills.io/prologic/gonix/src/branch/main/internal/applets/sh/sh.go

but the problem all of these implementations run into the issues I outlined above: aliases don't expand, read -r VAR doesn't read into a variable, exit also doesn't actually exit the interactive shell and asfaik there is no real way to propagate that exit code upwards.

I made a little test repo that shows the problem and gives a way clearer idea of what I am trying to do you can check it out here https://github.com/sweetbbak/ash/blob/main/cmd/readline/main.go, or you could quick install it with this if you want to see it first hand:

go install github.com/sweetbbak/ash/cmd/readline@6615b6b5e7588118ecb081ceed14c8cc0c3aa864

but ultimately, I would like to implement sh with a way more capable version of readline that allows for ^L clearing the screen, vim + emacs mode, etc... and the given API of parser.Interactive isn't capable of doing that (as far as I know, I could be wrong here) and replicating that behavior externally causes the issues I outlined. Thanks you for the awesome library and tooling by the way, it is awe inspiring to me.

edit: my apologies, I realized that read does work and I just made a stupid mistake of not exporting the variable, I'm guessing exit can be implemented externally, and aliases are fixed so I'll just close the issue.

This was referenced Nov 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants