LINUX.ORG.RU

Lua Shell

 , , ,


2

6

Контест этого топика: Леннарт теперь до эмуляторов терминала добрался (комментарий)

@EXL:

Лучше бы Lennart взялся за Bash.

@wandrien:

Там только выкинуть целиком. Я вот хочу попытаться для lua сделать обвязку для скриптинга уровня оболочки. Подобные либы на Lua есть, но качество и объем фич мне не нравится. Надо лучше. Тебе бы был интересен такой проект?


Итак, вот моя идея в общих чертах. Составные части, на которых основываться:

https://github.com/BanceDev/lush
Низкое качество сборочного скрипта. Вероятно, и кода тоже. Интересует идея в первую очередь.

https://github.com/mna/luashell
Ключевое, что нам нужно. Взять за основу. Но:

  • Нужны полнофункциональные средства перенаправления ввода-вывода, заменить эту часть API. Под капотом, вероятно. придётся делать полноценную обработку fork - настройка процесса - exec.
  • test() должен быть вменяемый, а не парсить строку по пробелам. Просто алиас для sh.cmd("test", ...).exec()
  • Форк процесса без exec в качестве элемента пайплайна на уровне API
  • Как расширение предыдущего - обёртка а ля sh.echo("text").

В качестве базового API взять https://25thandclement.com/~william/projects/lunix.html вместо https://github.com/luaposix/luaposix

Также рассмотреть для включения и/или как источник идей:


Общая идея:

  • Lua + lunix — получаем возможность писать на Луа «приложения как на Си под libc».
  • Сверху на это - форкнутый и допиленный luashell. Это ключевое.
  • Далее QoL вещи: lua-path, argparse, функции для парсинга и форматирвоания времени, функции для JSON.
  • Далее - разработать интерактивный режим для использования в качестве командной оболочки.

Продукт компилируется в статический бинарь с musl и/или cosmopolitan libc и получаем «вечный» shell. При этом весьма компактный.

★★★

Последнее исправление: wandrien (всего исправлений: 4)

Низкое качество сборочного скрипта.

Слепой я сначала прочитал «Новое качество сборочного скрипта». Порадовался за грамотный пиар и маркетинг :)

hobbit ★★★★★
()
Ответ на: комментарий от wandrien

А почему не сделать что-то вроде:

-- вводим два контекста - команды и оболочки, \
-- первые копят цепочку, второй исполняет, 
-- метатабличками перегружаем `_G`, и контексты до 
-- получения следующего синтаксиса:

ls{ "-la" } | grep { "lua" } | wc { "-l"} | shell {}

cat { in = "/etc/passwd" } | sort { out = "sorted.txt"} | shell {}

Вроде же симпатичнее и больше на обычный bash похоже(чтобы проще было примеры переписывать). То что вы показали больше как по мне похоже на эдакое расширение io.pipe()/os.execute(), который тогда уж проще расширить аналогом луашных регулярок, чем приведённый функциональный стиль использовать с кучей избыточных кавычек. Если же тюнить ваш пример, то как минимум лучше вытянуть stdin/stdout как нарушителей контейнера и вместо аргументов функций использовать таблицы, в которых и переопределять stdin/stdout

AKonia ★★★
()
Последнее исправление: AKonia (всего исправлений: 3)

Подброшу для оживления темы. У использования настоящих языков в качестве шелла есть свои плюсы и минусы. Например, выше @legolegs спрашивал, как записать банальную конструкцию

<file.gz gunzip | tee >(sha1sum > file.sha1) | xz > file.xz

И честный ответ — никак. Подстановку процессов в бабашку не завезли, потому как особый синтаксис будет всё усложнять, а т.к. задача не самая актуальная, то и ну его нафиг.

Обидно. Но не всё потеряно. Можно прибегнуть к жульничеству!

(with-open [in-sha1sum (-> (process {:out "file.sha1"} "sha1sum")
                           :in io/writer)]
  (run! check
        (pipeline
         (pb {:in (io/file "file.gz")}
             "gunzip")
         (pb {:err in-sha1sum} "tee")
         (pb {:out "file.xz"}
             "xz"))))

