Утилитки и программные пакеты. что удобнее?
Итак, все приложения можно условно поделить на два типа: утилитки и программные пакеты (в оригинале, Command Suite).
Первый тип — это приложения, которые имеют одну цель, один режим работы. Примеров этому типу программ бесчисленно, почти всех Unix-команды такие: ls, grep, diff,… (рубисты могут вспомнить, например, команду rspec) Удобство этих программ в том, что их возможности проще запомнить и труднее в них запутаться.
Кроме того, их проще склеивать в цепочки для последовательной обработки. Тут будет уместной следующая аналогия. Представьте, что вы строите дом, притом дом не типового образца. Гораздо удобнее строить его из кирпичей, а не из монолитных блоков, ведь кое-где вам эти блоки пришлось бы подпиливать, а где-то пришлось бы заделывать стыки камнями. Да и блоки можно только подъемным краном тягать, тогда как кирпичи можно класть руками.
Второй тип программ можно сравнить с швейцарским ножом или кухонным комбайном. Иногда они крайне удобны. Посмотрите на git (в мире руби сразу вспоминаются gem, rails, bundle) — одна программа, а сколько всего умеет. И коммитить/чекаутиться может, и ищет в истории сама, и изменения между файлами считает.
Так что grep, diff и прочее в неё встроено, ничего комбинировать с гитом и не надо, сам всё умеет. Если вернуться к аналогии с домом, то у гита есть типовой проект на каждый случай жизни (и попробуй ещё запомни их все).И всё же, не всем программам стоит быть многофункциональными: всё равно все варианты использования вы не переберете.
В подтверждение этого тезиса предлагаю вам представить себе «мультитул», который умеет делать cd, ls, pwd, diff, df и ещё кучу полезных операций одной командой, только опции надо будет слегка менять (например, filesystem change, filesystem show, filesystem where итд).
Кстати, если вы написали десяток утилит, а потом поняли, что хотите, чтобы это был программный пакет, то это не так уж и сложно поправить. Достаточно написать обертку, которая будет маршрутизировать команды. Вы, возможно не знаете, но git состоит из десятков утилиток типа git-commit, git-show, git-hash-object, git-cat-file, git-update-index итд, которым передает управление команда git, основываясь на типе команды и опциях (разумеется, за одной командой может стоять целая цепочка из вызовов утилит).
Введение
Операционная среда или окружениеenvironment — интерфейс, предоставляемый пользователю или программе операционной системой. В частности, пользовательский интерфейс является частью операционной среды.
Командная строкаcommand line — принцип организации пользовательского интерфейса на основе ввода текстовых команд с клавиатуры и текстового вывода результатов на экран. Интерфейс на основе командной строки — command line interface, CLI.
Оболочка командной строки или просто оболочкаshell — программное обеспечение, отвечающее за поддержку командной строки (обычно это компонент ОС, но может быть и сторонним ПО). Примеры: cmd.exe и Powershell в Windows, sh, csh, bash, ksh и др. в Unix-подобных системах. Оболочка командной строки предоставляет собственное окружение: “переменные среды” environment variables (глобальные и локальные для текущего сеанса) и интерпретатор текстовых команд.
Пакетный файлbatch file или сценарий — содержащий команды оболочки файл, который можно запустить на исполнение как исполняемый файл.
Терминал (от лат. terminus — граница) — устройство или ПО, выступающее посредником между человеком и вычислительной системой. Обычно данный термин используется, когда точка доступа к системе вынесена в отдельное физическое устройство и предоставляет свой пользовательский интерфейс на основе внутреннего интерфейса (например, сетевых протоколов).
Консольconsole — исторически реализация терминала с клавиатурой и текстовым дисплеем. В настоящее время это слово часто используется как синоним сеанса работы или окна оболочки командной строки. В том же смысле иногда применяется и слово “терминал”.
Консольное приложениеconsole application — вид ПО, разработанный с расчётом на работу внутри оболочки командной строки, т.е. опирающийся на текстовый ввод-вывод.
Ранние компьютеры представляли собой громоздкие и дорогие устройства, их рабочее время распределялось между многими пользователями. При этом пользователь обычно не мог работать непосредственно с вычислительным устройством, а использовал терминал, который выполнялся в виде рабочего места с устройствами ввода-вывода, подключенными к компьютеру.
Первые устройства такого рода использовали бумажные носители: перфокарты или перфоленту для ввода (символы задавались группами отверстий, пробитых или не пробитых в нужных позициях) и бумажную ленту для вывода (вывод компьютера печатался на ней устройством, аналогичным телетайпу).
Затем бумагу убрали, но представление диалога пользователя и компьютера в виде последовательности текстовых запросов и сообщений осталось: для ввода использовалась клавиатура, а для отображения и ввода и вывода — монитор. Равно остались и терминалы, с помощью которых множество пользователей могло работать с одним дорогим и высокопроизводительным компьютером (серийно выпускаемые большие компьютеры стали называть “мэйнфреймами”).
Впоследствии миниатюризация вычислительных устройств позволила сделать схему “ЭВМ ↔ сеть ↔ терминалы” необязательной, совместив ЭВМ и терминал в одном устройстве — персональном компьютере. Тем не менее, старый текстовый интерфейс продолжил существование ввиду своей исключительной простоты.
Обычно операционные системы предоставляют специальную программу — оболочку командной строки, работающую в текстовом режиме и принимающую некоторый относительно стандартизованный набор команд, с помощью которых пользователь может управлять работой ОС, запускать другие программы. Эту программу “по старой памяти” и называют “терминалом” или “консолью”.
В рамках стандартов языков C и C определён базовый набор средств, позволяющий работать в режиме текстового интерфейса. Соответственно, например, проект приложения Visual C , использующий эти средства, называется “консольным”.
Общая схема работы консольного приложения показана на рисунке.
Разложим содержание этой схемы по пунктам.
- Работа консольного приложения начинается с первой строчки функции main и заканчивается при выходе из этой функции.
- Во время работы консольному C -приложению доступны три текстовых потока: cin для ввода (по умолчанию подключен к клавиатуре), cout для вывода (по умолчанию выводит текст в окно консоли) и cerr для вывода сообщений об ошибках (по умолчанию тоже выводит текст в окно консоли).
- При запуске операционная система передаёт приложению параметры командной строки (см. ниже) в виде набора строк.
- При завершении работы приложение передаёт ОС целое число — код ошибки. (Стандартизовано два варианта — “успех” EXIT_SUCCESS, традиционно равный нулю, и “неудача” EXIT_FAILURE.)
Функция main пользуется определёнными привилегиями: она может ничего не принимать, игнорируя таким образом любые переданные ей системой параметры командной строки, она может не возвращать код ошибки явно (в общем случае это, однако, не означает, что всегда будет возвращён код успешного завершения программы — на самом деле, может быть возвращено любое число). Пример функции main, принимающий параметры командной строки, выглядит так:
При вводе в стандартный поток ввода с консоли можно явно завершить ввод с помощью управляющего символа “конец файла” end-of-file, EOF. В Windows для этого нужно нажать Ctrl Z и Enter, в Unix-подобных системах с той же целью традиционно используется сочетание Ctrl D.
– Настройка сетевого интерфейса через графический интерфейс [GUI]
Первым делом нам нужно попасть в “Центр управления сетями и общим доступом”.
Я привожу два универсальных способа, как в него попасть, используя операционные системы: Windows 7, 8, 8.1, 10, Server 2008, 2021, 2021.
Первый способ: Заходим в “Панель управления” -> “Центр управления сетями и общим доступом”.(Рис.1.1)

