Go: CLI & REPL

Go: CLI & REPL

Design pattern for console programming in Go

ยท

7 min read

CLI stands for Command Line Interface, which is a type of user interface that allows users to interact with a computer or a software application by typing commands into a terminal or command prompt. The CLI is a powerful tool that developers and system administrators use to perform a variety of tasks, from managing files and directories, to running complex scripts and executing programs.

CLI Examples

In this article we will use Go examples but the design pattern you learn for Go can be used in other languages like: Python, Ruby, Rust etc. Learning CLI is a fundamental skill.

Console Output

Go is a programming language that provides a rich standard library for building command-line tools, making it an excellent choice for developing CLI applications. Here is a simple example of a CLI program written in Go:

package main

import (
  "fmt"
  "os"
)

func main() {
   if len(os.Args) > 1 {
      fmt.Println("Hello,", os.Args[1])
   } else {
      fmt.Println("Hello, World!")
   }
}

Let's go through the code line by line:

  • The package main statement defines the package name as main, indicating that this is a standalone executable program, rather than a library or package.

  • The import statement includes the fmt and os packages, which are required for handling command-line input and output.

  • The main() function is the entry point of the program. It checks whether any command-line arguments were provided, and if so, prints a personalized greeting message. If no arguments were provided, it prints a generic greeting message.

To run this program, save the above code in a file named hello.go, then open a command prompt/terminal and navigate to the directory containing the file. Then, you can compile and run the program using the following commands:

go build hello.go
./hello Bob

The go build command compiles the program into an executable file named hello. The ./hello Bob command runs the program, passing in a command-line argument "Bob". The program will output "Hello, Bob" as its response.

Overall, the CLI is an important tool for developers and system administrators, and Go provides a convenient and powerful way to build command-line applications.


Interactive CLI

Here's an updated example that includes reading input from the console:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    reader := bufio.NewReader(os.Stdin)

    fmt.Print("What is your name? ")
    name, _ := reader.ReadString('\n')

    // Remove newline character from the input
    name = strings.TrimSuffix(name, "\n")

    if name != "" {
        fmt.Printf("Hello, %s!\n", name)
    } else {
        fmt.Println("Hello, World!")
    }
}

In this version of the program, we use the bufio package to create a reader that reads input from os.Stdin, which is the standard input stream. We then ask the user for their name by printing the prompt "What is your name?" and calling reader.ReadString('\n') to read the input until the user presses the enter key.

We then use the strings.TrimSuffix() function to remove the newline character (\n) that is included in the input by default. Finally, we check if the name is not empty (i.e., the user entered a name) and print out a personalized greeting message using fmt.Printf(). If the user did not enter any input, we print a generic greeting message.

To run the program, save the above code in a file named hello.go, then open a command prompt/terminal and navigate to the directory containing the file. Then, you can compile and run the program using the following commands:

go build hello.go
./hello

The program will prompt you to enter your name, and will output a personalized greeting message after you press enter.

Clear the console

To clear the console in Go and then print some output, you can use the os/exec package to run a command-line command to clear the console, and then use fmt.Println() to print the output. Here's an example:

package main

import (
    "fmt"
    "os/exec"
)

func main() {
    cmd := exec.Command("clear") // Run the "clear" command on UNIX/Linux or "cls" on Windows
    cmd.Stdout = &myWriter{}     // Set the standard output of the command to a custom writer

    err := cmd.Run()
    if err != nil {
        fmt.Println("Failed to clear console:", err)
    }

    fmt.Println("Hello there! This is a cleared console.")
}

// A custom writer that does nothing
type myWriter struct{}

func (w *myWriter) Write(p []byte) (n int, err error) {
    return len(p), nil
}

In this example, we create a cmd variable that holds an exec.Command struct that represents the command we want to run. We set the Path field of the struct to "clear", which is the command used to clear the console on UNIX/Linux. On Windows, you can replace this with "cls".

We also create a custom writer called myWriter that does nothing when output is written to it. We set the cmd.Stdout field to this writer so that the output of the command is discarded, rather than being printed to the console.

