run cmds in shell, cd & exit builtins

This commit is contained in:
Andre Heber
2023-11-06 21:15:17 +01:00
parent f07acf2e81
commit 6c3fef61a6
4 changed files with 125 additions and 0 deletions

2
.gitignore vendored
View File

@ -21,3 +21,5 @@
# Go workspace file # Go workspace file
go.work go.work
# Output of go tooling
shell

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module gitea.futureblog.eu/andre/shell
go 1.21.1

94
main.go Normal file
View 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
View 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)
}
})
}
}