Второй способ: через “Выполнить”:
Заходим в “Выполнить”[Win R] -> Вводим команду приведённую ниже и жмём – “OK”.(Рис.1.2)
control.exe /name Microsoft.NetworkandSharingCenter
И так, перед нами “Центр управления сетями и общим доступом”. В разделе “Просмотр активных сетей” мы видим сети, к которым подключен наш ПК…
На данный момент мой ПК подключен к одной сети. Для того чтобы перейти к параметрам соединения, в строке “Подключение:” нажмите на тип подключения, в моём случае это “Ethernet” [При подключении по Wi-FI надпись будет – “Беспроводное сетевое соединение (название сети)”](Рис.1.3)

Откроется окно – “Состояние – Ethernet”, показывающее состояние подключения к сети. Нас интересует кнопка “Свойства”, нажимаем её -> открывается окно “Ethernet: свойства” в списке компонентов выбираем “IP версии 4 (TCP/IPv4)” и опять же жмём кнопку “Свойства”.(Рис.1.4)
Откроется окно – “Свойства: IP версии 4 (TCP/IPv4)”, оно то нам и нужно.(Рис.1.5)

1. – GUI – Получение динамического IP-адреса [Автоматически по DHCP]
Перед нами окно – “Свойства: IP версии 4 (TCP/IPv4)”.(Рис.1.5)
Для того чтобы получить динамические [автоматически по DHCP] настройки сети, а именно – IP-адрес, Маску подсети, Основной шлюз и DNS-серверы, нужно переключить радиокнопки в положения:
- Получить IP-адрес, автоматически
- Получить адрес DNS-сервера автоматически
В окне “Свойства: IP версии 4 (TCP/IPv4)” нажимаем “ОК”.

В окне “Ethernet: свойства” тоже нажимаем “ОК”.(Рис.1.6)