Finally, we call cmd.Run() to execute the command and clear the console, and then use fmt.Println() to print a simple message to the cleared console.

To run the program, save the above code in a file named clear-console.go, then open a command prompt/terminal and navigate to the directory containing the file. Then, you can compile and run the program using the following commands:

go build clear-console.go
./clear-console

When you run the program, it will clear the console and then print the message "Hello there! This is a cleared console." to the empty console.

Alternative.

A simpler alternative is possible. All you need is to send some special characters to the console using print() function and the console magicly clear itself:

/* clear the screen */
func clear() {
    fmt.Print("\033[H\033[2J")
}

I have used this method in my demo app and it works on Mac. I have not verified this on Windows or Linux. Is possible to use a different sequence of characters. I will update this article if I find out how.


REPL APP

A REPL, or Read-Eval-Print-Loop, is an interactive environment for running code snippets or commands. It allows you to experiment, test and debug your code easily, without having to go through the additional steps of writing and compiling your code in a separate program.

Example

Here's an example of a simple REPL built with the Go programming language. It includes a menu and 3 different functions for performing some calculations on numbers.

package main

import (
    "fmt"
    "strconv"
)

func main() {
    for {
        fmt.Println("==== Menu ====")
        fmt.Println("1. Add")
        fmt.Println("2. Subtract")
        fmt.Println("3. Multiply")
        fmt.Println("4. Exit")

        var input string
        fmt.Scanln(&input)

        choice, err := strconv.Atoi(input)
        if err != nil {
            fmt.Println("Invalid input, please try again.")
            continue
        }

        if choice == 4 {
            fmt.Println("Exiting program!")
            break
        }

        var num1, num2 float64
        fmt.Println("Enter first number:")
        fmt.Scanln(&input)
        num1, err = strconv.ParseFloat(input, 64)
        if err != nil {
            fmt.Println("Invalid input for first number, please try again.")
            continue
        }

        fmt.Println("Enter second number:")
        fmt.Scanln(&input)
        num2, err = strconv.ParseFloat(input, 64)
        if err != nil {
            fmt.Println("Invalid input for second number, please try again.")
            continue
        }

        switch choice {
        case 1:
            sum := add(num1, num2)
            fmt.Printf("%v + %v = %v\n", num1, num2, sum)
        case 2:
            diff := subtract(num1, num2)
            fmt.Printf("%v - %v = %v\n", num1, num2, diff)
        case 3:
            product := multiply(num1, num2)
            fmt.Printf("%v x %v = %v\n", num1, num2, product)
        default:
            fmt.Println("Invalid choice, please select a valid option.")
        }
    }
}

func add(a, b float64) float64 {
    return a + b
}

func subtract(a, b float64) float64 {
    return a - b
}

func multiply(a, b float64) float64 {
    return a * b
}

This example prompt the user with a menu of options to choose from, by scanning the input entered by the user, it either perform one of the 3 selected arithmetic functions or exists the program if they choose option 4. The example also demonstrates some error handling, validating that user input is valid, and restricitng the choice of the user between 1-4.


Tutorial

I have created for Sage-Code tutorial a REPL using Go language. This app is more complex, it has 4 submenu pages with numerous examples you can investigate to further learn about Go language.

Execute online on: replit.com


Conclusion:

I use CLI and REPL for teaching programming. Many of Sage-Code tutorials include such an APP. You can create your own little interpreter, using short commands instead of the menu. This is the beginning of a DSL interpreter. I guess all interpreters are actually simple REPL applications while compilers are CLI applications.
Disclaim. This article was written with AI. If you find errors, please report below. I hope you enjoy learning Go language. I'm curious if my article was helpful.


Follow up:

Visit my website and join my classroom on Discord for 101 mentoring. Invitation is at the bottom on my website. We teach Go advanced level only on Discord. This is our last article about Go.

https://sagecode.net


Thanks for reading about Go. Learn and prosper ๐Ÿ€๐Ÿ––๐Ÿผ

ย