Задача решена. Однако, можно зайти и с другой стороны. Если у нас есть нормальный язык, то мы можем добавить в него подстановку процессов самостоятельно.

(defn get-tmp-name
  "returns a name for a temporary file (but does not create it)"
  []
  (-> (p/sh "mktemp -u")
      :out
      str/trim-newline))

(defn mkfifo
  "creates a named pipe and puts it into an accumulator"
  [acc named-pipe]
  (let [file-name (get-tmp-name)]
    (p/sh "mkfifo" file-name)
    (concat acc
            (list named-pipe file-name))))

(defmacro with-named-pipes
  "Evaluates body with named-pipes bound to a standard
   unix fifo named pipes. Then cleans up the pipes.

   Example:
   
   (with-named-pipes [in out]
        (doall (map fs/readable? [in out])))
   
   "
  [named-pipes & body]
  `(let [~@(reduce mkfifo '() named-pipes)]
     (let [result ~@body]
       (doseq [pipe ~named-pipes]
         (fs/delete pipe))
       result)))

И тогда можно сделать морду кирпичём, провозгласить: „Явное лучше неявного!“ и записать решение в виде

(with-named-pipes [in1]
  (let [source-file (io/file "/tmp/secret.gz")
        dest-file (io/file "/tmp/secret.xz")
        p (p/pipeline (p/pb {:in source-file} "gunzip")
                      (p/pb "tee" in1)
                      (p/pb {:out  dest-file} "xz"))]
    (p/process {:in (io/file in1) :out "/tmp/plain.log"}
               "sha1sum")
    (run! p/check p)))

Девять строчек вместо одной (bash победил), зато никакой магии, полный контроль за ситуацией и возможность расширить язык в любую нужную сторону (bash, рыдая, удаляется в сторону кладбища. Все машут платочками в след (и радуются)).

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

нормальный язык

Мухахаха, продолжайте! Кстати, CL выглядит намного более нормальным, чем это порево.

bread
()
Ответ на: комментарий от dataman

У культурных людей всегда есть носовой платок в кармане пиджака. Так-то!

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

Как так вышло, что на «нормальном языке» всё еще нечитаемое месиво в сравнении с примером на Lua.

wandrien ★★★
() автор топика
Ответ на: комментарий от wandrien

Полагаю, что причины тут две: во-первых, текст на знакомом языке вам кажется более читаемым, нежели на незнакомом, а, во-вторых, я что-то не заметил реализованной на луа подстановки процессов. А реальный код всегда проигрывает воображаемому (зато работает).

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

Ну трындец же:

(with-named-pipes [in1]
  (let [source-file (io/file "/tmp/secret.gz")
        dest-file (io/file "/tmp/secret.xz")
        p (p/pipeline (p/pb {:in source-file} "gunzip")
                      (p/pb "tee" in1)
                      (p/pb {:out  dest-file} "xz"))]
    (p/process {:in (io/file in1) :out "/tmp/plain.log"}
               "sha1sum")
    (run! p/check p)))

Есть же возможность метапрограммирования в Лиспе. Или точнее сказать, в метапрограммировании суть Лиспа.

wandrien ★★★
() автор топика
Ответ на: комментарий от wandrien

Ну трындец же:

Это не критика, а мычание. Суть человека заключается в членораздельной речи, а вы ей так манкируете.

Есть же возможность метапрограммирования в Лиспе.

Здесь критерием оптимизации выступает количество wtf/loc. Можно записать всё то же самое гораздо короче, но тогда потеряется прямая связь с java process builder’ом. Концептуальная простота в данном случае важнее количества закорючек. Но это мой выбор, желающие могут всё переделать по другому. Благо, настоящий язык позволяет сделать почти всё (но не всё, нормальных reader macros’ов в кложу не завезли и это тоже разумное решение).

ugoday ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.