Перед нами окно “Состояние – Ethernet” -> нажимаем кнопку “Сведения…” -> откроется окно “Сведения о сетевом подключении”, в котором мы можем посмотреть настройки сети.(Рис.1.7)
И так мы видим:
- DHCP включен: Да
- Адрес IPv4: 10.0.0.50
- Маска подсети IPv4: 255.255.255.0
- Шлюз по умолчанию IP: 10.0.0.1
- DHCP-сервер IPv4: 10.0.0.1
- DNS-серверы IPv4: 8.8.8.8, 8.8.4.4
Настройки по DHCP получены, всё правильно, на этом с получением динамических сетевых настроек заканчиваем.

2. – GUI – Задать/Изменить статический IP-адрес [Указание IP-адреса вручную]
Статические настройки сети задаются вручную пользователем. Они прописываются в настройках сетевого подключения и жестко закрепляется за данным устройством.
В этом разделе, для моего ПК я пропишу статический IP-адрес – 10.0.0.10
Перед нами окно – “Свойства: IP версии 4 (TCP/IPv4)”.(Рис.1.8)
Для того чтобы задать/изменить статические настройки сети [Указать IP-адрес и DNS-серверывручную], нужно переключить радиокнопки в положения:
- Использовать следующий IP-адрес
- Использовать следующие адреса DNS-серверов
И ввожу:
Для сохранения нажимаем “ОК”.

В окне “Ethernet: свойства” тоже нажимаем “ОК”.(Рис.1.9)

Перед нами окно “Состояние – Ethernet”(Рис.1.10) -> нажимаем кнопку “Сведения…” откроется окно “Сведения о сетевом подключении”, в котором мы можем посмотреть настройки сети.
И так мы видим:
- DHCP включен: Нет
- Адрес IPv4: 10.0.0.10
- Маска подсети IPv4: 255.255.255.0
- Шлюз по умолчанию IP: 10.0.0.1
- DNS-серверы IPv4: 10.0.0.1
Нужные нам сетевые настройки применились. Всё отлично.

На этом с настройкой сети через графический интерфейс[GUI]заканчиваем.
– Настройка сетевого интерфейса через командную строку [CMD]
Для начала нам необходимо запустить командную строку с правами администратора, для этого выполните действия -> Заходим в “Выполнить”[Win R] -> Вписываем – cmd -> Нажимаем на клавиатуре сочетание клавиш “CTRL” “SHIFT” “ENTER”. Всё! Приступаем.
И так на данный момент настройки сети на моём ПК прописаны вручную[Статические].
Убедимся в этом набрав в командной строке [CMD] команду – ipconfig /all (Рис.2.1):
ipconfig /all
Видим:
- Адаптер Ethernet Ethernet:
- DHCP включен….: Нет [Речь идёт о DHCP-клиенте, и он выключен, а значит сетевые настройки прописаны вручную]
- IPv4-адрес….: 10.0.0.10(Основной)
- Основной шлюз…: 10.0.0.1
- DNS-серверы…: 10.0.0.1

Как-то не очень понятно в моём случае, название адаптера – “Адаптер Ethernet Ethernet:”, поэтому я решил воспользоваться другой командой, которая покажет мне имя интерфейса и его состояние(Рис.2.2):
netsh interface show interface
Ну вот… уже лучше – У меня есть интерфейс “Ethernet” в состоянии “Подключен”[connected].

Если у вас сетевой интерфейсотключен и вы хотите его включить, то воспользуйтесь командой:
1. – CMD – Получение динамического IP-адреса [Автоматически по DHCP]
Для того чтобы получить – IP-адрес, Маску подсети и Основной шлюз[Здесь отсутствует DNS-сервер] от DHCP-сервера, воспользуемся командой:
netsh interface ip set address "ИМЯ_ИНТЕРФЕЙСА" dhcpВ моём случае так(Рис.2.3):
netsh interface ip set address "Ethernet" dhcp
Проверим, какие сетевые настройки, наш ПК получил по DHCP, командой – ipconfig /all.(Рис.2.4)
ipconfig /all
Видим:
- Адаптер Ethernet Ethernet:
- DHCP включен….: да [Речь идёт о DHCP-клиенте, и он включен]
- IPv4-адрес….: 10.0.0.50(Основной) [Первый адрес из диапазона IP-адресов выдаваемых по DHCP]
- Основной шлюз…: 10.0.0.1
- DNS-серверы…: 10.0.0.1 [Остался старый, прописанный вручную, всё так и должно быть..]

Теперь наша задача по DHCP, получить адрес(а) DNS-сервера(ов).
Для этого воспользуемся командой:
netsh interface ip set dns "ИМЯ_ИНТЕРФЕЙСА" dhcpВ моём случае так (Рис.2.5):
netsh interface ip set dns "Ethernet" dhcp
Проверяем, командой – ipconfig /all, полученный по DHCP, адрес(а) DNS-сервера(ов):
ipconfig /all
Всё отлично! По DHCP получены адреса DNS-серверов: 8.8.8.8 и 8.8.4.4 (Рис.2.6)

