Как сделать простую интерактивную оболочку в Golang

Итак, сегодня я узнаю кое-что новое и немного базового о Голанге. Это была простая интерактивная оболочка. Это было очень простое приложение, но выглядит круто.

Я работаю на Голанге более 1 года. Так много инструментов уже создано людьми, например: cobra от spf13 или многие другие, которые помогают нам создавать приложения из командной строки. Но почему-то мне любопытно, как сделать простой, не имея зависимостей с другими внешними библиотеками.

Так что просто из любопытства я начинаю искать, как это сделать в гугле. Но то, что я нашел, - это только учебник с использованием других внешних зависимостей. Каждый продвигает свою собственную библиотеку только для облегчения создания интерактивной оболочки 😌. Ребята? Мне просто нужно сделать простой пакет с чистым пакетом Golang (без внешних зависимостей) 😌

Но я нашел статью о создании интерактивной оболочки, например эту статью: http://technosophos.com/2014/07/11/start-an-interactive-shell-from-within-go.html, но, то, что он делает, не нравится мне. Итак, позже я приведу только множество примеров, которые я нашел на таких сайтах, как Gobyexample.com и т. Д.

Я сам сделал простое приложение Shell. Здесь я объясню, как я это сделал, ниже.

Оболочка

Поэтому, прежде чем создавать простое приложение Shell, я хочу убедиться, что то, что я имею в виду, совпадает с тем, что вы имеете в виду.

Для меня Shell - это приложение, которое будет действовать как самый простой пользовательский интерфейс (текстовый интерфейс). Некоторые люди могут сказать, что это интерфейс командной строки (CLI).

Оформление заявки

Чтобы сделать приложение, для простейшего прототипа я сделаю его вот так.

$ ls
go.mod  main.go

Прочтите команду из терминала

Чтобы сделать это, первое, что нужно сделать, это прочитать ввод. Мне нужно прочитать ввод с терминала. Для этого я делаю это так.

reader := bufio.NewReader(os.Stdin)
cmdString, err := reader.ReadString('\n')
if err != nil {
 fmt.Fprintln(os.Stderr, err)
}

Выполнить команду

Затем выполните команду. После прочтения командной строки с терминала выполните команду.

cmdString = strings.TrimSuffix(cmdString, "\n")
cmd := exec.Command(commandString)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
cmd.Run()

До сих пор я могу запускать свое простое приложение оболочки, но оно будет запускаться только для одной команды. После выполнения команды она остановится.

Добавление бесконечного цикла

Чтобы он получал каждую команду после однократного выполнения. Нам нужно добавить его в бесконечный цикл.

for {
 fmt.Print("$ ")
 cmdString, err := reader.ReadString('\n')
 if err != nil {
  fmt.Fprintln(os.Stderr, err)
 }
cmdString = strings.TrimSuffix(cmdString, "\n")
cmd := exec.Command(cmdString)
 cmd.Stderr = os.Stderr
 cmd.Stdout = os.Stdout
 err = cmd.Run()
 if err != nil {
  fmt.Fprintln(os.Stderr, err)
 }
}

Если мы запустим приложение, оно будет выглядеть так, как показано ниже

Работа с аргументами

До этой строки я закончил свое первое приложение CLI. Но он по-прежнему не принимает аргументы. Если я передам аргумент, он выдаст ошибку.

$ ls -lah // this command wil throw error

Чтобы прочитать аргументы, я делаю это так, как показано ниже. Сначала я разбил команду на массив строк.

// ...
cmdString = strings.TrimSuffix(cmdString, "\n")
arrCommandStr := strings.Fields(cmdString)
cmd := exec.Command(arrCommandStr[0], arrCommandStr[1:]...)
// ...

Для разделения текста я использую функцию Fields из пакета string. Эта функция аналогична функции разделения строки. Если string.Split разделит строку заданным определенным разделителем. Но strings.Fields будет разделять слова пробелами.

Пример:

str := "Hello World    Beautiful World"
arrString := strings.Fields(str)
fmt.Println(arrString)
// [Hello World Beautiful World]

Теперь моя простая оболочка уже принимает и обрабатывает заданные параметры. Теперь эта команда должна работать.

$ ls -lah
total 4280
drwxr-xr-x  5 iman  staff   160B Nov  6 19:48 .
drwxr-xr-x  6 iman  staff   192B Nov  6 11:41 ..
-rw-r--r--  1 iman  staff    38B Nov  6 11:43 go.mod
-rw-r--r--  1 iman  staff   606B Nov  6 20:11 main.go
-rwxr-xr-x  1 iman  staff   2.1M Nov  6 19:49 simshel

Добавление команды выхода

Но это приложение работает только для любого встроенного приложения, зарегистрированного в среде. Команды типа exit не существует, потому что она запрограммирована в каждом приложении CLI.

Затем я создаю обработчик case-case для каждой команды, не имеющей встроенного приложения в системе, такого как theexit command.

Добавление настраиваемой команды

Чтобы добавить собственные команды, я мог бы просто добавить их в обработчик switch-case. Например, я хочу добавить такую ​​команду, как plus. Эта команда не существует ни в одном приложении CLI. Я хочу сделать так:

$ plus 2 4 5 6
17

Для этого мне просто нужно добавить функцию суммирования и добавить обработчик case case в switch-case

Заключение

В конце концов, это очень простое приложение. Но наверняка я узнаю кое-что, когда сделаю это. А еще, когда писал это, я нашел аналогичную статью с этой статьей (здесь: https://sj14.gitlab.io/post/2018-07-01-go-unix-shell/). Прочитав эту статью, я подумываю отказаться от своего черновика. Но я написал здесь много объяснений и примеров, поэтому решил все равно опубликовать XD.

Во всяком случае, я тоже поместил исходный код в свой Github здесь: https://github.com/bxcodec/simpleshell, если появится хоть какой-то шанс, я постараюсь добавить в него некоторые функции позже.

* Индонезийская версия: https://medium.com/easyread/today-i-learned-belajar-membuat-aplikasi-interactive-shell-sederhana-di-golang-2ef013003393
* Обновление: добавьте еще один пример настраиваемая команда