Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
В статье мы поговорим о разных частях терминала и о том, как они взаимодействуют, напишем небольшую прогу для чтения ввода в командной строке и рассмотрим, как она интерпретирует команды. Затем мы обсудим, как создать пользовательский интерфейс в терминальном приложении. И в конце концов увидим, как можно использовать все это ради шутки.
Примеры кода я привел на языке Rust, в то же время я буду стараться делать их как можно проще и короче, чтобы даже незнакомому с Rust программисту было все понятно. Кроме того, я подробно опишу значимые части кода. Мы рассмотрим работу эмулятора терминала в Linux, но с таким же успехом ты можешь применить сведения в любой другой Unix-подобной системе (например, в macOS).
Материал рассчитан на новичков в разработке терминальных приложений, тем не менее я старался сделать его полезным и для ветеранов разработки приложений командной строки.
Чтобы установить Rust в Linux или macOS, надо выполнить в командной строке следующую команду:
curl --proto '=https' --tlsv1.2 -sSfhttps://sh.rustup.rs | sh
В macOS ее выполнение проходит без сучка, без задоринки. Однако в Linux могут возникнуть разные проблемы, например связанные с отсутствием curl
. Если ты не хочешь тратить время на их решение, можешь установить Rust из официального репозитория системы. С 20-й версии Ubuntu среди ее пакетов есть Rust. В этом случае в командной строке надо набрать следующее:
sudo apt install rustc
Вместе с компилятором языка Rust устанавливается система сборки Cargo. Ее используют, чтобы управлять проектами, а кроме этого, она играет роль пакетного менеджера. С ее помощью создаются, объединяются, строятся проекты и скачиваются зависимые библиотеки.
Чтобы создать проект, набери в консоли cargo
.
В результате будет создан проект с таким содержимым.
Исходный код программы HelloRust содержится в файле main.
, который, в свою очередь, находится в папке src
.
Файл Cargo.
содержит метаданные описания проекта:
[package]
name = "HelloRust"
version = "0.1.0"
edition = "2022"
[dependencies]
Кроме того, в нем указываются библиотеки, используемые для компиляции программы (ниже ключевого слова [
). Все рассматриваемые в статье примеры обращаются к либе nix
.
Чтобы скомпилировать пример, надо выполнить команду cargo
, находясь в папке с проектом. А чтобы запустить проект на выполнение, нужно набрать команду cargo
. Она не только запустит программу на выполнение, но и пересоберет ее, если в исходный код будут внесены изменения.
В любой удобной для тебя IDE, поддерживающей подсветку синтаксиса. Это в идеальном случае. Плагины поддержки языка существуют для многих сред, например VS Code. Но можно воспользоваться абсолютно любым текстовым редактором. Это уже на твое усмотрение.
Если рассматриваемая тема тебе интересна, напиши об этом в комментарии. Мы подумаем над новыми статьями, посвященными этому языку.
Время от времени эмулятор терминала называют командной строкой (оболочкой) или pty (псевдотерминалом). Для начала обсудим смысловую нагрузку этих названий и решим, в каких контекстах они могут использоваться.
Эмулятор терминала имеет длинную историю, но мы сконцентрируемся на тех вещах, которые подразумеваются под этим словом в наши дни. Как эмулятор терминала (gnome terminal, alacritty, rxvt-unicode), так и командная оболочка (bash, zsh, fish) представляют собой приложения, выполняемые под управлением операционной системы.
Эмулятор терминала — это графическое приложение, которое преобразует поступающие из командной оболочки данные и отображает их на экране. Обычно вывод текстовый, но могут быть исключения. Их мы рассмотрим дальше.
Командная оболочка (у нас распространено короткое название shell) предоставляет интерфейс к операционной системе, позволяет пользователю взаимодействовать с ее файловой системой, запускать процессы, а иногда — выполнять скрипты.
Эти две программы соединяются с помощью pty (pseudoterminal), который организует двунаправленный асинхронный канал связи между ними. В одном направлении pty передает данные от терминала к оболочке (STDIN), во втором — от оболочки к терминалу (STDOUT). Другими словами, когда пользователь вводит команды в терминал, последний пересылает их через канал STDIN псевдотерминала (pty) в оболочку. С другой стороны, когда оболочка готова показать пользователю результат на экране, она отправляет данные в терминал по каналу STDOUT псевдотерминала.
Как видно, у pty имеются две стороны. Мы будем называть их «первичная» и «вторичная», хотя в официальной документации они называются master и slave. На первичной стороне находится эмулятор терминала, на вторичной — командная оболочка. Тем не менее на вторичной стороне может располагаться любая программа, ожидающая подключения терминала, например Vim или Top.
Предлагаю взглянуть на картинку, демонстрирующую, как эта система работает в целом, на примере команды ls
.
Когда командная оболочка отправляет текст терминалу, она использует весь доступный ей набор инструкций. Ты мыслишь в правильном направлении, если назвал этот набор escape-символами ANSI. Они используются, когда командной оболочке надо каким‑либо образом форматировать вывод: изменить цвет/стиль шрифта или переместить курсор ввода. Чтобы увидеть, как это работает, давай напишем небольшую программку на Rust, которая создает псевдотерминал (pty) и на вторичной стороне запускает предопределенную в конкретной системе командную оболочку. Таким образом мы сможем увидеть, какие данные из недр системы нам шлет shell.
unimplemented!()}fn spawn_pty_with_shell(default_shell: String) -> RawFd {
unimplemented!()}fn main() {
let default_shell = std::env::var("SHELL").expect("could not find default shell from $SHELL");
let stdout_fd = spawn_pty_with_shell(default_shell);
let mut read_buffer = vec![];
loop {match read_from_fd(stdout_fd) {Some(mut read_bytes) => {read_buffer.append(&mut read_bytes);}None => {println!("{:?}", String::from_utf8(read_buffer).unwrap());std::process::exit(0);}}
}}
Исходные коды примеров в статье можно найти в репозитории автора.
Функция main
начинается с того, что мы получаем путь к системной командной оболочке из переменной окружения SHELL
. В случае ошибки функция expect
выводит сообщение. Далее вызывается функция spawn_pty_with_shell
, которой в качестве параметра передается полученный на прошлом шаге путь к системной оболочке. Внутри функции оболочка запускается в новом процессе на вторичной стороне pty. Но этот момент рассмотрим ниже.
|
|