2. – CMD – Задать/Изменить статический IP-адрес [Указание IP-адреса вручную]
В этом разделе, для моего ПК я пропишу статический IP-адрес – 10.0.0.5
Для того чтобы вручную назначить/изменить – IP-адрес, Маску подсети и Основной шлюз[Здесь отсутствует DNS-сервер], воспользуемся командой:
netsh interface ip set address name="ИМЯ_ИНТЕРФЕЙСА" static IP-АДРЕС МАСКА_ПОДСЕТИ ОСНОВНОЙ_ШЛЮЗ
В моём случае это выглядит так(Рис.2.7):
netsh interface ip set address name="Ethernet" static 10.0.0.5255.255.255.010.0.0.1

После ввода команды, сеть будет работать некорректно, так как полученные ранее, по DHCP, адреса DNS-серверов пропадут из конфигурации сети, и нам нужно их прописать вручную.
Приступим:
Указание/Смена Предпочитаемого DNS-сервера[При смене предпочитаемого сервера, пропадут альтернативные]:
netsh interface ip set dns "ИМЯ_ИНТЕРФЕЙСА" static АДРЕС_DNS-СЕРВЕРА-1
В моём случае так(Рис.2.8):
netsh interface ip set dns "Ethernet" static 8.8.8.8

Для корректной работы сети обычно требуется указать адрес одного DNS-сервера, в некоторых случаях требуется указать два, но я, в целях тестирования, укажу 3.
Указание Альтернативного DNS-сервера:
netsh interface ip add dns "ИМЯ_ИНТЕРФЕЙСА" АДРЕС_DNS-СЕРВЕРА-2
В моём случае так(Рис.2.9):
netsh interface ip add dns "Ethernet" 8.8.4.4

Указание Третьего DNS-сервера:
Добавляем точно так же, как и второй. В моём случае так(Рис.2.10):
netsh interface ip add dns "Ethernet" 10.0.0.1

Если кто-то ошибся с вводом адреса DNS-сервера, то удалить его можно командой:
netsh interface ip del dns "ИМЯ_ИНТЕРФЕЙСА" АДРЕС_DNS-СЕРВЕРА
И так проверяем, что у нас в итоге получилось командой – ipconfig /all.(Рис.2.11)
ipconfig /all
Видим:
- Адаптер Ethernet Ethernet:
- DHCP включен….: Нет [Речь идёт о DHCP-клиенте, и он выключен]
- IPv4-адрес….: 10.0.0.5(Основной)
- Основной шлюз…: 10.0.0.1
- DNS-серверы…: 8.8.8.8, 8.8.4.4, 10.0.0.1
Нужные нам сетевые настройки применились. Всё отлично.

На этом с настройкой сети через командную строку[CMD]заканчиваем.
1. – PowerShell – Получение динамического IP-адреса [Автоматически по DHCP]
Для того чтобы получить IP-адрес, Маску подсети и Основной шлюз от DHCP-сервера[Здесь отсутствует DNS-сервер], воспользуемся командой:
Set-NetIPInterface -InterfaceAlias "ИМЯ_ИНТЕРФЕЙСА" -Dhcp EnabledЛично у меня, интерфейс увидел DHCP-сервер только после рестарта интерфейса:
Restart-NetAdapter -InterfaceAlias "ИМЯ_ИНТЕРФЕЙСА"В моём случае так:
Set-NetIPInterface -InterfaceAlias "Ethernet" -Dhcp EnabledRestart-NetAdapter -InterfaceAlias "Ethernet"Проверим, какие сетевые настройки, наш ПК получил по DHCP.(Рис.3.3)
Get-NetIPConfiguration -Detailed -All
Как видим:
- InterfaceAlias: Ethernet [Name][Название сетевого интерфейса]
- IPv4Address : 10.0.0.52 [IP-адрес]
- IPv4DefaultGateway: 10.0.0.1 [Основной шлюз]
- NetIPv4Interface.DHCP: Enabled [Речь идёт о DHCP-клиенте, и он включен]
- DNSServer: 10.0.0.1 [DNS-сервер, остался старый, прописанный вручную, всё так и должно быть..]

Теперь наша задача по DHCP, получить адрес(а) DNS-сервера(ов).
Для этого воспользуемся командой:
Set-DnsClientServerAddress -InterfaceAlias "ИМЯ_ИНТЕРФЕЙСА" -ResetServerAddressesВ моём случае так (Рис.3.4):
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ResetServerAddressesПроверяем, полученные по DHCP, адрес(а) DNS-сервера(ов):
Get-NetIPConfiguration
Всё отлично! По DHCP получены адреса DNS-серверов: 8.8.8.8 и 8.8.4.4

