Skip to content

Commit b4a658f

Browse files
committed
first commit
0 parents  commit b4a658f

6 files changed

Lines changed: 438 additions & 0 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.git
2+
.svn
3+
.vscode
4+
.idea

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) 2024 web@tkdeng.com
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

bash.go

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
package bash
2+
3+
import (
4+
"io"
5+
"os"
6+
"os/exec"
7+
)
8+
9+
/*
10+
Run will run a bash command based on the given args
11+
12+
note: stdin is piped to the os logs
13+
14+
@dir: a directory to run the command in (set to an empty string to disable)
15+
@env: an optional list of environment variables (set to nil to disable)
16+
17+
[optional]
18+
@liveOutput[0]: set to true to pipe stdout and stderr to the os
19+
@liveOutput[1]: set to false to only pipe stdout to the os, and keep stderr hidden
20+
*/
21+
func Run(args []string, dir string, env []string, liveOutput ...bool) (output []byte, err error) {
22+
arg1 := args[0]
23+
args = args[1:]
24+
cmd := exec.Command(arg1, args...)
25+
if dir != "" {
26+
cmd.Dir = dir
27+
}
28+
if cmd.Env == nil {
29+
cmd.Env = os.Environ()
30+
}
31+
if env != nil {
32+
cmd.Env = append(cmd.Env, env...)
33+
}
34+
cmd.Stdin = os.Stdin
35+
if len(liveOutput) != 0 && liveOutput[0] == true {
36+
cmd.Stdout = os.Stdout
37+
if len(liveOutput) <= 1 || liveOutput[1] == true {
38+
cmd.Stderr = os.Stderr
39+
}
40+
return []byte{}, cmd.Run()
41+
}
42+
return cmd.CombinedOutput()
43+
}
44+
45+
/*
46+
RunRaw will run an unescaped (unquoted) bash command as a different user
47+
48+
this method uses `bash -c` to get around the auto quotes added by golang
49+
50+
note: user input is Not recommended for this method
51+
52+
note: stdin is piped to the os logs
53+
54+
@cmdStr: the command to run
55+
@dir: a directory to run the command in (set to an empty string to disable)
56+
@env: an optional list of environment variables (set to nil to disable)
57+
58+
[optional]
59+
@liveOutput[0]: set to true to pipe stdout and stderr to the os
60+
@liveOutput[1]: set to false to only pipe stdout to the os, and keep stderr hidden
61+
*/
62+
func RunRaw(cmdStr string, dir string, env []string, liveOutput ...bool) (output []byte, err error) {
63+
cmd := exec.Command(`bash`, `-c`, cmdStr)
64+
if dir != "" {
65+
cmd.Dir = dir
66+
}
67+
if cmd.Env == nil {
68+
cmd.Env = os.Environ()
69+
}
70+
if env != nil {
71+
cmd.Env = append(cmd.Env, env...)
72+
}
73+
cmd.Stdin = os.Stdin
74+
if len(liveOutput) != 0 && liveOutput[0] == true {
75+
cmd.Stdout = os.Stdout
76+
if len(liveOutput) <= 1 || liveOutput[1] == true {
77+
cmd.Stderr = os.Stderr
78+
}
79+
return []byte{}, cmd.Run()
80+
}
81+
return cmd.CombinedOutput()
82+
}
83+
84+
/*
85+
RunUser will run an unescaped (unquoted) bash command as a specified user
86+
87+
this method uses `runuser -l [user] -c`
88+
89+
note: user input is Not recommended for this method
90+
91+
note: stdin is piped to the os logs
92+
93+
@cmdStr: the command to run
94+
@user: the username to run the command as
95+
@dir: a directory to run the command in (set to an empty string to disable)
96+
@env: an optional list of environment variables (set to nil to disable)
97+
98+
[optional]
99+
@liveOutput[0]: set to true to pipe stdout and stderr to the os
100+
@liveOutput[1]: set to false to only pipe stdout to the os, and keep stderr hidden
101+
*/
102+
func RunUser(cmdStr string, user string, dir string, env []string, liveOutput ...bool) (output []byte, err error) {
103+
cmd := exec.Command(`runuser`, `-l`, user, `-c`, cmdStr)
104+
if dir != "" {
105+
cmd.Dir = dir
106+
}
107+
if cmd.Env == nil {
108+
cmd.Env = os.Environ()
109+
}
110+
if env != nil {
111+
cmd.Env = append(cmd.Env, env...)
112+
}
113+
cmd.Stdin = os.Stdin
114+
if len(liveOutput) != 0 && liveOutput[0] == true {
115+
cmd.Stdout = os.Stdout
116+
if len(liveOutput) <= 1 || liveOutput[1] == true {
117+
cmd.Stderr = os.Stderr
118+
}
119+
return []byte{}, cmd.Run()
120+
}
121+
return cmd.CombinedOutput()
122+
}
123+
124+
/*
125+
RunUserFile will run a bash file as a specified user
126+
127+
this method uses `pkexec --user [user]` to simulate a user in a normal desktop environment
128+
129+
note: user input is Not recommended for this method
130+
131+
note: stdin is piped to the os logs
132+
133+
@file: the file to run
134+
@user: the username to run the command as
135+
@dir: a directory to run the command in (set to an empty string to disable)
136+
@env: an optional list of environment variables (set to nil to disable)
137+
138+
[optional]
139+
@liveOutput[0]: set to true to pipe stdout and stderr to the os
140+
@liveOutput[1]: set to false to only pipe stdout to the os, and keep stderr hidden
141+
*/
142+
func RunUserFile(file string, args []string, user string, dir string, env []string, liveOutput ...bool) (output []byte, err error) {
143+
cmd := exec.Command(`pkexec`, append([]string{`--user`, user, file}, args...)...)
144+
if dir != "" {
145+
cmd.Dir = dir
146+
}
147+
if cmd.Env == nil {
148+
cmd.Env = os.Environ()
149+
}
150+
if env != nil {
151+
cmd.Env = append(cmd.Env, env...)
152+
}
153+
cmd.Stdin = os.Stdin
154+
if len(liveOutput) != 0 && liveOutput[0] == true {
155+
cmd.Stdout = os.Stdout
156+
if len(liveOutput) <= 1 || liveOutput[1] == true {
157+
cmd.Stderr = os.Stderr
158+
}
159+
return []byte{}, cmd.Run()
160+
}
161+
return cmd.CombinedOutput()
162+
}
163+
164+
/*
165+
Pipe allows you to pipe multiple bash commands
166+
167+
[example (bash)]
168+
echo "test" | tee -a "./test.txt"
169+
170+
[example (go)]
171+
bash.Pipe(".", []string{"echo", "test"}, []string{"tee", "-a", "./test.txt"})
172+
173+
@dir: a directory to run the command in (set to an empty string to disable)
174+
*/
175+
func Pipe(dir string, args ...[]string){
176+
if len(args) == 1 {
177+
arg1 := args[0][0]
178+
args1 := args[0][1:]
179+
c1 := exec.Command(arg1, args1...)
180+
c1.Stdout = os.Stdout
181+
}
182+
183+
cmd := []*exec.Cmd{}
184+
185+
arg0 := args[0][0]
186+
args0 := args[0][1:]
187+
cmd = append(cmd, exec.Command(arg0, args0...))
188+
cmd[0].Stdin = os.Stdin
189+
if dir != "" {
190+
cmd[0].Dir = dir
191+
}
192+
193+
if cmd[0].Env == nil {
194+
cmd[0].Env = os.Environ()
195+
}
196+
197+
for i := 1; i < len(args); i++ {
198+
arg0 = args[i][0]
199+
args0 = args[i][1:]
200+
cmd = append(cmd, exec.Command(arg0, args0...))
201+
202+
pr, pw := io.Pipe()
203+
cmd[i-1].Stdout = pw
204+
cmd[i].Stdin = pr
205+
if dir != "" {
206+
cmd[i].Dir = dir
207+
}
208+
209+
if cmd[i].Env == nil {
210+
cmd[i].Env = os.Environ()
211+
}
212+
213+
cmd[i-1].Start()
214+
215+
go func(i int){
216+
defer pw.Close()
217+
218+
cmd[i-1].Wait()
219+
}(i)
220+
}
221+
222+
cmd[len(cmd)-1].Stdout = os.Stdout
223+
224+
cmd[len(cmd)-1].Start()
225+
cmd[len(cmd)-1].Wait()
226+
}
227+
228+
/*
229+
PipeMultiDir allows you to pipe multiple bash commands with a different directory for each of them
230+
231+
note: the first arg is the directory
232+
233+
[example]
234+
bash.PipeMultiDir([]string{"/dir1", "cat", "test.txt"}, []string{"./dir2", "tee", "-a", "./test.txt"})
235+
*/
236+
func PipeMultiDir(args ...[]string){
237+
if len(args) == 1 {
238+
arg1 := args[0][0]
239+
args1 := args[0][1:]
240+
c1 := exec.Command(arg1, args1...)
241+
c1.Stdout = os.Stdout
242+
}
243+
244+
cmd := []*exec.Cmd{}
245+
246+
dir := args[0][0]
247+
arg0 := args[0][1]
248+
args0 := args[0][2:]
249+
cmd = append(cmd, exec.Command(arg0, args0...))
250+
cmd[0].Stdin = os.Stdin
251+
if dir != "" {
252+
cmd[0].Dir = dir
253+
}
254+
255+
if cmd[0].Env == nil {
256+
cmd[0].Env = os.Environ()
257+
}
258+
259+
for i := 1; i < len(args); i++ {
260+
dir = args[i][0]
261+
arg0 = args[i][1]
262+
args0 = args[i][2:]
263+
cmd = append(cmd, exec.Command(arg0, args0...))
264+
265+
pr, pw := io.Pipe()
266+
cmd[i-1].Stdout = pw
267+
cmd[i].Stdin = pr
268+
if dir != "" {
269+
cmd[i].Dir = dir
270+
}
271+
272+
if cmd[i].Env == nil {
273+
cmd[i].Env = os.Environ()
274+
}
275+
276+
cmd[i-1].Start()
277+
278+
go func(i int){
279+
defer pw.Close()
280+
281+
cmd[i-1].Wait()
282+
}(i)
283+
}
284+
285+
cmd[len(cmd)-1].Stdout = os.Stdout
286+
287+
cmd[len(cmd)-1].Start()
288+
cmd[len(cmd)-1].Wait()
289+
}
290+
291+
/*
292+
PipeMultiDirEnv is just like the 'PipeMultiDir' method, but it also allows you to add custom envirronment vars
293+
294+
note: the first arg is the directory
295+
296+
[example]
297+
bash.PipeMultiDirEnv([]string{`MyEnvVar=CustomValue`}, []string{"/dir1", "cat", "test.txt"}, []string{"./dir2", "tee", "-a", "./test.txt"})
298+
*/
299+
func PipeMultiDirEnv(env []string, args ...[]string){
300+
if len(args) == 1 {
301+
arg1 := args[0][0]
302+
args1 := args[0][1:]
303+
c1 := exec.Command(arg1, args1...)
304+
c1.Stdout = os.Stdout
305+
}
306+
307+
cmd := []*exec.Cmd{}
308+
309+
dir := args[0][0]
310+
arg0 := args[0][1]
311+
args0 := args[0][2:]
312+
cmd = append(cmd, exec.Command(arg0, args0...))
313+
cmd[0].Stdin = os.Stdin
314+
if dir != "" {
315+
cmd[0].Dir = dir
316+
}
317+
318+
if cmd[0].Env == nil {
319+
cmd[0].Env = os.Environ()
320+
}
321+
if env != nil {
322+
cmd[0].Env = append(cmd[0].Env, env...)
323+
}
324+
325+
for i := 1; i < len(args); i++ {
326+
dir = args[i][0]
327+
arg0 = args[i][1]
328+
args0 = args[i][2:]
329+
cmd = append(cmd, exec.Command(arg0, args0...))
330+
331+
pr, pw := io.Pipe()
332+
cmd[i-1].Stdout = pw
333+
cmd[i].Stdin = pr
334+
if dir != "" {
335+
cmd[i].Dir = dir
336+
}
337+
338+
if cmd[i].Env == nil {
339+
cmd[i].Env = os.Environ()
340+
}
341+
if env != nil {
342+
cmd[i].Env = append(cmd[i].Env, env...)
343+
}
344+
345+
cmd[i-1].Start()
346+
347+
go func(i int){
348+
defer pw.Close()
349+
350+
cmd[i-1].Wait()
351+
}(i)
352+
}
353+
354+
cmd[len(cmd)-1].Stdout = os.Stdout
355+
356+
cmd[len(cmd)-1].Start()
357+
cmd[len(cmd)-1].Wait()
358+
}

0 commit comments

Comments
 (0)