run cmds in shell, cd & exit builtins
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,3 +21,5 @@
|
|||||||
# Go workspace file
|
# Go workspace file
|
||||||
go.work
|
go.work
|
||||||
|
|
||||||
|
# Output of go tooling
|
||||||
|
shell
|
||||||
|
|||||||
94
main.go
Normal file
94
main.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Load the configuration file, if any
|
||||||
|
|
||||||
|
// Run command loop
|
||||||
|
myshell_loop()
|
||||||
|
|
||||||
|
// Perform any shutdown/cleanup
|
||||||
|
}
|
||||||
|
|
||||||
|
func myshell_loop() {
|
||||||
|
for {
|
||||||
|
// Read command
|
||||||
|
fmt.Printf("myshell> ")
|
||||||
|
line := read_line()
|
||||||
|
|
||||||
|
// Parse command
|
||||||
|
args := split_line(line)
|
||||||
|
|
||||||
|
// Execute command
|
||||||
|
output, err := execute(args)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error during exec command:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Loop back to read command
|
||||||
|
}
|
||||||
|
|
||||||
|
func read_line() string {
|
||||||
|
// Read the command line
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
input, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error reading command line: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the newline character
|
||||||
|
input = strings.TrimSuffix(input, "\r\n")
|
||||||
|
input = strings.TrimSuffix(input, "\n")
|
||||||
|
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the line into arguments
|
||||||
|
func split_line(line string) []string {
|
||||||
|
fieldSeparator := func(c rune) bool {
|
||||||
|
return unicode.IsSpace(c) || c == '\a'
|
||||||
|
}
|
||||||
|
args := strings.FieldsFunc(line, fieldSeparator)
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
var builtin_cmds = map[string]func([]string) ([]byte, error){
|
||||||
|
"cd": myshell_cd,
|
||||||
|
"exit": myshell_exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
func execute(args []string) ([]byte, error) {
|
||||||
|
// Check for builtin commands
|
||||||
|
if cmd, ok := builtin_cmds[args[0]]; ok {
|
||||||
|
return cmd(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("sh", "-c", strings.Join(args, " "))
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
return output, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func myshell_cd(args []string) ([]byte, error) {
|
||||||
|
if len(args) < 2 {
|
||||||
|
return nil, fmt.Errorf("cd: missing argument")
|
||||||
|
}
|
||||||
|
err := os.Chdir(args[1])
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func myshell_exit(args []string) ([]byte, error) {
|
||||||
|
os.Exit(0)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
26
main_test.go
Normal file
26
main_test.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSplitLine(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
line string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{"", []string{}},
|
||||||
|
{"arg1 arg2 arg3", []string{"arg1", "arg2", "arg3"}},
|
||||||
|
{" arg1\targ2\narg3\rarg4\aarg5 ", []string{"arg1", "arg2", "arg3", "arg4", "arg5"}},
|
||||||
|
{"\"quoted arg\" 'another arg'", []string{"\"quoted", "arg\"", "'another", "arg'"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.line, func(t *testing.T) {
|
||||||
|
if got := split_line(tt.line); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("split_line(%q) = %v, want %v", tt.line, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user