Has sensible defaults but is configurable
Повторю, стандартные сценарии использования должны быть доступны без указания тысячи опций. Нестандартные сценарии не обязаны быть простыми в использовании, но должны быть все-таки доступными. Кроме того, набор опций-по-умолчанию должен быть настраиваемым.
Про стандартные сценарии всё понятно. Необходимо продумать, для чего программа будет использоваться и выбрать самые популярные параметры — параметрами по-умолчанию.У всех пользователей потребности немного разные, так что подумайте о как можно большем числе вариантов использования вашего скрипта.
Если ваше приложение будет выполнять одну задачу, но будет гибко настраиваться (в разумных пределах), пользователи скажут вам спасибо. Нестандартные сценарии должны быть, если они имеют применение. Не страшно, если для их выполнения придется указать множество опций — пользователь два раза подумает, для того ли предназначен ваш скрипт.
На последнем пункте остановлюсь подробнее. Что значит «набор опций-по-умолчанию должен быть настраиваемым»? Представьте, что вашей программой пользуются множество людей и делают это часто. К примеру, вы написали утилиту для бэкапа БД. Ваш сисадмин пользуется утилитой каждый день и использует набор опций по-умолчанию (например, –no-scheme –gzip), просто набирая db_backup my_db.
Но кроме админа программой пользуются ваши коллеги разработчики, у которых схема БД меняется каждый день. И они каждый день вынуждены писать db_backup –scheme my_db, им нельзя забыть этот ключик. Вы, возможно, будете правы, если скажете, что сисадминские настройки важнее и будут настройками по-умолчанию… но в действительности там будут ещё и опции –login, –password, –host, –force, и такой набор параметров уже сложно воспроизвести без ошибок даже сисадмину, у которого остальные настройки идут по-умолчанию.
Для этого служат файлы вида ~/.myapp.rc. В файле нет никакой магии, это лишь соглашение. Каждый пользователь в своём домашнем каталоге может создать файл с предпочтительными для него настройками по-умолчанию. В домашнем каталоге — чтобы разные пользователи могли задавать разные умолчания.
Точка в начале файла — чтобы сделать его скрытым. Расширение .rc — дань традиции. Что должно храниться в этом файле? Просто перечисление тех опций, которые отличаются от стандартных значений по умолчанию. Для этого конфигурационного файла крайне удобно использовать формат YAML. Приведу пример:
Helpful
Представьте, что вы впервые видите программу awesome_program, которую вы собираетесь использовать. Будучи опытным пользователем, вы наверняка наберете
awesome_program --help
в надежде увидеть порядок аргументов, набор опций и примеры использования. Когда вы выпускаете свою программу, помните, что пользователь, который впервые её увидел, наверняка первым делом сделает то же самое, поэтому пусть у вас будут ключи
-h--help
наготове. Если вы используете библиотеку типа OptionParser, в строку подсказки у вас автоматически будет внесено перечисление всех опций, которые программа распознает с теми описаниями, которые вы дадите.
Помимо строки подсказки имеет смысл написать расширенную справку в man. Впрочем, насколько я знаю, rubygems автоматически не устанавливает странички в man. Однако существует гем gem-man, который позволяет показывать странички man-документации для установленных в системе гемов.
Для того, чтобы создать man-документацию необходимо создать файл в непростом формате nroff. Для упрощения задачи воспользуйтесь библиотекой-конвертером ronn, которая позволяет писать странички документации в более простом формате. Когда всё будет готово, вы можете воспользоваться командой gem man awesome_gem — и увидеть строку помощи.
Кроме того, вы можете прописать alias gem=’gem man -s’. При этом команда man заменяется командой gem man, что позволяет искать man-ом помощь по гемам. По тем запросам, которые gem-man обработать не смог, происходит автоматическое перенаправление на соответствующую страничку обычного man-а.Если соберетесь делать свои man-подсказки, загляните в книгу, там этому уделяется значительно больше внимания.
Чтобы ещё больше облегчить пользователю запуск команды, можно сделать автодополнение команды на уровне шелла (работает не во всех шеллах). Это позволит по нажатию на кнопку tab автоматически дополнять названия команд, имена файлов итд. У пользователя будет меньше шансов сделать орфографическую ошибку, и он потратит намного меньше времени на написание команды.
Для того, чтобы сделать tab-completion и хранить историю команд внутри программы (в интерактивном режиме в программах типа irb) достаточно воспользоваться встроенной в руби библиотекой readline. Она позволяет автоматически сохранять историю всех введенных команд — за счет использования команды Readline.readline вместо gets.
В компьютерных играх
Изначально консоль в играх использовалась для отладки.
Как только появился интерфейс командной строки, стали появляться и игры, его использующие, особенно актуально это было на тех платформах, где более сложные интерфейсы (графические) было невозможно реализовать вследствие аппаратных ограничений.
Наиболее ярким примером игр, использующих интерфейс командной строки, могут быть названы текстовые квесты, а также сетевые многопользовательские ролевые игры — MUD. Команды в таких играх вводятся на так называемом псевдоестественном языке.
Во многих графических играх присутствует консоль для облегчения доступа к настройкам игры, поскольку в сложных играх реализовать все команды через систему меню неудобно. Первая такая игра — Quake. Стандартная кнопка для вызова консоли — ~ (тильда);
реже ↵ Enter’, ещё реже ⇧ Shift D . Консоль позволяет вносить изменения в настройки игры оперативнее, чем меню — например, набрать name Terminator быстрее, чем найти то меню, в котором вводится имя игрока, и ввести Terminator. Консоль позволяет в числе прочего вводить чит-коды.
Также консоль предоставляет возможность изменять настройки назначения горячих клавиш, что может использоваться для обмана соперника в многопользовательских играх, например, предложением ввести команду unbindall, отменяющую все горячие клавиши, в том числе и отвечающие за движение игрока.
Интерфейс, который предоставляется моддерам, не всегда позволяет менять меню; но он всегда позволяет добавлять свои консольные команды. Например, в DotA (карте для игры Warcraft III) режим игры задаёт участник, играющий синими, через консоль.
Давным-давно в далёкой-далёкой серверной…
С первых дней развития информатики людям нужен был эффективный способ передавать компьютеру команды и данные и видеть результат выполнения этих команд/вычислений.
Одним из первых по-настоящему эффективных человеко-машинных интерфейсов стал Tele-Typewriter или «телетайп». Это электромеханическая машина с клавиатурой для ввода данных и каким-нибудь устройством вывода — сначала использовался принтер, позже экран.
Вводимые оператором символы локально буферизуются и отправляются с телетайпа на соседний компьютер или мейнфрейм в виде серии сигналов по электрическому кабелю (например, RS-232) со скоростью 10 символов в секунду (110 бод, бит в секунду, bps):

