Is there a way to check if the input stream (os.Stdin) has data?
The post Read from initial stdin in GO? shows how to read the data, but unfortunately blocks if no data is piped into the stdin.
os.Stdin is like any other "file", so you can check it's size:
package main
import (
"fmt"
"os"
)
func main() {
file := os.Stdin
fi, err := file.Stat()
if err != nil {
fmt.Println("file.Stat()", err)
}
size := fi.Size()
if size > 0 {
fmt.Printf("%v bytes available in Stdin\n", size)
} else {
fmt.Println("Stdin is empty")
}
}
I built this as a "pipe" executable, here is how it works:
$ ./pipe
Stdin is empty
$ echo test | ./pipe
5 bytes available in Stdin
This seems to be reliable solution and works even with sleep/delayed data via pipe.
https://coderwall.com/p/zyxyeg/golang-having-fun-with-os-stdin-and-shell-pipes
package main
import (
"os"
"fmt"
)
func main() {
fi, err := os.Stdin.Stat()
if err != nil {
panic(err)
}
if fi.Mode() & os.ModeNamedPipe == 0 {
fmt.Println("no pipe :(")
} else {
fmt.Println("hi pipe!")
}
}
Related
I am new to golang so please review the code and suggest any changes required.
So the problem statement goes below,
We have a file whose contents are in binary and are encrypted. The only way to read that contents if by using a custom utility say (named decode_it).. The command just accepts filename like below
decode_it filename.d
Now what I have to do is live monitoring the output of the decode_it utility in GO. I have written the code which is working great but somehow it is not able to process the latest tailed output (it is waiting for some amount of time for reading the last latest chunk before more data comes in ). s.Scan() is the function which is not returning the latest changes in output of that utility. I have another terminal side by side so I know that a line is appended or not. The GO Scan() function only scans when another chunk is appended at the end.
Please help. Suggest any changes required and also if possible you can suggest any other alternative approach for this.
Output of utility is - These are huge and come in seconds
1589261318 493023 8=DECODE|9=59|10=053|34=1991|35=0|49=TEST|52=20200512-05:28:38|56=TEST|57=ADMIN|
1589261368 538427 8=DECODE|9=59|10=054|34=1992|35=0|49=TEST|52=20200512-05:29:28|56=TEST|57=ADMIN|
1589261418 579765 8=DECODE|9=59|10=046|34=1993|35=0|49=TEST|52=20200512-05:30:18|56=TEST|57=ADMIN|
1589261468 627052 8=DECODE|9=59|10=047|34=1994|35=0|49=TEST|52=20200512-05:31:08|56=TEST|57=ADMIN|
1589261518 680570 8=DECODE|9=59|10=053|34=1995|35=0|49=TEST|52=20200512-05:31:58|56=TEST|57=ADMIN|
1589261568 722516 8=DECODE|9=59|10=054|34=1996|35=0|49=TEST|52=20200512-05:32:48|56=TEST|57=ADMIN|
1589261618 766070 8=DECODE|9=59|10=055|34=1997|35=0|49=TEST|52=20200512-05:33:38|56=TEST|57=ADMIN|
1589261668 807964 8=DECODE|9=59|10=056|34=1998|35=0|49=TEST|52=20200512-05:34:28|56=TEST|57=ADMIN|
1589261718 853464 8=DECODE|9=59|10=057|34=1999|35=0|49=TEST|52=20200512-05:35:18|56=TEST|57=ADMIN|
1589261768 898758 8=DECODE|9=59|10=031|34=2000|35=0|49=TEST|52=20200512-05:36:08|56=TEST|57=ADMIN|
1589261818 948236 8=DECODE|9=59|10=037|34=2001|35=0|49=TEST|52=20200512-05:36:58|56=TEST|57=ADMIN|
1589261868 995181 8=DECODE|9=59|10=038|34=2002|35=0|49=TEST|52=20200512-05:37:48|56=TEST|57=ADMIN|
1589261918 36727 8=DECODE|9=59|10=039|34=2003|35=0|49=TEST|52=20200512-05:38:38|56=TEST|57=ADMIN|
1589261968 91253 8=DECODE|9=59|10=040|34=2004|35=0|49=TEST|52=20200512-05:39:28|56=TEST|57=ADMIN|
1589262018 129336 8=DECODE|9=59|10=032|34=2005|35=0|49=TEST|52=20200512-05:40:18|56=TEST|57=ADMIN|
1589262068 173247 8=DECODE|9=59|10=033|34=2006|35=0|49=TEST|52=20200512-05:41:08|56=TEST|57=ADMIN|
1589262118 214993 8=DECODE|9=59|10=039|34=2007|35=0|49=TEST|52=20200512-05:41:58|56=TEST|57=ADMIN|
1589262168 256754 8=DECODE|9=59|10=040|34=2008|35=0|49=TEST|52=20200512-05:42:48|56=TEST|57=ADMIN|
1589262218 299908 8=DECODE|9=59|10=041|34=2009|35=0|49=TEST|52=20200512-05:43:38|56=TEST|57=ADMIN|
1589262268 345560 8=DECODE|9=59|10=033|34=2010|35=0|49=TEST|52=20200512-05:44:28|56=TEST|57=ADMIN|
1589262318 392894 8=DECODE|9=59|10=034|34=2011|35=0|49=TEST|52=20200512-05:45:18|56=TEST|57=ADMIN|
1589262368 439936 8=DECODE|9=59|10=035|34=2012|35=0|49=TEST|52=20200512-05:46:08|56=TEST|57=ADMIN|
1589262418 484959 8=DECODE|9=59|10=041|34=2013|35=0|49=TEST|52=20200512-05:46:58|56=TEST|57=ADMIN|
1589262468 531136 8=DECODE|9=59|10=042|34=2014|35=0|49=TEST|52=20200512-05:47:48|56=TEST|57=ADMIN|
1589262518 577190 8=DECODE|9=59|10=043|34=2015|35=0|49=TEST|52=20200512-05:48:38|56=TEST|57=ADMIN|
1589262568 621673 8=DECODE|9=59|10=044|34=2016|35=0|49=TEST|52=20200512-05:49:28|56=TEST|57=ADMIN|
1589262618 661569 8=DECODE|9=59|10=036|34=2017|35=0|49=TEST|52=20200512-05:50:18|56=TEST|57=ADMIN|
1589262668 704912 8=DECODE|9=59|10=037|34=2018|35=0|49=TEST|52=20200512-05:51:08|56=TEST|57=ADMIN|
1589262718 751844 8=DECODE|9=59|10=043|34=2019|35=0|49=TEST|52=20200512-05:51:58|56=TEST|57=ADMIN|
1589262768 792980 8=DECODE|9=59|10=035|34=2020|35=0|49=TEST|52=20200512-05:52:48|56=TEST|57=ADMIN|
1589262818 840365 8=DECODE|9=59|10=036|34=2021|35=0|49=TEST|52=20200512-05:53:38|56=TEST|57=ADMIN|
1589262868 879185 8=DECODE|9=59|10=037|34=2022|35=0|49=TEST|52=20200512-05:54:28|56=TEST|57=ADMIN|
1589262918 925163 8=DECODE|9=59|10=038|34=2023|35=0|49=TEST|52=20200512-05:55:18|56=TEST|57=ADMIN|
1589262968 961584 8=DECODE|9=59|10=039|34=2024|35=0|49=TEST|52=20200512-05:56:08|56=TEST|57=ADMIN|
1589263018 10120 8=DECODE|9=59|10=045|34=2025|35=0|49=TEST|52=20200512-05:56:58|56=TEST|57=ADMIN|
1589263068 53127 8=DECODE|9=59|10=046|34=2026|35=0|49=TEST|52=20200512-05:57:48|56=TEST|57=ADMIN|
1589263118 92960 8=DECODE|9=59|10=047|34=2027|35=0|49=TEST|52=20200512-05:58:38|56=TEST|57=ADMIN|
1589263168 134768 8=DECODE|9=59|10=048|34=2028|35=0|49=TEST|52=20200512-05:59:28|56=TEST|57=ADMIN|
1589263218 180362 8=DECODE|9=59|10=035|34=2029|35=0|49=TEST|52=20200512-06:00:18|56=TEST|57=ADMIN|
1589263268 220070 8=DECODE|9=59|10=027|34=2030|35=0|49=TEST|52=20200512-06:01:08|56=TEST|57=ADMIN|
1589263318 269426 8=DECODE|9=59|10=033|34=2031|35=0|49=TEST|52=20200512-06:01:58|56=TEST|57=ADMIN|
1589263368 309432 8=DECODE|9=59|10=034|34=2032|35=0|49=TEST|52=20200512-06:02:48|56=TEST|57=ADMIN|
1589263418 356561 8=DECODE|9=59|10=035|34=2033|35=0|49=TEST|52=20200512-06:03:38|56=TEST|57=ADMIN|
Code -
package main
import (
"bytes"
"bufio"
"io"
"log"
"os/exec"
"fmt"
)
// dropCRLR drops a terminal \r from the data.
func dropCRLR(data []byte) []byte {
if len(data) > 0 && data[len(data)-1] == '\r' {
return data[0 : len(data)-1]
}
return data
}
func newLineSplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, '\n'); i >= 0 {
// We have a full newline-terminated line.
return i + 1, dropCRLR(data[0:i]), nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), dropCRLR(data), nil
}
// Request more data.
// fmt.Println("Returning 0,nil,nil")
return 0, nil, nil
}
func main() {
cmd := exec.Command("decode_it", "filename.d", "4", "1")
var out io.Reader
{
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
log.Fatal(err)
}
out = io.MultiReader(stdout, stderr)
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
// Make a new channel which will be used to ensure we get all output
done := make(chan struct{})
go func() {
// defer cmd.Process.Kill()
s := bufio.NewScanner(out)
s.Split(newLineSplitFunc)
for s.Scan() {
fmt.Println("---- " + s.Text())
}
if s.Err() != nil {
fmt.Printf("error: %s\n", s.Err())
}
}()
// Wait for all output to be processed
<-done
// Wait for the command to finish
if err := cmd.Wait(); err != nil{
fmt.Println("Error: " + string(err.Error()))
}
// if out closes, cmd closed.
log.Println("all done")
}
Also, Since scan() is taking a lot of time and goes into a loop from which I am not able to break as well. Please help for that too..
try something like this one, i fixed some issues and make it more simple:
package main
import (
"bufio"
"fmt"
"io"
"log"
"os/exec"
)
func main() {
var err error
// change to your command
cmd := exec.Command("sh", "test.sh")
var out io.Reader
{
var stdout, stderr io.ReadCloser
stdout, err = cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
stderr, err = cmd.StderrPipe()
if err != nil {
log.Fatal(err)
}
out = io.MultiReader(stdout, stderr)
}
if err = cmd.Start(); err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(out)
for scanner.Scan() {
fmt.Println("---- " + scanner.Text())
}
if err = scanner.Err(); err != nil {
fmt.Printf("error: %v\n", err)
}
log.Println("all done")
}
test.sh that i used in test:
#!/bin/bash
while [[ 1 = 1 ]]; do
echo 1
sleep 1
done
:)
I tried resolving the above issue using stdbuf -
cmd := exec.Command("stdbuf", "-o0", "-e0", "decode_it", FILEPATH, "4", "1")
Reference link - STDIO Buffering
When programs write to stdout they write with line bufferring. If they are writing to something else, then they use fully buffered mode. golang exec.Command seems to end up using fully buffered mode so using stdbuf forces no buffering.
At the end of the JSON and Go blog post you'll find this sample program:
package main
import (
"encoding/json"
"log"
"os"
)
func main() {
dec := json.NewDecoder(os.Stdin)
enc := json.NewEncoder(os.Stdout)
for {
var v map[string]interface{}
if err := dec.Decode(&v); err != nil {
log.Println(err)
return
}
for k := range v {
if k != "Name" {
delete(v, k)
}
}
if err := enc.Encode(&v); err != nil {
log.Println(err)
}
}
}
I compiled this with go build json_decoder.go and then ran the program like so in bash:
echo '{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}' | json_decoder
and received this output:
{"Name":"Wednesday"}
2019/08/17 22:09:20 EOF
The first line of output is exactly what I'd expect. But where is the line 2019/08/17 22:09:20 EOF coming from?
When EOF is reached the decoder returns io.EOF which is then is being output by the logger log.Println(err) with a timestamp prepended.
You can check for EOF when decoding like this for example
if err := dec.Decode(&v); err != nil {
if err != io.EOF {
log.Println(err)
}
return
}
Output:
➜ echo '{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}' | ./json_decoder
{"Name":"Wednesday"}
log.Println(err)
That logger writes to standard error and prints the date and time of each logged message.
You are seeing both stdoutand stderr so:
The solution is to redirect the stdout to a file (or to another pipe):
echo '{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}' | ./jsonio > file.json
Output (which is stderr):
2019/08/17 20:35:40 EOF
Then see the file content (which is stdout):
cat file.json
{"Name":"Wednesday"}
You may discard stderr (not recommended, but for test purpose) or redirect stderr:
echo '{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}' | ./jsonio 2>/dev/null
Output (which is stdout):
{"Name":"Wednesday"}
Or don't show the EOF at all (note: you'll see other errors like incorrect JSON errors in stderr):
package main
import (
"encoding/json"
"io"
"log"
"os"
)
func main() {
dec := json.NewDecoder(os.Stdin)
enc := json.NewEncoder(os.Stdout)
for {
var v map[string]interface{}
if err := dec.Decode(&v); err != nil {
if err != io.EOF {
log.Println(err)
}
return
}
for k := range v {
if k != "Name" {
delete(v, k)
}
}
if err := enc.Encode(&v); err != nil {
log.Println(err)
}
}
}
Is it possible to read a commands output with its color attributes. I mean, can we read the actual escape sequences.
for instance;
A command output is red colored:
Hello
I want to read it as :
\033[31;1;4mHello\033[0m
Currently I am reading it like:
func stat(hash string) string {
cmd := exec.Command("git", "show", "--stat", hash)
out, err := cmd.Output()
if err != nil {
return err.Error()
}
return string(out)
}
Use the github.com/creack/pty library to run the command in a pty
This works for me
The escape sequences are visible in the output
package main
import (
"github.com/creack/pty"
"io"
"os"
"os/exec"
)
func main() {
hash := os.Args[1]
cmd := exec.Command("git", "show", "--stat", hash)
f, err := pty.Start(cmd)
if err != nil {
panic(err)
}
io.Copy(os.Stdout, f)
}
In this minimal working example I'm trying to do the following:
Prompt user for password
Unmarshal JSON either from files specified as arguments or from STDIN
Here's the source code:
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"os"
"syscall"
"golang.org/x/crypto/ssh/terminal"
)
const correctPassword = "secret"
func main() {
args := os.Args[1:]
var passwd string
for {
passwd = promptPassword()
if passwd == correctPassword {
log.Println("Correct password! Begin processing...")
break
}
log.Println("Incorrect password!")
}
if len(args) == 0 { // Read from stdin
log.Println("Reading from stdin")
dec := json.NewDecoder(os.Stdin)
for {
var v interface{}
if err := dec.Decode(&v); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
log.Printf("%#v", v)
}
}
for _, fileName := range args {
log.Println("Reading from", fileName)
f, err := os.Open(fileName)
if err != nil {
log.Println(err)
continue
}
defer f.Close()
dec := json.NewDecoder(f)
for {
var v interface{}
if err := dec.Decode(&v); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
log.Printf("%#v", v)
}
}
}
func promptPassword() (passwd string) {
for {
fmt.Fprintln(os.Stderr, "Enter password:")
b, _ := terminal.ReadPassword(int(syscall.Stdin))
passwd = string(b)
if passwd != "" {
break
}
}
return passwd
}
Everything works all right except when already prepared data is piped or redirected (e.g. go run main.go < mydata.json, or echo 42 | go run main.go, etc).
When I pipe or redirect some data to the program, the data gets processed by the password prompt, not the JSON decoder part. Is there any way to at first prompt for the password, and only after process the incoming data?
I was trying to detect if there's any data in STDIN to read it and store in some temporary bytes slice, but I can't find how to close/truncate the STDIN, so it won't read data twice.
Without any changes to your program, you can include password in stdin before json, eg (bash): {echo pass; cat data.json; } | goprog, or cat pass.txt data.json | goprog
For better method for password passing (eg environment or file descriptor) look at sshpass: https://linux.die.net/man/1/sshpass
You can also buffer all stdin, and reuse its content later (via io.Reader)
Redesign your application logic to function which accept io.Reader as source of data to unmarshall.
In main() pass os.Stdin to mentioned function if there is no file argument on command line, otherwise (try to) open file and pass it to unmarshalling function.
Note: for deciding whether to print prompt or not you may use isatty like function, which tells if stdin is interactive: https://github.com/mattn/go-isatty
You wont be able because the shell will close the stdin file descriptor once it has finished to write the content.
Once stdin is closed you cant re open it, this is controlled by the shell.
Check this program, to test this behavior
package main
import (
"fmt"
"io"
"os"
)
func main() {
io.Copy(os.Stdout, os.Stdin)
fmt.Println("done")
some := make([]byte, 100)
_, err := os.Stdin.Read(some)
fmt.Println(err)
fmt.Println(string(some))
}
The output will be
$ echo "some" | go run main.go
some
done
EOF
Isn't this Golang program supposed to output a directory listing to stdout?
It compiles ok, but does nothing.
package main
import "exec"
func main() {
argv := []string{"-la"}
envv := []string{}
exec.Run("ls", argv, envv, "", exec.DevNull, exec.PassThrough, exec.MergeWithStdout)
}
this works:
package main
import "exec"
func main() {
cmd, err := exec.Run("/bin/ls", []string{"/bin/ls", "-la"}, []string{}, "", exec.DevNull, exec.PassThrough, exec.PassThrough)
if (err != nil) {
return
}
cmd.Close()
}
You could also do it in native go using: ioutil.ReadDir(dir), like so:
//listdir.go
package main
import (
"os"
"io/ioutil"
"fmt"
)
func ListDir(dir string) ([]os.FileInfo, error) {
return ioutil.ReadDir(dir)
}
func main() {
dir := "./"
if len(os.Args) > 1 {
dir = os.Args[1]
}
fi, err := ListDir(dir)
if err != nil {
fmt.Println("Error", err)
}
for _, f := range fi {
d := "-"
if f.IsDir() { d = "d" }
fmt.Printf("%s %o %d %s %s\n", d, f.Mode() & 0777, f.Size(), f.ModTime().Format("Jan 2 15:04"), f.Name())
}
}
Checkout the documentation available for ioutil and os packages.
By default exec.Command will leave standard input, output and error connected to /dev/null. So, your 'ls' command is running fine but the output is just being thrown away. If you add:
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
before the exec.Run call then your output will go where you probably expect it.
exec.Run replaces your program with the one it executes -- it never returns to your app. This means that when 'cd' completes, it will exit as normal, and the only effect should be of changing the directory; 'ls' will never run.