I need to emulate a terminal in go
. I try to do it like this:
lsCmd := exec.Command("bash", "-c", "ls")
lsOut, err := lsCmd.Output()
if err != nil {
panic(err)
}
fmt.Println(string(lsOut))
And it seems to work correctly (the native ubuntu terminal displays a horizontal list, and the result of this function goes vertically).
But if I specifically call the wrong command, for example exec.Command ("bash", "-c", "lss")
, I get:
panic: exit status 127
And in the native ubuntu terminal I get the following result:
Command 'lss' not found, did you mean:
and enumeration of commands.
I need to communicate with the native terminal, and get the same thing as the result of the command if I wrote the command in the standard ubuntu terminal.
What is the best way to do this? Maybe the exec
library is not suitable for this? All this is necessary for front-end communication with the OS terminal. On a simple html/css/js
page, the user enters a command, after go
it sends it to the native terminal of the operating system and returns the result to the front-end.
How I can get the same result of executing commands as if I were working in a native terminal?
But if I specifically call the wrong command, for example exec.Command ("bash", "-c", "lss"), I get:
panic: exit status 127
And in the native ubuntu terminal I get the following result:
Command 'lss' not found, did you mean:
and enumeration of commands.
This has nothing to do with Go, and the problem is actually two-fold:
Ubuntu ships with a special package, command-not-found
, which is usually preinstalled, which tries make terminal more friently for mere mortals by employing two techniques:
When the command is not found, "plain" (see below) shell fails the attempt by returning a non-zero exit code.
This is absolutely expected and normal.
I mean, panicking on it is absolutely unwise.
There's a historical difference on how a shell is run on a Unix system.
When a user logs into the system (remember that back in the days the concept of the shell was invented you'd be logging in via a hardware computer terminal which was basically what your GNOME Terminal window is but in hardware, and connected over a wire),
the so-called login shell is started.
The primary idea of a logic shell is to provide interactive environment for the user.
But as you surely know, shells are also capable of executing scripts. When a shell executes a script, it's running in a non-interactive mode.
Now let's dig deeper into that thing about interactive vs non-interactive shells.
In the interactive mode:
bash
, for instance, engages GNU readline
.In the non-interactive mode:
/dev/null
).GNU bash
is able to run in both modes, and which mode it runs in depends
on how it was invoked.
When initializing in different modes, bash
reads different initialization scripts, and this explains why the machinery provided by the command-not-found
package gets engaged in the interactive mode and does not when bash is run otherwise — like in your invocation from Go.
The simplest thing to try is to run bash with the --login
command-line option or otherwise make it think it runs as an interactive shell.
This might solve the problem for your case but not necessarily.
The next possible problem is that some programs do really check whether they're running at a terminal — usually these are programs which insist on real interaction with the user, usually for security purposes, and there are programs which simply cannot run when not connected to a real terminal — these are "full-screen" text UI programs such as GNU Midnight Commander, Vim, Emacs, GNU Nano and anything like this.
To solve this problem, the only solution is to run the shell in a pseudo-terminal environment, and that's what @eudore hinted at in their comment.
The github.com/creack/pty
might be a package to start looking at; golang.org/x/crypto/ssh
also provides some means to wrangle PTYs.