Телетайп Model 33 ASRПримечание: Дэвид Гессвейн ведёт отличный сайт по PDP-8, где можно найти больше информации об ASR33 (и соответствующей технологии PDP-8), в том числе фотографии, видео и др.
Программа на компьютере получает введённые символы, решает, что с ними делать, и, возможно, асинхронно отправляет ответ на телетайп. Телетайп может напечатать/показать оператору полученные символы.
Затем технология улучшилась, скорость передачи выросла до 19200 bps, а шумные и дорогие принтеры заменили ЭЛТ-дисплеями (широко распространённый тип дисплеев в 80-е и 90-е годы), как на популярном терминале DEC VT100:

Терминал DEC VT100
Хотя технология улучшилась, но эта модель — терминал отправляет символы программе на компьютере, а он выдаёт текст для пользователя — осталась и сегодня как фундаментальная модель взаимодействия всех командных строк и консолей на всех платформах!

Архитектура терминала и командной строки
Модель по-своему элегантна. Одна из причин — в сохранении простоты и цельности каждого компонента: клавиатура выдаёт символы которые буферизуются как электрические сигналы. Устройство вывода просто выдаёт на дисплей (бумагу/экран) символы, полученные с компьютера.
На каждом этапе в системе передаётся только поток символов, так что это относительно простой процесс для внедрения различной инфраструктуры связи. Например, для добавления модемов, чтобы передавать потоки входных и выходных символов на большие расстояния по телефонным линиям.
Достоинства
- Легкость автоматизации. Shell script в UNIX-подобных системах является полноценным интерпретируемым языком программирования и способен автоматизировать любую системную задачу. В Windows присутствует их примитивный аналог — пакетные файлы, и более мощный аналог — powershell. По сути, это — простейшая программируемость. С графическим интерфейсом без поддержки программой командной строки это сделать почти невозможно.
- Можно управлять программами, не имеющими графического интерфейса (например, выделенным сервером).
- Любую команду можно вызвать небольшим количеством нажатий.
- Можно обращаться к командам для разных исполнимых файлов почти мгновенно и непосредственно, тогда как в GUI приходится сначала запускать, а затем закрывать графический интерфейс для каждого исполнимого файла.
- Просмотрев содержимое консоли, можно повторно увидеть промелькнувшее сообщение, которое вы не успели прочитать.
- Можно пользоваться удаленным компьютером с любого устройства подключаемого к Интернету или локальной сети (ПК, субноутбук, КПК, сотовый телефон, портативная игровая консоль) без особых затрат трафика (единицы килобайт за сеанс).
- Отсутствие деталей интерфейса, таких как пусковые панели и рамки окон, что при равных разрешениях позволяет вместить значительно больше текста на страницу.
- Возможность работы через стандартизированные линии передачи данных, такие как RXD, TXD в RS232, на небольшой скорости (наиболее часто 9600 бод), делает этот интерфейс основным, а порой и единственно возможным, для встраиваемых систем, систем с небольшой вычислительной мощностью, иных недорогих и экономичных устройств. Принтеры, жёсткие диски, роутеры, датчики пожарной охраны — лишь малая часть примеров такого оборудования.
- Легче отладка сообществом. Если пользователь столкнулся с проблемой или ошибкой, на интернет-форуме достаточно оставить копию диалога пользователя и ЭВМ. Такой листинг имеет однозначное толкование (в том числе по порядку ввода и появления сообщений), в отличие от графического интерфейса, когда нужно не только отправить копию экрана, и порой не одну (что уже само по себе может быть проблемно), но также и входящие данные (какую «галочку» и где нажать, и тому подобное). К тому же, копия экрана одной и той же программы может существовать на разных языках, что усложнит отладку международным сообществом.
Итак, на чём мы остановились?
Дорогой читатель, если вы прочитали всё вышенаписанное, спасибо вам, и примите поздравления — теперь вы знаете больше о консоли Windows, чем большинство ваших друзей, и, вероятно, даже больше, чем вы
сами
хотели узнать! Какая удача!
![]()
Мы многое рассмотрели в этой статье:
- Основные строительные блоки консоли Windows:
- Condrv.sys — коммуникационный драйвер
- ConHost.ехе — UX консоли и механика:
- Сервер API — сериализует вызовы API и текстовые данные с помощью сообщений IOCTL, отправляемых в/из драйвера
- API — функциональность консоли
- Буферы — буфер ввода, хранящий пользовательский ввод, и буфер вывода, хранящий выходной/отображаемый текст
- Парсер VT — преобразует последовательности ANSI/VT из текстового потока в вызовы API
- UX консоли — состояние UI консоли, настройки, функции
- Другое — технические данные, безопасность и проч.
- Что делает консоль
- Отправляет пользовательский ввод в подключенное приложение командной строки
- Получает и отображает выходные данные из подключенного приложения командной строки
- Чем консоль отличается от терминалов *NIX
- NIX: «Всё представляет собой файл/текстовый поток»
- Windows: «Все представляет собой объект, доступный через API»
- Проблемы консоли
- Консольные приложения и приложения командной строки взаимодействуют через запросы вызовов API и текст, сериализованный в Сообщения IOCTL
- Консольный API могут вызвать только приложения командной строки Windows
- Сложнее портировать приложения на/из Windows
- Приложения взаимодействуют с консолью через Windows API
- Затрудняет удалённое взаимодействие с приложениями и средствами командной строки Windows
- Зависимость от IOCTL нарушает схему «обмен символами» терминала
- Затрудняет эксплуатацию инструментов командной строки удалённого с не-Windows машин
- Запуск приложений командной строки Windows является «необычным»
- К приложениям командной строки можно присоединить только ConHost.exe
- Сторонние терминалы вынуждены создавать внеэкранную консоль, отправлять туда символы и скрапить экран
- Windows исторически не понимает последовательности ANSI/VT
- В основном исправлено в Windows 10
- У консоли ограниченная поддержка Юникода и в настоящее время проблемы с хранением и рендерингом UTF-8 и глифов, которые нуждаются в соединительных символах нулевой ширины
В следующих нескольких статьях этой серии мы более подробно разберём консоль и обсудим решение этих проблем… и не только!
Как всегда, следите за обновлениями.
Соглашения по использованию опций
Опции могут быть указаны как в короткой
-i
, так и в длинной
--ignore-case
форме. В принципе, ничто не мешает вам развлекаться с форматом опций как угодно, ведь опции командной строки вы можете перехватить в скрипте напрямую. Но лучше все же придерживаться выработанных Unix-сообществом правил, так как они отточены временем, и большинство людей привыкло к ним. Кроме того, существуют готовые библиотеки, позволяющие удобно работать с этими опциями
Эти правила таковы:
- Длинная опция (
--long-option) начинается с двух дефисов. В названии опции не может быть пробелов, зато одиночные дефисы вполне допустимы. - Короткая опция (
-l) состоит из одной буквы (как правило, регистр имеет значение) и предшествующего ей дефиса — одного. Удобно, когда короткая опция — это первая буква длинного аналога, так проще запомнить её значение. - Несколько коротких опций можно объединить вместе следующим образом:
ls -a -lэквивалентноls -al. После одиночного дефиса может идти сколько угодно опций без аргументов - Если у короткой опции есть параметр, обычно он может идти как сразу после опции, так и отделяться от неё пробелом.
-C4или-C 4. Я не случайно сказал «обычно». Так, например, стандартная руби-библиотека optparse обрабатывает эти два случая одинаково. Утилита ls обрабатывает сходные опции также одинаково. А вот утилита grep, например, считает, что-Cотдельно, а4— отдельно. Может быть, это баг. Я не мог не упомянуть, что исключения бывают, но пользователи вряд ли будут вам благодарны, если ваша программа станет ещё одним исключением.
Я не знаю, как обычно поступают в случае, когда короткая опция имеет нечисловой параметр со значением по-умолчанию (-c [param]), ведь-cxyzможно трактовать и как-с xyz, и как-c -x -y -z. Пользователю лучше всегда писать пробел в случае наличия у опции необязательного параметра. Программисту лучше заранее подумать о том, как минимизировать проблемы связанные со слитным написанием опций. - В случае длинной опции обычно допускается использование пробела перед параметром, но желательно использовать знак равенства.
ls --width=10илиls --width 10
Без разделительного символа после длинной опции параметр не указывают (сами посудите, какая путаница получится, особенно если параметр не числовой). - Каждая опция может иметь как короткую форму, так и длинную. А может быть и так, что есть только короткая или только длинная. Впрочем, наличие длинной формы для каждой из опций крайне желательно.
- Для булевых опций можно указать опциональный префикс
no-, например:--[no-]pager. Опция –pager задает разделение на страницы,--no-pagerуказывает, что разделения на страницы быть не должен, а отсутствие опции сохраняет значение по-умолчанию. Это особенно важно в случае, когда опции по-умолчанию конфигурируемы. Без префикса, например, невозможно было бы отменить значение опции заданной по-умолчанию.
В заключение не могу не рассказать о нескольких популярных библиотеках.
Если вы хоть немного проработали с руби-проектами, наверняка уже встречали команду
, а может и команду
. Это — специализированные библиотеки, позволяющие при помощи специального DSL описать набор утилиток, как набор задачи автоматизации.
Rake — это улучшенный аналог программы make для ruby. Он заглядывает в Rakefile текущего или родительского каталога и ищет там описание задачи. Например, bundler создает для каждого нового гема Rakefile с набором задач: build, install, release, что позволяет инсталлировать и публиковать собственные гемы одной командой.
Одной из отличительных фишек rake является система зависимостей между задачами — так rake release сначала выполнит задачу build и только затем release. К сожалению, передавать аргументы в rake то ли нельзя, то ли нетривиально. На хабре, кстати, уже был вводный пост про rake.
Thor — система похожая на rake. Она помимо прочего позволяет «устанавливать» задачи в систему. Подробнее лучше посмотреть в других источниках. Я упоминаю об этих библиотеках, поскольку они могут облегчить вам жизнь, если вам не нужна никакая сложная обработка опций и аргументов.
Большую часть повествования я использовал для разбора опций OptionParser из стандартной библиотеки ruby. Это весьма удобная библиотека, однако некоторые считают её довольно тяжеловесной и пишут обертки. Некоторые обертки концентрируются на том, чтобы упростить задание опций, некоторые — на том, чтобы сделать хэш опций доступным глобально итд.
Если вам покажется, что OptionParser вас тормозит, можете подобрать одну из готовых оберток (список можно найти на сайте книги — см. начало статьи) или сделать свою.Есть у OptionParser и другие недостатки: она не способна отличить глобальные опции от локальных в программном пакете (это разделение мы сами сделали; вообще говоря, оно ниоткуда не следует, кроме успешного применения концепции в некоторых крупных проектах, таких как git).
Ещё одна особенность OptionParser-а (это не прописано в спецификации и я полагаю, что это баг) — то что отрицательные числа в аргументах он понимает как опции. Полагаю, что рано или поздно этот баг исправят, но если ваша программа принимает числовые аргументы — будьте осторожны и тщательно тестируйте программу.
Для построения программных пакетов автор книги Дэвид Коупленд сделал весьма неплохой гем GLI. Он различает глобальные опции, опции команды и распознает саму команду. Проект живой и периодически получает обновления.
Кроме того, не могу не упомянуть довольно сырой, но крайне любопытный проект — docopt. Это — библиотека, которая по строке подсказки генерирует парсер опций, тогда как OptionParser и родственные библиотеки делают наоборот. Эта библиотека изначально написана на python и портирована на довольно большое количество языков.
P.S. Помимо описанного мной в статье, рубисту, работающему с приложениями командной строки, есть смысл почитать про специальную переменную ARGF. Если все аргументы вашего скрипта — имена файлов, то ARGF — просто конкатенация содержимого всех файлов.




