I want to use GO to run an asynchronous command on windows 10. The command I am running is blocking, and if run directly from the terminal it provides a constant steam of status messages until ctrl-c
I want to run that command from GO via exec and catch the output from the exec command to the terminal in real time, i.e. not only when the GO application terminates.
I have tried numerous examples but with not success, I just get a blank terminal and even after exiting the GO application, I don't see the output from the command I executed.
You can use cmd.StdoutPipe to do that:
cmd := exec.Command(cmdName, cmdArgs...)
cmdReader, _ := cmd.StdoutPipe()
scanner := bufio.NewScanner(cmdReader)
done := make(chan bool)
go func() {
for scanner.Scan() {
fmt.Printf(scanner.Text())
}
done <- true
}()
cmd.Start()
<- done
err = cmd.Wait()
You can use io.MultiWriter to capture output and forward it to stdout and stderr.
var stdoutBuf, stderrBuf bytes.Buffer
cmd := exec.Command("/some-command")
cmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
cmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
err := cmd.Start() // Starts command asynchronously
if err != nil {
fmt.Printf(err.Error())
}
Related
I'm trying to get a list of system interfaces on my machine, I'm using the net package, which is defined here.
I have this tiny little snippet of code, which crashes when trying to print out the error, and I can't figure out why the error is sigsev'ing on me. It's supposed to return an error, or nil right?
I've ran this code with sudo, root and under a regular user account thinking it might be a permissions thing for the network interface, but it persists amongst all user levels.
package main
import (
"net"
"fmt"
)
func main() {
var err error
var interfaces []net.Interface
var ifString []string
interfaces, err = net.Interfaces()
fmt.Printf("%v",interfaces)
if err != nil {
for _, v := range interfaces {
ifString = append(ifString, v.Name)
}
} else {
fmt.Printf(err.Error())
ifString = append(ifString, "unable to get system interfaces")
}
}
Program output is as follows when running go build and executing it:
[{1 65536 lo up|loopback} {2 1500 eno1 b8:cc:3c:8e:d4:d3 up|broadcast|multicast} {9 1500 tun0 up|pointtopoint|multicast}]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x4a5269]
goroutine 1 [running]:
main.main()
/home/andrew/interface/borked.go:20 +0x269
You are receiving a nil pointer dereference because that's exactly what you're doing.
Given the following if statement in your code:
if err != nil {
for _, v := range interfaces {
ifString = append(ifString, v.Name)
}
} else {
fmt.Printf(err.Error())
...
}
The else is reached when err is nil. Yet you are attempting to access a field in it: err.Error(), dereferencing a nil pointer.
You need to invert your if statement, it's the wrong way around.
I am trying to run some autoit.au3 script from command line and see results there. I have put some ConsoleWrite inside script and also Exit(1) but after I run script nothing is shown in console. It just stop script on Exit and ConsoleWrite is not displayed.
I have use command:
"C:...(path to my AutoIt3.exe)" /ErrorStdOut "path_to_my_script.au3"'
Also I have tried to run script.exe with this same command but with similar (no) result. I would like to see output in console and/or custom error messages when script fail (I don't know if that is possible).
AutoIt3.exe is a GUI program. So the STD streams of a GUI program are not printed at a console by default.
The /ErrorStdOut argument redirects errors messages to Console instead of a Msgbox.
This argument does not enable print at the Console.
Command Prompt:
To print at a Command Prompt, you could pipe to more, i.e.
"C:...(path to my AutoIt3.exe)" /ErrorStdOut "path_to_my_script.au3" 2>&1|more
more reads from the Stdin stream and prints to Console.
I intentionly added 2>&1 so the Stderr stream is merged with
Stdout so you get the merged streams printed.
If you do not want the errors, then you can redirect the Stderr stream to nul i.e.
replace 2>&1 with 2>nul.
If you used a for loop at a Command Prompt, it would be i.e.
for /f "delims=" %A in ('"C:...(path to my AutoIt3.exe)" /ErrorStdOut test1.au3') do echo %A
If you use the for loop in batch-file, use %%A instead of %A. To also capture the Stderr, insert 2^>&1 into the for command or to ignore, insert 2^>nulinto the for command i.e.
for /f "delims=" %A in ('2^>nul "C:...(path to my AutoIt3.exe)" /ErrorStdOut test1.au3') do echo %A
The previous methods will not get the Exitcode.
AutoIt code:
An AutoIt script can get the Stdout and the Exitcode.
#pragma compile(Out, 'consoleau3.exe')
#pragma compile(Console, True)
$sAutoit = 'C:...(path to my AutoIt3.exe)'
$iPid = Run('"' & $sAutoit & '" /ErrorStdout ' & $CMDLINERAW, '', #SW_SHOW, 2) ; 2 = Get Stdout stream.
If #error Then Exit
; Open process handle.
$hPid = _ProcessOpenHandle($iPid)
; Get Stdout stream and then print to Console.
$sStdout = ''
Do
Sleep(10)
If $sStdout Then ConsoleWrite($sStdout & #CRLF)
$sStdout = StdoutRead($iPid)
Until #error
; Require process to be closed before calling _ProcessGetExitCode()
ProcessWaitClose($iPid)
; Get exitcode of process.
$iExitcode = _ProcessGetExitCode($hPid)
; Close process handle.
_ProcessCloseHandle($hPid)
Exit $iExitcode
Func _ProcessOpenHandle($iPID)
; Get the process handle of the process to query\n Return: Success Handle as array. Failure 0
Local Const $PROCESS_QUERY_INFORMATION = 0x400
Local $hPID = DllCall('kernel32.dll', 'ptr', 'OpenProcess', 'int', $PROCESS_QUERY_INFORMATION, 'int', 0, 'int', $iPID)
If #error Then Return SetError(#error, #extended, 0)
Return $hPID[0]
EndFunc
Func _ProcessGetExitcode($hPID)
; Get exitcode of the closed process\n Return: Success Exitcode as integer. Failure 0
Local $vPlaceholder
$hPID = DllCall('kernel32.dll', 'ptr', 'GetExitCodeProcess', 'ptr', $hPID, 'int*', $vPlaceholder)
If #error Then Return SetError(#error, #extended, 0)
Return $hPID[2]
EndFunc
Func _ProcessCloseHandle($hPID)
; Close the handle of a process\n Return: Success 1. Failure 0
DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $hPID)
If #error Then Return SetError(#error, #extended, 0)
Return 1
EndFunc
Correct the path to AutoIt.exe in the code.
Compile to AutoIt code to executable. It will be a Console program
and will be named consoleau3.exe as to the #pragma compile directives.
Usage:
consoleau3 "path_to_my_script.au3"
Script arguments can be added i.e.
consoleau3 "path_to_my_script.au3" arg1 arg2 arg3 ...
I have an issue with Go's http.Server, which I'm embedding in a struct that is supposed to control the server startup and shutdown. The struct looks like this:
type HTTPListen struct {
Consumers []pipeline.Consumer
Cfg HTTPListenConfig
Srv *http.Server
Logger log.Logger
wg *sync.WaitGroup
mu sync.Mutex
state State
}
The issue is that in my test code, I call my struct's Start() method (which in turn runs the Serve() method on the http.Server), check a few vars, and then call Stop(), whitch Shutdown()s the server and then waits for the http.Server to exit (return err from the Serve() method).
Now, for some reason, the Serve() method seems to just hang on the WaitGroup.Wait(), when I try to shutdown the server immediately after starting. When I add a short pause (tried 100ms), or when running the tests with the race detector, It works just fine.
Not sure if it matters, but there are no incoming requests between calling Serve() and Shutdown().
EDIT: link to a playground minimal example. If you comment out the time.Sleep call, the program hangs.
Here is the relevant code for the two methods:
func (h *HTTPListen) Start() error {
h.Logger.Log("msg", "starting HTTPListen input")
addr := h.Cfg.ListenAddr
ln, err := net.Listen("tcp", addr)
if err != nil {
h.Logger.Log("msg", "failed to create listener on tcp/"+addr+": "+err.Error())
h.setState(StateFailed)
return err
}
h.wg.Add(1)
go func() {
defer h.wg.Done()
err := h.Srv.Serve(ln)
h.Logger.Log("msg", "HTTP server stopped: "+err.Error())
}()
h.setState(StateStarted)
h.Logger.Log("msg", "HTTPListen input started")
return nil
}
Stop method:
func (h *HTTPListen) Stop() error {
h.Logger.Log("msg", "stopping HTTPListen input")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
if err := h.Srv.Shutdown(ctx); err != nil {
h.Logger.Log("msg", "HTTP server shutdown deadline expired")
}
h.wg.Wait()
h.setState(StateStopped)
h.Logger.Log("msg", "HTTPListen input stopped")
return nil
}
Log output:
kwz#cyclone ~/s/stblogd> go test -v ./pkg/pipeline/input/ -run TestHTTPListen_StartStop
=== RUN TestHTTPListen_StartStop
msg="starting HTTPListen input"
msg="HTTPListen input started"
msg="stopping HTTPListen input"
... hangs indefinitely
Log output when running tests with the race detector:
kwz#cyclone ~/s/stblogd> go test -race -v ./pkg/pipeline/input/ -run TestHTTPListen_StartStop
=== RUN TestHTTPListen_StartStop
msg="starting HTTPListen input"
msg="HTTPListen input started"
msg="stopping HTTPListen input"
msg="HTTP server stopped: http: Server closed"
msg="HTTPListen input stopped"
--- PASS: TestHTTPListen_StartStop (0.00s)
PASS
ok stblogd/pkg/pipeline/input 1.007s
I'm tempted to just slap a short delay on the test and call it a day, but I would like to know why it behaves like this.
This is a known issue, see this thread:
https://github.com/golang/go/issues/20239
Hopefully they will fix it soon but for now it sounds like adding a short delay in your test is the simplest solution - it probably doesn't come up in real world use much because you won't trigger a shutdown so soon after starting.
I'm looking for a way to mimick a terminal for some automated testing: i.e. start a process and then interact with it via sending data to stdin and reading from stdout. E.g. sending some lines of input to stdin including ctrl-c and ctrl-\ which should result in sending signals to the process.
Using std::process::Commannd I'm able to send input to e.g. cat and I'm also seeing its output on stdout, but sending ctrl-c (as I understand that is 3) does not cause SIGINT sent to the shell. E.g. this program should terminate:
use std::process::{Command, Stdio};
use std::io::Write;
fn main() {
let mut child = Command::new("sh")
.arg("-c").arg("-i").arg("cat")
.stdin(Stdio::piped())
.spawn().unwrap();
let mut stdin = child.stdin.take().unwrap();
stdin.write(&[3]).expect("cannot send ctrl-c");
child.wait();
}
I suspect the issue is that sending ctrl-c needs the some tty and via sh -i it's only in "interactive mode".
Do I need to go full fledged and use e.g. termion or ncurses?
Update: I confused shell and terminal in the original question. I cleared this up now. Also I mentioned ssh which should have been sh.
The simplest way is to directly send the SIGINT signal to the child process. This can be done easily using nix's signal::kill function:
// add `nix = "0.15.0"` to your Cargo.toml
use std::process::{Command, Stdio};
use std::io::Write;
fn main() {
// spawn child process
let mut child = Command::new("cat")
.stdin(Stdio::piped())
.spawn().unwrap();
// send "echo\n" to child's stdin
let mut stdin = child.stdin.take().unwrap();
writeln!(stdin, "echo");
// sleep a bit so that child can process the input
std::thread::sleep(std::time::Duration::from_millis(500));
// send SIGINT to the child
nix::sys::signal::kill(
nix::unistd::Pid::from_raw(child.id() as i32),
nix::sys::signal::Signal::SIGINT
).expect("cannot send ctrl-c");
// wait for child to terminate
child.wait().unwrap();
}
You should be able to send all kinds of signals using this method. For more advanced "interactivity" (e.g. child programs like vi that query terminal size) you'd need to create a pseudoterminal like #hansaplast did in his solution.
After a lot of research I figured out it's not too much work to do the pty fork myself. There's pty-rs, but it has bugs and seems unmaintained.
The following code needs pty module of nix which is not yet on crates.io, so Cargo.toml needs this for now:
[dependencies]
nix = {git = "https://github.com/nix-rust/nix.git"}
The following code runs cat in a tty and then writes/reads from it and sends Ctrl-C (3):
extern crate nix;
use std::path::Path;
use nix::pty::{posix_openpt, grantpt, unlockpt, ptsname};
use nix::fcntl::{O_RDWR, open};
use nix::sys::stat;
use nix::unistd::{fork, ForkResult, setsid, dup2};
use nix::libc::{STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::io::prelude::*;
use std::io::{BufReader, LineWriter};
fn run() -> std::io::Result<()> {
// Open a new PTY master
let master_fd = posix_openpt(O_RDWR)?;
// Allow a slave to be generated for it
grantpt(&master_fd)?;
unlockpt(&master_fd)?;
// Get the name of the slave
let slave_name = ptsname(&master_fd)?;
match fork() {
Ok(ForkResult::Child) => {
setsid()?; // create new session with child as session leader
let slave_fd = open(Path::new(&slave_name), O_RDWR, stat::Mode::empty())?;
// assign stdin, stdout, stderr to the tty, just like a terminal does
dup2(slave_fd, STDIN_FILENO)?;
dup2(slave_fd, STDOUT_FILENO)?;
dup2(slave_fd, STDERR_FILENO)?;
std::process::Command::new("cat").status()?;
}
Ok(ForkResult::Parent { child: _ }) => {
let f = unsafe { std::fs::File::from_raw_fd(master_fd.as_raw_fd()) };
let mut reader = BufReader::new(&f);
let mut writer = LineWriter::new(&f);
writer.write_all(b"hello world\n")?;
let mut s = String::new();
reader.read_line(&mut s)?; // what we just wrote in
reader.read_line(&mut s)?; // what cat wrote out
writer.write(&[3])?; // send ^C
writer.flush()?;
let mut buf = [0; 2]; // needs bytewise read as ^C has no newline
reader.read(&mut buf)?;
s += &String::from_utf8_lossy(&buf).to_string();
println!("{}", s);
println!("cat exit code: {:?}", wait::wait()?); // make sure cat really exited
}
Err(_) => println!("error"),
}
Ok(())
}
fn main() {
run().expect("could not execute command");
}
Output:
hello world
hello world
^C
cat exit code: Signaled(2906, SIGINT, false)
Try adding -t option TWICE to force pseudo-tty allocation. I.e.
klar (16:14) ~>echo foo | ssh user#host.ssh.com tty
not a tty
klar (16:14) ~>echo foo | ssh -t -t user#host.ssh.com tty
/dev/pts/0
When you have a pseudo-tty, I think it should convert that to SIGINT as you wanted to do.
In your simple example, you could also just close stdin after the write, in which case the server should exit. For this particular case it would be more elegant and probably more reliable.
Solution without using a crate
Now that you are spawning a command in Rust, you might as well spawn another to send SIGINT to it. That command is kill.
So, you can do this:
use std::process::{Command, Stdio};
use std::io::{Result, Write};
fn main() -> Result<()> {
let mut child = Command::new("sh")
.arg("-c").arg("-i").arg("cat")
.stdin(Stdio::piped())
.spawn()?;
let mut stdin = child.stdin.take().unwrap();
let mut kill = Command::new("kill")
.arg(child.id().to_string())
.spawn()?;
kill.wait()
}
I want to write and build and execute a Pascal Program in Notepad ++. If i execute the program in cmd the output is normal, but in the console in nppexec the output is empty
My Code:
Program Edgar;
Uses Crt;
Var cnt, tip, pot : INTEGER;
Begin
TextColor(Red);
WriteLn('Hallo');
tip := -1;
cnt := 0;
Randomize;
pot := Random(2501)*10;
WriteLn(pot);
WHILE (tip <> pot) do
Begin
WriteLn('Tip: ');
ReadLn(tip);
if (tip < pot) then begin
WriteLn('Too low');
cnt := cnt + 1
end;
if (tip > pot) then begin
WriteLn('Too High');
cnt := cnt + 1
end;
end;
cnt:= cnt + 1;
WriteLn('IA IA');
WriteLn('Tries: ',cnt );
End.
Build Commands:
cd $(CURRENT_DIRECTORY)
fpc $(NAME_PART).pas
$(NAME_PART).exe
Output(Nppexec):
Free Pascal Compiler version 2.6.2 [2013/02/12]
for i386 Copyright (c) 1993-2012 by Florian Klaempfl
and others Target OS: Win32 for i386
Compiling ue23.pas
Linking ue23.exe 27 lines compiled, 0.1 sec , 33536 bytes code, 1900 bytes data
<<< Process finished.
(Exit code 0)
ue23.exe Process started >>>
If you enable unit CRT, the application will write to the console directly (using *console winapi functions) instead of using stdout.
Probably the console screen of npp is not a real console screen, but a capture of stdout (-piped) only.
Except not using crt (and thus not using cursor movement and coloring) there is not much that can be done, this is probably a NPP limitation.
After that, you need to press "Enter" while your cursor blinking in output side.
And you will get the output with these lines at the end.
<<< Process finished. (Exit code 0)
================ READY ================
There is no limitation, you can run commands from that output side of notepad++.