Shell – консольный Just for fun

    Значительная часть операционной системы КолибриОС (ядро и большинство драйверов и программ) написаны на языке ассемблера. Тем не менее, программы на языках высокого уровня также есть. Сегодня пойдёт речь об одной из них.

    До создания программы командной строки Shell у меня уже был опыт написания программ для КолибриОС. В основном, это были игры – Piton, Donkey, порт эмулятора ZX Spectrum, названный мною e80, порты консольных пятнашек и порт виртуального собеседника Eliza. Я развлекался, как мог.



    Кроме игрушек были попытки написать что-то более полезное. Так, например, утилита cObj (“see .obj”) позволяла просмотреть список экспортируемых динамической библиотекой, т.е. COFF файлом с глобальной переменной EXPORTS или _EXPORTS, функций. По сути, это максимально урезанный Dependency Walker. Программа cObj была написана на ассемблере, а размер исполняемого файла, сжатого с помощью компрессора kpack, составляет 256 байт. Поэтому, как мне и сейчас кажется, данная программа отлично вписывается в концепцию КолибриОС.



    Почему я посвятил целый абзац совсем не той программе, о которой будет идти речь далее? Причины две. Первая: cObj – оказался замечательным инструментом для исследования библиотеки для работы с консолью (console.obj). Замечательным дополнительным инструментом, так как библиотека хорошо документирована, как и многие вещи в КолибриОС. Вторая причина состоит в том, что я ленивый человек, и программирование на ассемблере для меня – такое же развлечение, как времяпрепровождение за компьютерными играми, а не достижение определённого результата. Низкоуровневая оптимизация кода, связанного с проектами по основному месту работы, – не в счёт. Все перечисленные в статье программы, кроме cObj, разумеется, и порта Элизы, написаны на великом и могучем Си.

    Игрушки игрушками (блин, и сейчас их пишу!), но захотелось мне написать что-нибудь полезное не только для разработчиков программ. И так как мне не доставало продвинутой командной строки, решил реализовать её.

    Может возникнуть вопрос: «Разве в КолибриОС не было командной строки?!». Да, командная строка существовала. Написанная на языке ассемблера программа называлась CMD, и, в принципе, если говорить объективно, была достаточно функциональной.



    Однако субъективно я видел в ней много недочётов (как то мелкий шрифт, например, или невозможность прокрутки окна). Поэтому цель была поставлена – «я напишу свою командную строку, со скриптами и консольными приложениями».



    Июль 2008 года. Открыл на форуме КолибриОС тему «Очень функциональный шелл». Первое сообщение начиналось так: «Решил написать функциональный шелл на Си с использованием console.obj.» Заканчивалось сообщение словами «Пока сделал функцию отделения команды и параметров, а также реализовал команды help (пока без параметров), ver и exit.»

    Прикреплённый к первому сообщению файл shell.zip (сейчас скачан более 180 раз) содержал бинарник данной поделки размером 1,3 килобайта.

    Уже на следующий день выложил версию с добавленными командами exit, ls, pwd, ps, kill, help (последняя команда – с параметрами и без них).

    Поначалу обновления были достаточно частыми, потом появлялись всё реже. Нельзя сказать, что я потерял интерес к своей разработке. Просто не всегда оставалось времени на программирование «just for fun».

    История версий программы Shell начинается с 0.01. Да, именно 0.01. В версии 0.1 появилась возможность запускать программы (кто-бы мог подумать, что такая возможность не будет реализована сразу же?). Начиная с версии 0.3, Shell мог запускать скрипты собственного формата. Также в этой версии появилась история команд, работающая, в том числе, и с псевдонимами (aliases).

    Начиная с версии 0.4.2, к разработке проекта присоединились и другие участники сообщества КолибриОС. Так, diamond (автор многих программ для Колибри и разработчик библиотеки console.obj) реализовал корректное завершение программы при закрытии окна и оптимизировал функции по скорости, Pterox добавил несколько полезных команд, Leency сделал поддержку относительных путей в скриптах. Кроме того, не удивительно, что не обошлось без помощи CleverMouse.

    Вскоре, с выходом КолибриОС 0.7.5.0 в конце января 2009 года, программа Shell заменила старую командную строку CMD.
    Каковы же возможности этого «почти очень функционального шелла»?

    На сегодняшний день актуальная версия программы 0.7.4. Да, до 1.0 далеко, а значит, ожидать многого не стоит. Тем не менее, реализовано 25 команд (about, alias, cd, clear, cp, date, echo, exit, free, help, history, kill, ls, mkdir, more, ps, pwd, reboot, rm, rmdir, shutdown, sleep, touch, uptime, ver), есть поддержка скриптов и консольных приложений, история команд, система псевдонимов команд, обработка управляющих символов и ESC-последовательностей, сделаны первые шаги в поддержке общесистемного буфера обмена. Программа может компилироваться с сообщениями либо на русском, либо на английском языках.



    «Под капотом» у Shell всего навсего около 80 килобайт исходного кода на Си, из которых 18 – библиотека, аналогичная libc, а также библиотека с функциями API КолибриОС. Использование собственной libc (кроме неё в КолибриОС существует ещё как минимум две реализации) позволило облегчить сборку программы и немного уменьшить размер выходного файла. К слову, для сборки Shell необходим компилятор MinGW, хотя версия Shell от diamond’а была предназначена для MSVC. Исходники программы я старался сделать как можно более прозрачными, т.е. понятными даже начинающим программистам (Вот именно поэтому в них так мало комментариев! Код ведь и так понятен! Шучу). Исходный код Shell разбит на модули, а каждая из внутренних команд вынесена в отдельные файлы, что теоретически должно упростить поиск нужных участков.

    От возможностей программы перейдём к принципам работы. Если с интерактивным режимом, думаю, всё понятно (вводим команду, получаем ответ, и так до тех пор, пока не надоест), то в написании скриптов и консольных приложений для Shell есть нюансы.

    Скрипты могут содержать любые команды, которые работают в интерактивном режиме. Более того, при запуске Shell ищет в каталоге настроек (обычно /sys/settings/), а затем в каталоге, откуда он запущен, файл “.shell” (без кавычек) и если находит, выполняет его. Обычно в нём записана команда about и предложение набрать help для помощи. Но, естественно, можно написать и свою последовательность команд.



    В скриптах возможно использовать комментарии, начинающиеся с решётки (#).

    Первая строка скрипта (см. скриншот выше) обязательно должна быть “#SHS”. Она означает “SHellScript”, и именно по ней Shell определяет, можно ли выполнять скрипт.

    Консольные приложения для Shell – обычные приложения КолибриОС, для которых даже не нужно инициализировать окно консоли. Правда, разработчику такого приложения придётся позаботиться о правильной инициализации интерфейса с Shell.

    Обмен информацией между Shell и консольными приложениями осуществляется через именованную область. Консольное приложение после своего запуска в первую очередь должно создать именованную область с именем pid-SHELL, где pid — идентификатор процесса без заглавных нулей, например: 6, 42 или 204. Первый байт области — команда (т.е. максимум 255 команд, что вполне достаточно), далее идут данные (впрочем, они могут и отсутствовать).

    Список реализованных команд:

    • SC_OK 0 ничего не делать
    • SC_EXIT 1 выход
    • SC_PUTC 2 вывести на экран символ
    • SC_PUTS 3 вывести на экран строку
    • SC_GETC 4 считать с клавиатуры символ
    • SC_GETS 5 считать с клавиатуры строку
    • SC_CLS 6 очистить экран

    Консольное приложение может быть написано на любом языке программирования, компилятор которого может производить код для КолибриОС. На SVN-сервере КолибриОС в каталоге Shell можно посмотреть два примера консольных приложений – на Си и на языке ассемблера (причём примечательно, что последний пример появился гораздо позже первого).

    Ещё несколько особенностей, которые должен знать разработчик:
    • Консольная программа для Shell должна сама заботиться о рациональном использовании процессорного времени.
    • Консольная программа для Shell должна сама заботиться о закрытии именованной области.

    Т.е. в принципе, несколько простых правил, а остальное – очевидно, особенно после разбора исходника примера (2 килобайта на Си и 3 килобайта на ассемблере). Разве является сложной, например, функция main из примера?

    void kol_main()
    {
    
    char string[256];
    
    sc_init();
    
    sc_cls();
    sc_puts("This is a test console application for Shell\n\r");
    sc_puts("Type a string (255 symbols max): ");
    sc_gets(string);
    sc_puts("You typed:\n\r");
    sc_puts(string);
    sc_puts("Press any key: ");
    string[0] = sc_getc();
    sc_puts("\n\rYou pressed: ");
    sc_putc(string[0]);
    
    sc_exit();
    
    }
    


    Несмотря на всю простоту интерфейса, в КолибриОС пока нет ни одной консольной программы для Shell! Скрипты, правда, тоже не особо распространены, но всё же иногда используются.

    Вообще, о программе Shell можно говорить достаточно долго. Особенно о её дальнейшем развитии. Ведь то, что есть на сегодняшний день многих не устраивает. Пользователи требуют реализации новых команд, не уточняя, правда, каких. Да, Shell очень далёк от BusyBox. Но если учесть, что Shell предназначен для не-UNIX-подобной системы, а стандартная libc не используется, то должно быть понятно, что реализация каждой новой функции – своего рода эксперимент. И таких экспериментов может быть ещё очень много. Поэтому надеюсь, к версии 1.0 Shell можно будет смело назвать «очень функциональным шеллом».
    KolibriOS Project Team
    0,00
    Быстрая операционная система для бизнеса и хобби
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 11

      +6
      Почему не shebang? (#!)? Казалось бы мелочь, а можно обеспечить фрагментик совместимости с юниксами.
        +7
        Чтобы не обнадёживать пользователей. Потому что на этом совместимость и закончилась бы. Нет многих команд, перенаправления ввода/вывода (пока что), пайпов и много другого. Также различна структура каталогов операционных систем. Тем не менее, если пользователь уверен в своих силах, никто не может запретить ему скорректировать скрипт для запуска в Шелле.
          +3
          _сейчас_ не поддерживает. А потом может и начать поддерживать. А сигнатура останется.

          Такие вещи лучше продумывать в начале и менять, пока проект маленький.
            +3
            Да, согласен. Возможно, стоит задуматься над вопросом совместимости. Но это больше философский вопрос. С точки зрения программирования реализация не такая уж и сложная.
        0
        Видимо вскоре КолибриОС перестанет быть колибри, а перерастет в воробушка или голубя.
        Это к тому, что любой «быстрый» дистрибутив (не важно какой) быстрый до тех пор, пока не обзаведется всем функционалом десктопа.
          +5
          Не будет такого. За 10 лет система не стала тормозней и намного больше. Со скоростью так вообще стала только быстрей.
          +3
          typedef struct
          {
          unsigned        p00;
          unsigned        p04;
          unsigned        p08;
          unsigned        p12;
          unsigned        p16;
          char            p20;
          char            *p21;
          } kol_struct70;
          

          Ничего не понял :)
            +3
            Чего непонятного то? Структура :)
              +3
              Да, фантазии не хватило дать полям структуры имена. p — pointer to XX offset. Возьму на заметку — надо исправить.
              А вообще, это структура для работы с файловой системой:

              Общий формат информационной структуры:
              +0: dword: номер подфункции
              +4: dword: смещение в файле
              +8: dword: старший dword смещения (должен быть 0) или поле флагов
              +12 = +0xC: dword: размер
              +16 = +0x10: dword: указатель на данные
              +20 = +0x14: n db: ASCIIZ-строка с именем файла или
              +20 = +0x14: db 0
              +21 = +0x15: dd указатель на ASCIIZ-строку с именем файла
                +4
                Так стало понятнее, спасибо :)
                Мне вообще кажется, что правильнее было бы для каждой подфункции сделать отдельную си-функцию, которая будет верно заполнять struct70 (так как в большой части подфункций там всегда требуются нули) и дергать ее.
                  +5
                  Логично. Спасибо за подсказку!

                  Вообще, комментарии к статье оказались очень полезными — они указывают на такие вещи, на которые я обращал недостаточно внимания.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое