How to take input when outputting? - go

What I'm trying to achieve is some kind of an application that logs a lot of information into the console (for instance, numbers from 0 to 1000000000000) and an ability to stop the execution by typing a command in the console (for example "stop").
I came up with an idea of using goroutines, however, I can't type in commands because the input field is changing to output.
Here is my code:
package main
import (
"bufio"
"os"
)
var process bool = true
func commandHandler() {
reader := bufio.NewReader(os.Stdin)
for process {
text, _ := reader.ReadString('\n')
if text == "stop\r\n" {
process = false
}
}
}
func task() {
var i int64 = 0
for ; i < 10000000000 || process; i++ {
if i%30000000 == 0 { // Just to slow down an output for a while
println(i)
}
}
}
func main() {
go task()
commandHandler()
}
And the result that I get. Letters are my input that I`m trying to type in and numbers - the output of the application
Any suggestions on how to make proper input\output in my Go application?

I can't type in commands because the input field is changing to output
You can type in the commands just fine. They'll be echoed to your console as you type them. This will be combined with everything else that is being output to your console, which can be confusing to the user - but not to the computer.
Your program works just fine, assuming this actually matches on your OS:
if text == "stop\r\n" {
On my OS there's no \r. A more portable method would be to use strings.TrimSpace to remove all the whitespace.
import (
...
"strings"
)
...
...
if strings.TrimSpace(text) == "stop" {
With this change, the code works for me. As you said, the output and input is interpolated onto the screen, but you can still type stop and end the program.
Any suggestions on how to make proper input\output in my Go application?
Almost no real world terminal programs are interactive. Instead command line arguments and environment variables provide their configurationat invocation time. Shell redirection is typically used to retain (in a file) or paginate (eg |less) any long output of a program.
You'll see a lot of academic programming using interactive terminal models where the program prompts the user and collects data, but in the real world this is very rare and is generally an ill advised complication. Standard input is typically used to provide data input for computation, rather than user commands (for example, data to be manipulated or filtered).
As #tinkerer suggests, signals can already be used to terminate your program (details are OS specific). So there's really no need to worry about how a terminal application would handle the visual representation of standard input and output being interpolated just to terminate the program. In a Posix environment there are a number of other signals, some of which are entirely user oriented and can be used for arbitrary purposes.
Once more complicated user input is required at runtime, the program typically listens on a socket for commands. For this kind of program, web interfaces serving HTTP have become increasingly common for the simplicity and accessibility.

Related

How does crypto/rand generate a secure random number?

I started to geek out and I wanted to see how https://golang.org/src/crypto/rand/rand_unix.go works on the inside.
I wanted to see when it generates rands from dev/random (more secure) and when it generates from dev/urandom(less security)
It looks like if rand_batched.go is initialized (this initializes altGetRandom) and GOOS is not plan9 (then r.name = urandomDevice it will return the length of the random array and not the content (which is surprising, why the length?)
see line 57:
if altGetRandom != nil && r.name == urandomDevice && altGetRandom(b) {
return len(b), nil
}
else it will simply return the content of the array which will be based on dev/random only if GOOS=plan9.
So why should it ever return len(b)?
Also it looks to me that most of the time it will use dev/urandom which is suboptimal... am I wrong (guess so because of docs, but help me understand)?
altGetRandom is used on systems where there is a system call to get random bytes, rather than depending on the existence of /dev/urandom. This is sometimes useful in special environments (a chroot where there is no /dev, Docker-ish systems with weird/wrong /dev, FreeBSD jails with an incorrect /dev setup, etc.), and also is a bit faster than opening the file (as it does not go through quite as many system call layers), though in general one should just use the file.
The call in question is in an io.Reader-style function, whose job is to return the length of the block of bytes read, and any error. When using the system call, the OS fills in—or is assumed to fill in—the array b completely, so len(b) is the correct result.

Python text color doesn't work on Windows

I'm trying to get this snippet to work, but it seems it won't work in Windows. Under Linux it works just fine!
Here is the sample snippet of code demonstrating the usage:
tops = []
for ind, top in enumerate(lr.top):
color = colors.setdefault(top, COLORS[len(colors) % len(COLORS)])
if top in disconnected_tops:
top = '\033[1;4m' + top
if len(lr.loss_weight) > 0:
top = '{} * {}'.format(lr.loss_weight[ind], top)
tops.append('\033[{}m{}\033[0m'.format(color, top))
top_str = ', '.join(tops)
When the whole script is run, the escape character seems not to be working and weird characters show up on the screen. How do I get this to work on Windows?
I found the problem!
I had to use init() in the script that was missing originally!.
Seems init() is not needed in Linux based OSes!since if it were!, this shouldn't had worked there in first place!
Ok.Here is the documentation itself!:
On Windows, calling init() will filter ANSI escape sequences out of
any text sent to stdout or stderr, and replace them with equivalent
Win32 calls.
On other platforms, calling init() has no effect (unless you request
other optional functionality; see “Init Keyword Args”, below). By
design, this permits applications to call init() unconditionally on
all platforms, after which ANSI output should just work.

How do I get colors to work with golang tabwriter?

I am using the tabwriter and I cannot get it to work with colors. I am using the "github.com/fatih/color" package.
Basically the problem is that I need to call tabwriter's w.Flush() in order to get the colors to render... I cannot switch colors if I have not called a flush.
Calling Flush in turn screws with the tabwriter formatting.
Any ideas on how to combine the two?
package main
import "fmt"
import "text/tabwriter"
import "os"
import "github.com/fatih/color"
func main() {
w := new(tabwriter.Writer)
w.Init(os.Stderr, 0, 8, 0, '\t', 0)
color.Set(color.FgGreen)
fmt.Fprintln(w, "ID\tNAME\tSIZE\tFIELD1\tSTATUS\tSTATE")
// ------> Calling w.Flush() here cases problems.
color.Set(color.FgYellow)
fmt.Fprintln(w, "8617833164795356724\tfoo1\t1.1 Gb\t3\tsome_status\tsome_state")
fmt.Fprintln(w)
w.Flush()
}
Despite what the accepted answer says, it is possible, you just have to be very careful about field length.
Wrap each "field" (i.e. a specific row and column) with a color+reset code. If all codes are of the same string length, tabwriter will give you a good result.
I have a crude demonstration here: https://play.golang.org/p/r6GNeV1gbH
I didn't do so in my demo, but you should also add the background codes as well (you can simply add them together as in RedText + YellowBackground), providing a default background. In this way, everything will be of equal length and you'll have background support as well.
Please note that I am a beginner Go programmer. I don't claim that my code is any good.
Short answer
You can't.
Naive answer
Use the color.Color.SprintFunc() method to get a function and wrap your strigns using this function.
Real answer
That won't work either, because the color is set using a special character sequence that isn't recognized by the tabwriter, so this row will be shorter by the length of twice the marker (one to set the color and one to get back to the standard color).
Solution
Write an alternative tabwriter (the algoerithm isn't even complex) that recognized the color character sequence and ignore it.

Determine escape sequence independent from terminal type

My app reading escape sequences from terminal in raw mode. And when it's running on xterm I got F2 like "\eOQ". But when it's running in linux tty terminal (Switching by Ctrl-Alt-F1) I got "\e[[[B".
What is the correct way to determine that I got F2 independent from terminal type application running on?
If you're wanting to read terminal keypresses, you likely want to look at something like libtermkey , which abstracts the general problem away for you. Internally it uses a combination of terminfo lookups, or hardcoded knowledge of the extended xterm-like model for modified keypresses, so it can understand things like Ctrl-Up, which a regular curses/etc... cannot.
while((ret = termkey_waitkey(tk, &key)) != TERMKEY_RES_EOF) {
termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_VIM);
printf("You pressed key %s\n", buffer);
if(key.type == TERMKEY_TYPE_FUNCTION &&
!key.modifiers &&
key.code.number = 2)
printf("Got F2\n");
}
Ok, as I got the best way to use [n]curses library. It is read terminfo (termcap) database and determine what mean escape sequence you got depend on terminal type.
It is not necessary using it's terminal graphics functions. To get correct escape sequences using curses you may do the following:
newterm(NULL, stdout, stdin);
raw();
noecho();
keypad();
ch = getch();
if (ch == KEY_F(2)) printf("Got F2");
endwin();
Also, it is probably possibly do it manually by reading terminfo database in you app.

It's posting the input twice

I want to read some input from stdin and then display it. At the moment I am doing this like this:
in := bufio.NewReader(os.Stdin);
input, err = in.ReadString('\n');
if err != nil {
fmt.Println("Error: ", err)
os.Exit(1)
}
fmt.Printf("This is your", input)
...but after running this and entering some input it is always displaying my input twice like this:
This is just a test
This is your This is just a test
Is there anyway to remove the first line?
I haven't yet tried anything with the package, but I guess it could be helpful in this case: exp/terminal. Specifically the ReadPasword function documentations is:
ReadPassword reads a line of input from a terminal without local echo.
This is commonly used for inputting passwords and other sensitive data.
The slice returned does not include the \n.
I assume your first line is just your echoed input text? That's actually a function of the process' terminal. Since the go runtime treats Stdin like any other file, you don't have direct access to the terminal attributes. However you might be able to hack something together with CGO and the approach described here.

Resources