Маленький shell, который запускает .bat-файлы на Linux и macOS. Не очень полезный и совсем не серьёзный проект — сделал просто для удовольствия.
Что это
cmd — это интерпретатор командных файлов Windows (.bat / .cmd), написанный на Go и работающий на Unix и Linux. Он умеет запускать .bat-скрипты нативно или работать как интерактивная оболочка с синтаксисом BAT:
1 | @ECHO OFF |
1 | $ cmd hello.bat |
Сразу честно: это не замена cmd.exe и вообще штука сомнительной полезности. Никто в здравом уме не пишет на batch под Linux — для этого есть bash, и он лучше во всём. Я сделал это, потому что мне нравится возиться с языками и интерпретаторами, а синтаксис BAT — это маленький музей странных решений, который интересно воспроизводить.
Как это устроено
Внутри — обычный конвейер интерпретатора, по стадиям на строку:
- Раскрытие переменных —
%VAR%подставляются до разбора строки, ровно как в настоящем cmd.exe. - Лексер — режет строку на токены (слова, операторы
|,&&,>, переменные). - Парсер — рекурсивный спуск собирает токены в AST (
IF,FOR,CALL, пайпы, блоки( ... )). - Исполнитель — выполняет AST: счётчик команд для
GOTO, вызовы подпрограмм, циклы, перенаправления, пайпы.
Самое забавное в BAT — двухфазное раскрытие переменных. %VAR% подставляется на этапе разбора, а !VAR! (отложенное раскрытие) — уже в момент выполнения. Из-за этого %RESULT% и !RESULT! внутри одного цикла дают разные значения. Пришлось воспроизводить и это, и кучу других причуд: вывод с \r\n, поведение FOR /F "tokens=...", динамические %DATE%, %TIME%, %RANDOM%, обработку ^ как символа продолжения строки и даже несколько откровенных багов cmd.exe, на которые молча опираются реальные скрипты.
Чем проверял
Самая приятная часть. Чтобы понять, насколько интерпретатор близок к настоящему cmd.exe, мне нужен был сложный, реальный набор .bat-файлов. И он нашёлся: gw-batsic — интерпретатор GW-BASIC, написанный на самом batch. Это тысячи строк лютого BAT с эмуляцией чисел с плавающей точкой, таблицами LR-парсера, загружаемыми через имена переменных, и стек-машиной.
Если такое запускается одинаково на Windows и на моём порту — значит, совместимость серьёзная. Сейчас весь тестовый набор gw-batsic (больше тысячи проверок) проходит на cmd без единого расхождения. По дороге это вскрыло десятки тонких несовместимостей, которые я и чинил одну за другой.
Технологии и библиотеки
Тут всё скромно. Это чистый Go без экзотики:
- Сам язык — почти всё на стандартной библиотеке:
os/execдля внешних команд,path/filepathдля путей и шаблонов,regexpдляFINDSTR,timeдля%DATE%/%TIME%,math/randдля%RANDOM%. - Единственная внешняя зависимость — chzyer/readline для интерактивного режима: редактирование строки, история, автодополнение по Tab.
Никакого генератора парсеров — лексер и парсер написаны руками, так проще ловить причуды синтаксиса. Подробное описание реализации я сложил в docs/, если кому интересно покопаться.
Итог
Бесполезно? Скорее да. Но было увлекательно — особенно момент, когда GW-BASIC-интерпретатор, написанный на batch, впервые целиком завёлся на Linux и напечатал то же, что на Windows. Ради таких моментов всё и затевалось.