How I make a simple interactive shell in Golang “command computer keyboard key” by on Hannah Joshua Unsplash So today, I learn something new and a bit of basic thing in Golang. It was making a simple interactive shell. It was just a very simple application, but it kinda looks cool to make it. I’ve been working over 1 year in Golang. So many tools already created by people out there, for example: cobra by , or many more that help us to make command line application. But, somehow I’m curious how to make a simple one without having dependencies with other external libraries. spf13 So just out of curiosity, I start to search how to make it in Google. But what I found is only tutorial using other external dependencies. Everyone is only promoting their own library to ease making an interactive shell 😌. Guys? I just need to make a simple one with the pure Golang package (No external dependencies)😌 But, I found an article about making an interactive shell, like this article: , but, what he does is not like I want. So later with only many examples that I found from websites like Gobyexample.com, etc. http://technosophos.com/2014/07/11/start-an-interactive-shell-from-within-go.html I made a simple Shell application by myself. Here I will explain how I made it below. Shell So before making a simple Shell application, I want to make sure that what I mean shell is the same as what you mean. For me, Shell is an application that will act as the very basic user interface (text-based interface). Some people might say it Command Line Interface (CLI). Making The Application To make the application, for the simplest prototype I will make it like this. $ . main. ls <br> go mod go Read the Command from Terminal To make this, the first thing to do is reading the input. I need to read the input from the terminal. To that, I make it like this. reader := bufio.NewReader(os.Stdin) cmdString, := reader.ReadString( ) err != nil { fmt.Fprintln(os.Stderr, err) } err '\n' if Execute the Command Then execute the command. After reading the command string from the terminal, now execute the command. cmdString = strings.TrimSuffix(cmdString, ) cmd := exec.Command(commandString) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout cmd.Run() "\n" Until here, now I can run my simple shell application, but it will only run for one command. After running a command, it will stop. Adding the Infinite Loop To make it receive every command after executed once. We need to add it to an infinite loop. { fmt.Print( ) cmdString, := reader.ReadString( ) err != nil { fmt.Fprintln(os.Stderr, err) } cmdString = strings.TrimSuffix(cmdString, ) cmd := exec.Command(cmdString) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout err = cmd.Run() err != nil { fmt.Fprintln(os.Stderr, err) } } for "$ " err '\n' if "\n" if If we run the application it will look like this below simple shell application Dealing with the Arguments Until this line, I’ve finished my first CLI application. But it still doesn’t accept arguments. If I pass an argument, then it will throw an error. $ -lah this wil throw error ls // command To read the arguments, I make it like this below. First I split the command into an array of string. cmdString = strings.TrimSuffix(cmdString, ) arrCommandStr := strings.Fields(cmdString) cmd := exec.Command(arrCommandStr[ ], arrCommandStr[ :]...) // ... "\n" 0 1 // ... For splitting the text, I use the Fields function from package string. This function is similar to the split string function. If string.Split will split the string by given specific separator. But strings.Fields will separate the words by whitespace. Example: str := arrString := strings.Fields(str) fmt.Println(arrString) "Hello World Beautiful World" // [Hello World Beautiful World] Now, my simple shell is already accepting and process the given params. Now, this command should be worked. $ ls -lah total drwxr-xr-x iman staff B Nov : . drwxr-xr-x iman staff B Nov : .. -rw-r--r-- iman staff B Nov : go.mod -rw-r--r-- iman staff B Nov : main.go -rwxr-xr-x iman staff M Nov : simshel 4280 5 160 6 19 48 6 192 6 11 41 1 38 6 11 43 1 606 6 20 11 1 2.1 6 19 49 Adding Exit Command But, this application is only worked for any built-in application that registered in the environment. Command like exit is not exist, because it is programmed in every CLI application. Then, I make a switch-case handler for each command that doesn’t have a built-in application in the system like the command. exit package main ( ) func main() { := bufio.NewReader(os.Stdin) { fmt.Print( ) cmdString, := reader.ReadString( ) err != nil { fmt.Fprintln(os.Stderr, err) } err = runCommand(cmdString) err != nil { fmt.Fprintln(os.Stderr, err) } } } func runCommand(commandStr string) error { commandStr = strings.TrimSuffix(commandStr, ) arrCommandStr := strings.Fields(commandStr) arrCommandStr[ ] { : os.Exit( ) } cmd := exec.Command(arrCommandStr[ ], arrCommandStr[ :]...) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout cmd.Run() } import "bufio" "fmt" "os" "os/exec" "strings" reader for "$ " err '\n' if if "\n" switch 0 case "exit" 0 // add another case here for custom commands. 0 1 return Adding the Custom Command To add custom commands, I could simply add it to the switch-case handler. For example, I want to add a command like . This command does not exist in any CLI application out there. I want to make it like this: plus $ plus <br> 2 4 5 6 17 To make this, I just need to add a sum function and add the command case handler in the switch-case Conclusion In the end, it’s only a very simple application. But for sure, I learn a few things when making this. And also, when writing this, I found a similar article with this article (here: ). After reading that article, I’m thinking of discarding my draft. But, I’ve written a lot of explanation and example here, so then I decided to post it anyway XD. https://sj14.gitlab.io/post/2018-07-01-go-unix-shell/ Anyway, I’ve put the source code in my Github too here: , if just some chance happens, I will try to add some feature into it later. https://github.com/bxcodec/simpleshell *Indonesian version: * Update: Add another example of custom command https://medium.com/easyread/today-i-learned-belajar-membuat-aplikasi-interactive-shell-sederhana-di-golang-2ef013003393