1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
package fhelp
import (
"errors"
"time"
"github.com/go-cmd/cmd"
"go.wit.com/log"
)
func RunRealtime(args []string) cmd.Status {
return PathRunRealtime("", args)
}
// this is stuff from a long time ago that there must be a replacement for
func RemoveFirstElement(slice []string) (string, []string) {
if len(slice) == 0 {
return "", slice // Return the original slice if it's empty
}
return slice[0], slice[1:] // Return the slice without the first element
}
// echos twice a second if anything sends to STDOUT or STDERR
// not great, but it's really just for watching things run in real time anyway
// TODO: fix \r handling for things like git-clone so the terminal doesn't
// have to do a \n newline each time.
// TODO: add timeouts and status of things hanging around forever
func PathRunRealtime(pwd string, args []string) cmd.Status {
// Check if the slice has at least one element (the command name)
if len(args) == 0 {
var s cmd.Status
s.Error = errors.New("Error: Command slice is empty.")
return s
}
// Start a long-running process, capture stdout and stderr
a, b := RemoveFirstElement(args)
findCmd := cmd.NewCmd(a, b...)
if pwd != "" {
findCmd.Dir = pwd
}
statusChan := findCmd.Start() // non-blocking
ticker := time.NewTicker(100 * time.Microsecond)
// this is interesting, maybe useful, but wierd, but neat. interesting even
// Print last line of stdout every 2s
go func() {
// loop very quickly, but only print the line if it changes
var lastout string
var lasterr string
for range ticker.C {
status := findCmd.Status()
n := len(status.Stdout)
if n != 0 {
newline := status.Stdout[n-1]
if lastout != newline {
lastout = newline
log.Info(lastout)
}
}
n = len(status.Stderr)
if n != 0 {
newline := status.Stderr[n-1]
if lasterr != newline {
lasterr = newline
log.Info(lasterr)
}
}
if status.Complete {
return
}
}
}()
// Stop command after 1 hour
go func() {
<-time.After(1 * time.Hour)
findCmd.Stop()
}()
// Check if command is done
select {
case finalStatus := <-statusChan:
log.Info("finalStatus =", finalStatus.Exit, finalStatus.Error)
return finalStatus
// done
default:
// no, still running
}
// Block waiting for command to exit, be stopped, or be killed
// there are things being left around here. debug this
finalStatus := <-statusChan
if len(finalStatus.Cmd) != 0 {
if string(finalStatus.Cmd) != "go" {
log.Info("shell.Run() ok goroutine end?", finalStatus.Cmd, finalStatus.Exit)
}
}
return findCmd.Status()
}
|