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.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