Русский
Русский
English
Статистика
Реклама

Console

Если б тайлы были чуть больше тайлами. Выход за границы наивного представления

30.10.2020 08:15:26 | Автор: admin
image
Тот кто когда-нибудь задумывался о том как работает графическая часть 2д ретро ускорителя, примерно представляет как именно она рисует эти пресловутые Tiles, которые к слову из определения не обязаны быть прямоугольными. Тайлинг это про замощение плиткой. Да чаще всего разработчики железного апи понимают это и методы соответственно называют drawRect а не drawTile. Любой прямоугольник действительно может быть тайлом, но обратное не верно! И тут назревает вопрос: Почему 2D ускорители упорно ускоряют только rect Простой ответ на этот вопрос потому что все остальное слишком сложно для простой железки. Но тут я бы и поспорил. Можно мредложить как минимум одно простое, но очень функциональное расширение этой базовой абстракции, следующее:

Я давно не писал на хабр. Потому что все время хотелось сделать идеально есть у меня такая болезнь. Сам хабр тем временем ощутимо снизил планку, а я все не пишу да не пишу. Поэтому сегодняшняя статья родившаяся без подготовки из просто беседы с другом. Однако есть такие друзья с которыми хочется беседовать глубоко и обстоятельно.

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

Иными словами сегодня я ищу компромис между качеством контента и временем которое могу на него уделить. Надеюсь на понимание.

Итак начнем

Рассмотрим для образца общую функцию копирования произвольной прямоугольной области. Видим что это просто двойной цикл по строкам и по элементам строк:
image
Заметим, что этот блок кода обычно тривиально повторяют в х4 комбинациях направлений обхода обоих циклов для реализации Отражений:
image
Еще х2 комбинаций дает перестановка порядка на dst[x][y] отражая по оси y=x.
Попутно эти 8 вариантов отражений являются всеми возможными поворотами кратными 90гр со всеми их зеркальными отражениями слева направо.

А теперь посмотрим на то как еще минимальными добавлениями можно было бы сильно видоизменить данный базовый элемент.
Возможно никому не приходило это в голову, но почему тайл не может быть например параллелепипедом? Забегая вперед скажу, что это расширение носит минимальные накладные расходы, потому что для реализации этого необходимо добавить всего пару инкрементов на строку:
image
В результате комбинации с x/y перестановкой мы получим параллелепипед парой сторон всегда ориентированных по одной ортогонали, а две других будут под углом. Что же нам дает эта дешево полученная мелочь? Ну во первых возможность использовать три фиксированные проекции:
image
Если приращение сделать не константой, а дробным числом, угол сдвига сможет стать свободным. Что прекрасно может быть использовано как для эффектов покачиваний тайла в сайд скроллере:
image
Так и для построения стен в пространстве изометрических проекций:
image
И помним, что говоря дробные это не означает непеременную поддержку float! О чем современный избалованный программист порой начинает забывать. float это дробные с Плавающей точкой, а тут она совершенно не нужна. Поддержкой же дробных с фиксированной точкой (strict point), обладает Аппаратно по сути любой процессор имеющий сдвоенные регистры, где верхний из пары может читаться как отдельное значение. Более того в этом случае НИ одной лишней инструкции не добавиться. (Ну за исключением того что задаваться наклон будет в 256-ых долях) Так что это все бесплатно, берите ребята!

Еще один аналогичный шаг который попроситься по индукции у внимательного читателя, это повторение этого функционала ко второй границе перебора внутреннего цикла. Мы ведь можем отдельно развязать приращение end. И тогда в общем случае мы сможем рисовать любые лежащие основанием на одной из ортогоналей трапеции (как частный случай треугольники):
image
И на хрена же они нам сдались? Подумаете вы, но тут же сами догадаетесь, что всего за два таких вызова отрисовки, вы элементарно текстурируете плоскость из ортогонально ориентированных шестигранников или диагонально ориентированных ромбов, из предварительно подготовленного под это тайлсета. Довольно часто встречающиеся в игрострое кейсы замощений между прочим.
Во времена J2ME
Во времена J2ME мне было интересно реализовать изометрическую игрушку, и я скажу вам это тот еще пот. Но главное, что обходить ограничения приходилось ценой траты ресурсов как на комбинированный тайлсет, так и на постоянное преобразование данных карты между моделью хранения и представлением

Было бы здорово иметь для них аппаратную поддержку на ускорителе:
image
Дальнейшее описанное является скорее бонусным функционалом, т.к. реализовать его целенаправленно можно лучше. Однако, если говорить в том числе только о заливке примитива только цветом (или плоским паттерном), для каких то простых дополнений имеющегося игрового пространства
Той же парой вызовов отрисовки можно составить любой не ограниченный частностями треугольник (он просто сечется на два ортогональной линией по средней точке, это очень похоже на его классический алгоритм отрисовки) И из этого уже можно строить построения в перспективе. Кстати из самих трапеций например элементарно дешего получается вертикально фиксированная 3D проекция DOOM1 вида:
image
При желании можно пробовать рисовать заливкой даже что-то более менее крупно-воксельное. В зависимости от степеней свободы проекции затрачивая больше или меньше вызовов отрисовки. Первые две перспективные проекции ниже являются фиксированными однако каждая из них имеет 4 симметричных представления итого 8 углов отображения. Третья является общим случаем и фиксирована только вертикально. Вращаясь вокруг вертикальной оси она будет попеременно переходить то в первую, то во вторую:
image
Разбиения фигур на трапеции лучше реализовывать по возможности на более вытянутые вдоль ортогонали разреза куски, потому что дополнительный функционал вызова хоть и не дорог, но связан с итерациями внешнего только цикла, которых желательно что бы было меньше. На самом деле это справедливо даже для рисования базового алгоритма прямоугольника горизонтальный рисовать эффективнее вертикального.

Выполнение на бонусном функционале перспективного текстурирования в этих вызовах, возможно, но требует реализации дробного скейлинга и связано с внесением дополнительных коэффициентов расхождения при src, и коэффициентов прореживания, которые пуще того необходимо будет раз это 3D довольно криво рассчитывать в динамике, что не целесообразно для данной простой архитектуры.

Ну и заканчивая реванш перед хабром за свой собственный интерес, я все же не удержусь кратко пояснить откуда берется вся эта магия коэфициентов приращения алгебраически. Формула прямой y=kx+b которую учат в школах, передает нам здесь привет сразу дважды. Все коэффициенты пригодились на своих местах:
image

Почему же мне не встречались аппаратные 2д ускорители с такой реализацией тайлов я могу лишь гадать:
Понятие Тайл было столь жестко зафиксированным на практике шаблоном, что никто и не думал, что можно было бы сделать чуть больше получив много больше.
Или никто не хотел потратить и пары тактов на такие функции, потому что не анализировал изложенные широко простирающиеся следствия, или спроса на них тогдашние разработчики с незамысловатыми играми не создавали. Тем не менее был же mode7 (читовый алгоритм реализации перспективного текстурирования, через вывод кусками прямоугольных спрайтов на железе с поддержкой дробного скейлинга)
Или я не достаточно олд, и мне надо олдеть дальше
Подробнее..

Перевод Лучшие практики bash-скриптов краткое руководство по надежным и производительным скриптам bash

17.08.2020 18:04:48 | Автор: admin

Shell wallpaper by manapi

Отладка сценариев bash это как поиск иголки в стоге сена, тем более, когда новые дополнения появляются в существующей кодовой базе без своевременного рассмотрения вопросов структуры, логирования и надежности. В таких ситуациях можно оказаться как из-за собственных ошибок, так и при управлении сложными нагромождениями скриптов.

Команда Mail.ru Cloud Solutions перевела статью с рекомендациям, благодаря которым вы сможете лучше писать, отлаживать и поддерживать свои сценарии. Хотите верьте, хотите нет, но ничто не может сравниться с удовлетворением от написания чистого, готового к использованию bash-кода, который работает каждый раз.

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

Обработчики ловушек


Большинство скриптов bash, с которыми я сталкивался, никогда не использовали эффективный механизм очистки, когда во время выполнения скрипта происходит что-то неожиданное.

Неожиданности могут возникнуть извне, например получение сигнала от ядра. Обработка таких случаев чрезвычайно важна для того, чтобы сценарии были достаточно надежными для запуска в продакшен-системах. Я часто использую обработчики выхода, чтобы реагировать на такие сценарии:

function handle_exit() {  // Add cleanup code here  // for eg. rm -f "/tmp/${lock_file}.lock"  // exit with an appropriate status code}  // trap <HANDLER_FXN> <LIST OF SIGNALS TO TRAP>trap handle_exit 0 SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM

trap это встроенная команда оболочки, помогающая вам зарегистрировать функцию очистки, которая вызывается в случае каких-либо сигналов. Однако следует соблюдать особую осторожность с такими обработчиками, как SIGINT, который вызывает прерывание сценария.

Кроме того, в большинстве случаев следует ловить только EXIT, но идея в том, что вы действительно можете настроить поведение скрипта для каждого отдельного сигнала.

Встроенные функции set быстрое завершение при ошибке


Очень важно реагировать на ошибки, как только они возникают, и быстро прекращать выполнение. Ничего не может быть хуже, чем продолжать выполнение команды вроде такой:

rm -rf ${directory_name}/*

Обратите внимание, что переменная directory_name не определена.

Для обработки таких сценариев важно использовать встроенные функции set, такие как set -o errexit, set -o pipefail или set -o nounset в начале скрипта. Эти функции гарантируют, что ваш скрипт завершит работу, как только он встретит любой ненулевой код завершения, использование неопределенных переменных, неправильные команды, переданные по каналу и так далее:

#!/usr/bin/env bashset -o errexitset -o nounsetset -o pipefailfunction print_var() {  echo "${var_value}"}print_var$ ./sample.sh./sample.sh: line 8: var_value: unbound variable

Примечание: встроенные функции, такие как set -o errexit, выйдут из скрипта, как только появится необработанный код возврата (кроме нуля). Поэтому лучше ввести пользовательскую обработку ошибок, например:

#!/bin/basherror_exit() {  line=$1  shift 1  echo "ERROR: non zero return code from line: $line -- $@"  exit 1}a=0let a++ || error_exit "$LINENO" "let operation returned non 0 code"echo "you will never see me"# run it, now we have useful debugging output$ bash foo.shERROR: non zero return code from line: 9 -- let operation returned non 0 code

Подобное написание скриптов заставляет вас внимательнее относиться к поведению всех команд в скрипте и предусматривать возможность возникновения ошибки прежде, чем она застанет врасплох.

ShellCheck для выявления ошибок во время разработки


Стоит интегрировать что-то вроде ShellCheck в ваши конвейеры разработки и тестирования, чтобы проверять ваш код bash на применение лучших практик.

Я использую его в своих локальных средах разработки, чтобы получать отчеты о синтаксисе, семантике и некоторых ошибках в коде, которые я мог пропустить при разработке. Это инструмент статического анализа для ваших скриптов bash, и я настоятельно рекомендую его применять.

Использование своих exit-кодов


Коды возврата в POSIX это не просто ноль или единица, а ноль или ненулевое значение. Используйте эти возможности для возврата пользовательских кодов ошибок (между 201-254) для различных случаев ошибок.

Эта информация может затем использоваться другими сценариями, которые обертывают ваш, чтобы точно понять, какой тип ошибки произошел, и реагировать соответствующим образом:

#!/usr/bin/env bashSUCCESS=0FILE_NOT_FOUND=240DOWNLOAD_FAILED=241function read_file() {  if ${file_not_found}; then    return ${FILE_NOT_FOUND}  fi}

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

Функции-логгеры


Красивое и структурированное ведение логов важно, чтобы легко понять результаты выполнения вашего скрипта. Как и в других языках программирования высокого уровня, я всегда использую в моих скриптах bash собственные функции логирования, такие как __msg_info, __msg_error и так далее.

Это помогает обеспечить стандартизированную структуру ведения логов, внося изменения только в одном месте:

#!/usr/bin/env bashfunction __msg_error() {    [[ "${ERROR}" == "1" ]] && echo -e "[ERROR]: $*"}function __msg_debug() {    [[ "${DEBUG}" == "1" ]] && echo -e "[DEBUG]: $*"}function __msg_info() {    [[ "${INFO}" == "1" ]] && echo -e "[INFO]: $*"}__msg_error "File could not be found. Cannot proceed"__msg_debug "Starting script execution with 276MB of available RAM"

Я обычно стараюсь иметь в своих скриптах какой-то механизм __init, где такие переменные логгера и другие системные переменные инициализируются или устанавливаются в значения по умолчанию. Эти переменные также могут устанавливаться из параметров командной строки во время вызова скрипта.

Например, что-то вроде:

$ ./run-script.sh --debug

Когда такой скрипт выполняется, в нем гарантировано, что общесистемные настройки установлены в значениях по умолчанию, если они обязательны, или, по крайней мере, инициализированы чем-то соответствующим, если это необходимо.

Я обычно основываю выбор, что инициализировать, а что нет, на компромиссе между пользовательским интерфейсом и деталями конфигураций, в которые пользователь может/должен вникнуть.

Архитектура для повторного использования и чистого состояния системы


Модульный / многоразовый код


 framework    common       loggers.sh       mail_reports.sh       slack_reports.sh    daily_database_operation.sh

Я держу отдельный репозиторий, который можно использовать для инициализации нового проекта/скрипта bash, который хочу разработать. Всё, что можно использовать повторно, может быть сохранено в репозитории и получено в других проектах, которые хотят использовать такие функциональные возможности. Такая организация проектов значительно уменьшает размер других скриптов, а также гарантирует, что кодовая база мала и легко тестируема.

Как и в приведенном выше примере, все функции ведения логов, такие как __msg_info, __msg_error и другие, например отчеты по Slack, содержатся отдельно в common/* и динамически подключаются в других сценариях, вроде daily_database_operation.sh.

Оставьте после себя чистую систему


Если вы загружаете какие-то ресурсы во время выполнения сценария, рекомендуется хранить все такие данные в общем каталоге со случайным именем, например /tmp/AlRhYbD97/*. Вы можете использовать генераторы случайного текста для выбора имени директории:

rand_dir_name="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)"

После завершения работы очистка таких каталогов может быть обеспечена в обработчиках ловушек, обсуждаемых выше. Если об удалении временных директорий не позаботиться, они накапливаются, и на каком-то этапе вызывают неожиданные проблемы на хосте, например заполненный диск.

Использование lock-файлов


Часто нужно обеспечить выполнение только одного экземпляра сценария на хосте в любой момент времени. Это можно сделать с помощью lock-файлов.

Я обычно создаю lock-файлы в /tmp/project_name/*.lock и проверяю их наличие в начале скрипта. Это помогает корректно завершить работу скрипта и избежать неожиданных изменений состояния системы другим сценарием, работающим параллельно. Lock-файлы не нужны, если вам необходимо, чтобы один и тот же скрипт выполнялся параллельно на данном хосте.

Измерить и улучшить


Нам часто приходится работать со сценариями, которые выполняются в течение длительного периода времени, например ежедневными операциями с базами данных. Такие операции обычно включают в себя последовательность шагов: загрузка данных, проверка на наличие аномалий, импорт данных, отправка отчетов о состоянии и так далее.

В таких случаях я всегда стараюсь разбивать сценарий на отдельные маленькие скрипты и сообщать об их состоянии и времени выполнения с помощью:

time source "${filepath}" "${args}">> "${LOG_DIR}/RUN_LOG" 2>&1

Позже я могу посмотреть время выполнения с помощью:

tac "${LOG_DIR}/RUN_LOG.txt" | grep -m1 "real"

Это помогает мне определить проблемные/медленные области в скриптах, которые нуждаются в оптимизации.

Удачи!

Что еще почитать:

  1. Go и кеши GPU.
  2. Пример event-driven приложения на основе вебхуков в объектном S3-хранилище Mail.ru Cloud Solutions.
  3. Наш телеграм-канал о цифровой трансформации.
Подробнее..

Начало работы с Windows Terminal

22.12.2020 10:22:28 | Автор: admin
Привет, Хабр! Сегодня делимся гайдом по началу работы с Windows Terminal. Да, поскольку он о начале работы с инструментом, в основном в материале описываются какие-то базовые моменты. Но я думаю, что и профессионалы смогут подчерпнуть для себя что-то полезное, как минимум из списка полезных ссылок в конце статьи. Заглядывайте под кат!



Установка


Windows Terminal доступен в двух разных сборках: Windows Terminal и Windows Terminal Preview. Обе сборки доступны для загрузки в Microsoft Store и на странице выпусков GitHub.

Требования


Для запуска любой сборки Windows Terminal на вашем компьютере должна быть установлена Windows 10 1903 или более поздняя версия.

Windows Terminal Preview


Windows Terminal Preview это сборка, в которой в первую очередь появляются новые функции. Эта сборка предназначена для тех, кто хочет видеть новейшие функции сразу после их выпуска. Эта сборка имеет ежемесячный цикл выпуска с новейшими функциями каждый месяц.

Windows Terminal


Терминал Windows это основная сборка продукта. Функции, которые поступают в Windows Terminal Preview, появляются в Windows Terminal через месяц эксплуатации. Это позволяет проводить обширное тестирование ошибок и стабилизацию новых функций. Эта сборка предназначена для тех, кто хочет получить функции после того, как они были изучены и протестированы сообществом Preview.

Первый запуск


После установки терминала вы можете запустить приложение и сразу приступить к работе с командной строкой. По умолчанию терминал включает профили Windows PowerShell, Command Prompt и Azure Cloud Shell в раскрывающемся списке. Если на вашем компьютере установлены дистрибутивы Подсистемы Windows для Linux (WSL), они также должны динамически заполняться как профили при первом запуске терминала.

Профили


Профили действуют как различные среды командной строки, которые вы можете настраивать внутри терминала. По умолчанию в каждом профиле используется отдельный исполняемый файл командной строки, однако вы можете создать столько профилей, сколько захотите, используя один и тот же исполняемый файл. Каждый профиль может иметь свои собственные настройки, которые помогут вам различать их и добавить в каждый свой собственный стиль.



Дефолтный профиль


При первом запуске Windows Terminal в качестве профиля по умолчанию устанавливается Windows PowerShell. Профиль по умолчанию это профиль, который всегда открывается при запуске терминала, и это профиль, который открывается при нажатии кнопки новой вкладки. Вы можете изменить профиль по умолчанию, установив defaultProfile на имя вашего предпочтительного профиля в файле settings.json.

"defaultProfile": "PowerShell"

Добавление нового профиля


Новые профили можно добавлять динамически с помощью терминала или вручную. Терминал Windows автоматически создаст профили для распределений PowerShell и WSL. Эти профили будут иметь свойство source, которое сообщает терминалу, где он может найти соответствующий исполняемый файл.

Если вы хотите создать новый профиль вручную, вам просто нужно сгенерировать новый guid, указать name и предоставить исполняемый файл для свойства commandline.

Примечание. Вы не сможете скопировать свойство source из динамически созданного профиля. Терминал просто проигнорирует этот профиль. Вам нужно будет заменить source на commandline и предоставить исполняемый файл, чтобы дублировать динамически созданный профиль.

Структура Settings.json


В Терминал Windows включены два файла настроек. Один из них defaults.json, который можно открыть, удерживая клавишу Alt и нажав кнопку Настройки в раскрывающемся списке. Это неизменяемый файл, который включает в себя все настройки по умолчанию, которые поставляются с терминалом. Второй файл settings.json, в котором вы можете применить все свои пользовательские настройки. Доступ к нему можно получить, нажав кнопку Настройки в раскрывающемся меню.

Файл settings.json разделен на четыре основных раздела. Первый это объект глобальных настроек, который находится в верхней части файла JSON внутри первого {. Примененные здесь настройки повлияют на все приложение.

Следующим основным разделом файла является объект profiles. Объект profiles разделен на два раздела: defaults и list. Вы можете применить настройки профиля к объекту defaults, и они будут применяться ко всем профилям в вашем list. list содержит каждый объект профиля, который представляет профили, описанные выше, и это элементы, которые появляются в раскрывающемся меню вашего терминала. Настройки, примененные к отдельным профилям в списке, имеют приоритет над настройками, примененными в разделе defaults.

Далее в файле расположен массив schemes. Здесь можно разместить собственные цветовые схемы. Отличный инструмент, который поможет вам создать свои собственные цветовые схемы, это terminal.sexy.

Наконец, в нижней части файла находится массив actions. Перечисленные здесь объекты добавляют действия в ваш терминал, которые можно вызывать с клавиатуры и/или находить внутри палитры команд.

Базовая кастомизация


Вот несколько основных настроек, которые помогут вам начать настройку вашего терминала.

Фон


Одна из самых популярных настроек настраиваемое фоновое изображение. Это настройка профиля, поэтому ее можно либо поместить внутри объекта defaults внутри объекта profiles, чтобы применить ко всем профилям, либо внутри определенного объекта профиля.

"backgroundImage": "C:\Users\admin\background.png"

Параметр backgroundImage принимает расположение файла изображения, которое вы хотите использовать в качестве фона вашего профиля. Допустимые типы файлов: .jpg, .png, .bmp, .tiff, .ico и .gif.



Цветовая схема


Список доступных цветовых схем можно найти на нашем сайте документации. Цветовые схемы применяются на уровне профиля, поэтому вы можете поместить настройку внутри значений по умолчанию или в конкретный объект профиля.

"colorScheme": "COLOR SCHEME NAME"

Этот параметр принимает название цветовой схемы. Вы также можете создать свою собственную цветовую схему и поместить ее в список schemes, а затем установить в настройках профиля имя этой новой схемы, чтобы применить ее.

Начертание шрифта


По умолчанию Windows Terminal использует Cascadia Mono в качестве шрифта. Начертание шрифта это настройка уровня профиля. Вы можете изменить шрифт, установив fontFace на имя шрифта, который вы хотите использовать.

"fontFace": "FONT NAME"`

Совет: Терминал Windows также поставляется с начертанием шрифта Cascadia Code, который включает программные лигатуры (см. Gif ниже). Если вы используете Powerline, Cascadia Code также поставляется в PL-версии, которую можно загрузить с GitHub.



Полезные ресурсы


Докуметация Windows Terminal
Скотт Хансельман: как сделать красивым Windows Terminal с помощью Powerline, шрифтов Nerd, кода Cascadia, WSL и oh-my-posh
Скотт Хансельман: Как настроить терминал с помощью Git Branch, Windows Terminal, PowerShell, + Cascadia Code!
Скотт Хансельман: Windows Terminal Feature PREVIEW Кастомизируйте свои привязки клавиш, цветовые схемы, панели, и многое другое!
>_TerminalSplash темы Windows Terminal
Подробнее..

Как приручить консоль, или 5 шагов к жизни с командной строкой

25.01.2021 16:17:27 | Автор: admin

Всем привет! Меня зовут Осип, я Android-разработчик в Redmadrobot и я люблю автоматизировать всё, что автоматизируется. В этом мне помогает консоль, поэтому решил поделиться опытом, как настроить командную оболочку так, чтобы в ней было приятно работать и она ежедневно помогала вам решать задачи.

Статья для тех, кто использует Linux или macOS. Если у вас Windows, вы можете использовать WSL (приравнивается к Ubuntu).

Есть задачи, которые проще выполнить в командном интерфейсе, а не в графическом, к примеру:

  • посчитать количество строк кода в проекте,

  • скопировать все файлы с расширением .png из одной папки в другую,

  • постучаться API и посмотреть какой ответ он выдаёт.

По опыту знаю: когда возникает одна из таких задач, разбираться, как это сделать в командной строке, если не используешь её это последнее, чего хочется. Кажется, что нет смысла залезать в неё ради одной задачи. Проще найти плагин или утилиту.

Поверьте, стоит один раз настроить командную оболочку и разобраться, как в ней работать, и рабочее окружение начнёт помогать вам как хорошая IDE.

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


Статья только началась, а по тексту уже встречались и командная строка, и командная оболочка. Чем отличаются консоль, терминал, командная оболочка и командная строка?

Если объяснять из центра наружу: командная строка строка, где пользователь пишет команды; командная оболочка программа, которая интерпретирует команды, введённые в командную строку и выводит результат.

Термины консоль и терминал в статье используются как синонимы и обозначают программу для ввода и вывода информации. В терминале запускается командная оболочка.

Подробнее о различиях можно почитать на Ask Ubuntu: What is the difference between Terminal, Console, Shell, and Command Line?

В статье будут встречаться примеры команд. Если по ходу прочтения вы не понимаете, что делает консольная команда, скопируйте её и вставьте в ExplainShell. Благо Роскомнадзор перестал его блокировать после разблокировки Telegram.

Зачем вообще использовать командную строку

Дисклеймер: Я не призываю использовать только командную строку и отказаться от графического интерфейса. Нужно просто учитывать командную строку при выборе инструмента для решения задачи.

Когда хотят рассказать чем же хорош CLI, выделяют разные преимущества перед GUI:

  • Доступность. Командная строка доступна везде. Внутри Android Studio есть вкладка с командной строкой. Можно и вовсе настроить drop-down терминал (ещё его называют quake style), который будет появляться поверх всех приложений по нажатию сочетания клавиш.

  • Многофункциональность. Одна точка доступа к любым утилитам.

  • Простота. Простой командный интерфейс может заменить сложный графический интерфейс. Сделать графический интерфейс который позволит ввести, например, пять параметров может быть довольно нетривиальной задачей, а сделать то же самое с командным интерфейсом просто.

  • Легковесность. Как правило, CLI утилиты используют меньше ресурсов.

Меня как разработчика больше всего впечатляет, как можно комбинировать CLI утилиты. Текст интерфейс общения, который понятен для всех утилит с командным интерфейсом. Утилиты принимают на вход текст и возвращают тоже текст. Это один из принципов Unix, которые сформулировал Дуглас Макилрой в 1978 году:

Пишите программы, которые делают одну вещь и делают её хорошо.


Пишите программы, которые бы работали вместе.


Пишите программы, которые поддерживают текстовые потоки, потому что это универсальный интерфейс.

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

Примеры из жизни

Я задал вопрос коллегам-роботам: Для чего вы чаще всего открываете терминал? Получился такой ТОП-5:

  1. Работа с Git там, где не хватает графического интерфейса.

  2. Установка пакетов и управление зависимостями (подробнее про менеджер пакетов поговорим в разделе Устанавливаем менеджер пакетов).

  3. Работа с SSH.

  4. Проверка API с помощью curl.

  5. Когда нужно грохнуть процесс.

Есть и менее очевидные применения:

  1. Скачать видео из YouTube поможет youtube-dl. Качаете подкаст и нужна только аудио-дорожка? Добавьте к команде флаг --audio. Хотите скачать весь плейлист или даже весь канал? Подставляйте ссылку на канал и готовьте побольше свободного места.

  2. Хотите посмотреть отличия между файлами? Выполните команду diff и укажите пути до файлов, которые надо сравнить.

Шаг 1: Открываем терминал

Не терминал, а эмулятор терминала. (c) Департамент зануд

Обычно в ОС уже есть какой-то терминал, и для начала можно использовать его. Если хочется больше функций и возможностей кастомизации, то есть из чего выбирать. Некоторые терминалы позволяют открывать вкладки, разделять экран и даже рендерить картинки. Можно найти на любой вкус, например, стилизованный под старый ламповый монитор.

cool-retro-terminalcool-retro-terminal

Выбор терминала это тема для отдельной статьи. Кратко: если у вас Linux, начните с этого списка. На macOS популярен iTerm2, но я его не использовал, поэтому не могу ни поругать, ни похвалить.

Для меня важно чтобы и на компьютере с Linux, и на рабочем ноутбуке с macOS был один и тот же терминал с одинаковыми настройками. Я выбирал среди кроссплатформенных и остановился на kitty.

Шаг 2: Устанавливаем менеджер пакетов

Мы оказались в терминале. Первое, с чем нужно разобраться, менеджер пакетов.

Я встречал людей, которые уверены, что, для того чтобы установить программу из консоли, нужно непременно собирать её из исходников и вообще это сложно. Это не так. Программы в собранном виде хранятся в репозиториях. Пакетные менеджеры устанавливают программы из репозиториев и отвечают за их обновление и удаление.

Это даже проще, чем искать надёжный источник, из которого можно скачать программу, и удобнее, чем магазины приложений в macOS или Windows, где зачастую нет нужных программ.

Менеджеры пакетов в Linux

В дистрибутивах Linux менеджер пакетов есть по умолчанию. В Ubuntu, Debian и Mint это apt-get, а в Manjaro и ArchLinux pacman.

Чтобы установить пакет достаточно в терминале написать apt-get install [package]. Если у вас pacman, то pacman -S [package]. Может понадобиться sudo в начале, чтобы выполнить команду с правами root.

Чтобы обновить все пакеты с помощью apt-get введите команду apt-get update && apt-get upgrade. В pacman pacman -Syu.

В pacman много флагов и сложно сразу запомнить нужные. Ещё одно неудобство он не поддерживает установку пакетов из репозитория AUR. Это репозиторий, в который могут загружать пакеты любые пользователи. Исправить минусы помогут утилиты, которые упрощают работу с pacman. Рекомендую попробовать yay.

Менеджеры пакетов в macOS

В macOS придется установить пакетный менеджер. Самые популярные Homebrew и MacPorts. Homebrew активнее поддерживается сообществом, а пакеты в нём обновляются чаще, поэтому лучше использовать его. Для установки скопируйте актуальную команду установки c официального сайта. Эта команда скачает скрипт установки и запустит его.

Может понадобиться установка XCode Command Line Tools. Это базовый набор консольных инструментов clang, git, make и других. Он не зависит от XCode, а называется так, потому что необходим XCode для компиляции.

Теперь, чтобы установить пакет, достаточно в терминале написать brew install [package].

Обновляются все пакеты одной командой brew upgrade. Если brew отказывается работать, напишите brew doctor , и brew подскажет, что с ним не так, а также как это исправить.

Шаг 3: Устанавливаем командную оболочку

Чтобы было проще работать, командная оболочка должна вам помогать. В ней должен быть поиск по введённым ранее командам, а ещё должно работать автодополнение. Здорово, если будут подсказки, которые учитывают, какие команды вы используете чаще других. Не помешает подсветка синтаксиса и ошибок при вводе команд.

Все примеры в статье будут сделаны с использованием командной оболочки fish. Эта командная оболочка проста для освоения и в ней все нужные функции работают из коробки.

Чтобы узнать, какая оболочка используется по умолчанию у вас, выполните команду echo $SHELL. Скорее всего, команда выведет /env/bash или /env/zsh это самые популярные оболочки. Если хотите сравнить возможности bash, zsh и fish, посмотрите эту таблицу.

Установим fish c помощью менеджера пакетов:

# Если pacmansudo pacman -S fish# Если apt-getsudo apt-get install fish# Если brewbrew install fish

В статье будут упоминаться и другие программы, все они устанавливаются таким же образом. Команды установки написаны на страницах программ, чтобы не повторяться, не буду писать их для каждой утилиты.

Fish установлен. Запускаем его командой fish:

osip@homepc ~ % fishWelcome to fish, the friendly interactive shellType `help` for instructions on how to use fishosip@homepc ~>

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

Fish по умолчанию

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

  1. Назначить fish командной оболочкой по умолчанию.

    Нужно учитывать, что скрипты инициализации текущей командной оболочки не будут выполняться. Команды и переменные окружения из .bashrc, .bash_profile, .zshrc и т.д, нужно переместить в .config/fish/fish.config , а затем адаптировать под синтаксис fish.

  2. Использовать fish только как интерактивную оболочку.

    Это более безболезненный способ, потому что не нужно мигрировать скрипты и переменные окружения. В конце скрипта инициализации текущей командной оболочки нужно запустить fish. Добавьте строку exec fish в файл .bash_profile, если у вас bash или в .zshrc, если zsh. Эти файлы находятся в корневой директории пользователя.

    На ArchWIki есть более подробное описание этого и еще нескольких способов.

Поиск по истории

Давайте-ка посмотрим, что умеет fish. Если еще не установили, можно попробовать в браузере. Я изменил только цвета и prompt, больше ничего не настраивал.

Когда вы начинаете набирать команду, fish подсказывает команды и аргументы, которые вы использовали раньше. Чтобы применить подсказку нажмите . Подставить одно слово из подсказки Ctrl+.

sample_command это демонстрационная функция. Она принимает любые параметры и ничего не возвращает.

Чтобы ввести предыдущую команду, нажмите клавишу . С помощью клавиш и можно перемещаться по истории команд. Если командная строка пуста, команды будут листаться по очереди. Если в командной строке есть текст, то история будет фильтроваться по нему.

Автодополнение

Начните писать любую команду и нажмите Tab, не дописывая её до конца. Попробуйте с командой git config:

Дополнение работает с командами, подкомандами и аргументами. Вместе с вариантами дополнения выводится описание подкоманд из документации.

Если утилита не поддерживает автодополнение, fish умеет создавать дополнения из документации man. Для этого нужно выполнить команду fish_update_completions.

А что с путями? Например, хотим перейти в папку dev/tools/jarjar/:

Дополнение путей тоже работает на Tab. Для перехода по пути не обязательно писать команду cd в начале. А еще работает дополнение, если написать первую букву имени каждой папки в пути. Если указан несуществующий путь, он подсвечивается красным.

Сложно запомнить все нужные флаги у команд. Хочу вывести дерево файлов, но не помню, как ограничить его глубину и сделать так, чтобы вывод был цветным. Для такого случая есть Shift+Tab дополнение с поиском:

Автодополнение может сработать в самых неожиданных местах, например, так работает автодополнение для команды kill:

Убийство Android Studio на глазах у studentdУбийство Android Studio на глазах у studentd

Wildcards

В fish, как и в bash, есть поддержка wildcards. Wildcards позволяют выполнить команду для нескольких файлов.

Выводим все файлы с расширением .md в текущей папкеВыводим все файлы с расширением .md в текущей папке

* соответствует любой строке
** соответствует любой иерархии папок, то есть рекурсивно заходит во вложенные папки

Применим wildcard, чтобы скопировать все файлы apk после сборки в папку output:

  • cp build/*.apk output/ скопирует все apk из папки build.

  • cp build/**.apk output/ скопирует все apk из папки build и из всех вложенных папок. То, что надо.

Функции, алиасы и аббревиатуры

Большиство команд fish это функции. Можно писать и свои функции. Синтаксис такой:

funcion [название]    [тело функции]end

Если функцию нужно сохранить для будущих сессий, не забудьте выполнить команду funcsave [название] после того как напишете функцию и проверите, что она корректно работает.

Для часто используемых команд можно создать более короткие синонимы алиасы. В fish команда alias создаёт однострочную функцию.

Как выглядит alias?

Флаг --save указывает, что нужно автоматически вызвать команду funcsave после создания алиаса. Таким образом алиасы сохранятся для будущих сессий.

Другой вариант сокращения команд аббревиатуры. Они настраиваются командой abbr или в fish_config во вкладке Abbreviations.

Удобно создавать аббревиатуру одновременно в английской и русской раскладках (см. скриншот). Такая аббревиатура выполнится, даже если вы забыли переключить раскладку клавиатуры.

Аббревиатуры подставляются на лету, когда вы нажимаете Space или Enter. В отличие от алиасов, аббревиатуры не являются функциями.

И па и gf превращается в git fetchИ па и gf превращается в git fetch

Шаг 4: Изучаем арсенал

Командная оболочка есть, теперь нужны команды.

Консольные утилиты могут быть с CLI и TUI. Command Line Interface (CLI) программа принимает команды через командную строку. Так работает большинство утилит. Text User Interface (TUI) интерфейс рисуется псевдографикой и по нему можно кликать мышкой как по GUI.

TUI для SpotifyTUI для Spotify

CLI не нужно бояться. На замену старым утилитам появляются новые с улучшенным UX, ориентированные на человека, а не на скрипты.

Например, многие старые утилиты, которые выводят размер файла, по умолчанию выводят его в байтах. А утилита df вообще выводит свободное место на диске в количестве блоков по 512 байт.

Чтобы выводились понятные человеку размерности, нужно добавить флаг -h (human readable). Цветной вывод удобнее читать, но он тоже по умолчанию обычно отключен и включается добавлением флага, чаще всего -C. В современных же утилитах по умолчанию включен цветной человекопонятный вывод.

Стандартные команды

Чтобы пользоваться командной строкой, нужно знать несколько стандартных команд:

  • cd [сhange directory] команда для навигации по файловой системе. Если запустить её без аргументов, вы окажетесь в домашней папке;

  • cp [copy], mv [move], rm [remove] команды для копирования, перемещения и удаления файлов, соответственно;

  • mkdir [make directory] команда для создания папки;

  • echo выводит строку, которую ей передали.

Если команда долго работает и вы не хотите дожидаться её завершения, прервите её выполнение сочетанием клавиш Ctrl + C.

Помощь: man, help, tldr

Есть несколько способов получить справку по команде.

man выводит полную справку:

  • описание команды,

  • список аргументов и описание каждого из них,

  • какие переменные окружения использует утилита и для чего,

  • известные баги,

  • советы и примеры использования,

  • другая информация, которую посчитал полезной разработчик.

Если ввести man man, вы получите справку по команде man, где всё это подробно описано.

man это утилита с TUI, в ней есть горячие клавиши. Для поиска нажмите /, а для выхода q. / и q стандартные клавиши для поиска и выхода, они работают во многих TUI утилитах. Ещё один стандартная клавиша ?, она открывает справку.

Можно выполнить команду из man для этого нажмите ! и введите команду. Хотите открыть man для другой команды внутри man или сразу попробовать выполнить команду, следуя документации? Легко.

Страницы в man пишут разработчики утилит. Если разработчик не написал справку, man выдаст No manual entry for [command]. Но даже если нет страницы в man можно вывести краткую справку с помощью флага --help. Попробуйте написать man --help.

Для команд fish можно открыть справку в браузере командой help <command>.

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

tldr tldrtldr tldr

Объединяем команды

Помните, в самом начале я говорил, что консольные команды как кирпичики и их можно объединять, чтобы решить более сложную задачу? Сейчас мы этим и займёмся.

Чтобы направить вывод одной команды на вход другой, используется оператор |. Он называется pipe, а на русский его переводят как конвейер. Если мы хотим подать вывод команды find_bone на вход команде eat, нужно между этими командами поставить трубу (pipe):

$ find_bone | eat

Синтаксис понятен, но таких команд не существует и запустить их у себя в консоли не получится. Давайте посмотрим на более реальный пример:

$ echo -e "spot\\nhandle\\npick\\natlas" > robots.txt$ cat robots.txt | sortatlashandlepickspot

Оператор | нам уже знаком, но что делает >? Этот оператор направляет вывод команды в файл. После этого командой cat мы достаём содержимое файла и с помощью оператора | отдаём на сортировку.

Современные утилиты

Просмотр списка файлов: ls, tree exa

Для просмотра списка файлов в папке обычно используют стандартную команду ls, а для просмотра иерархии папках tree. Обе эти утилиты можно заменить более дружелюбной утилитой exa.

В exa по умолчанию включен цветной вывод и человекопонятные размеры файлов, есть поддержка широких экранов, отображается статус файлов в Git, если использовать команду в репозитории. Подробнее обо всех фичах читайте на сайте утилиты.

Скриншоты. Сравнение ls, tree и exa.
Сравнение вывода ls и exaСравнение вывода ls и exaСравнение вывода tree и exaСравнение вывода tree и exa

Бонус: В exa можно совместить два режима вывода.

Просмотр запущенных процессов: top htop

top и htop. Обе утилиты выводят список запущенных процессов, но htop делает это гораздо приятнее.

В htop можно сортировать процессы по разным столбцам, нажимая на них. Кнопки в нижнем меню тоже нажимаются. Столбцы и информационные блоки в шапке настраиваются можно убрать лишнее и добавить нужное.

А как выглядит top?

Работа с JSON: jq

jq незаменимая утилита, если вы работаете с JSON. Проще показать на примерах что умеет делать jq.

Валидируем json:

$ echo '{"model": spot}' | jq typeparse error: Invalid numeric literal at line 1, column 15$ echo '{"model": "spot"}' | jq type"object"

Форматируем json:

$ echo '{"model":"spot"," type":"robodog"}' | jq{  "model": "spot",  "type": "robodog"}

Выкусываем из json'а только то, что нужно:

$ set json '[{"model": "spot", "type": "robodog"}, {"model": "atlas", "type": "humanoid"}]'$ echo $json | jq 'map(.model)' --compact-output["spot","atlas"]$ echo $json | jq .[0].model"spot"# А теперь пример посложнее$ echo $json | jq 'map({(.model): .type}) | add'{  "spot": "robodog",  "atlas": "humanoid"}

Это только малая часть возможностей. Все возможности смотрите в доке.

Другие утилиты

Чтобы этот раздел не раздулся, остальные утилиты я спрятал под спойлеры. При первом прочтении их можно пропустить и позже использовать этот раздел как справочник.

Консольный HTTP клиент: curl, wget httpie

httpie делает то же что curl отправляет запросы в сеть. Но посмотрите как отличается синтаксис и оформление вывода в curl и httpie.

На фотографии слева направо: curl и httpieНа фотографии слева направо: curl и httpie
Отображение содержимого файла: cat bat

cat и bat выводят содержимое файла, но bat подсвечивает синтаксис и отображает изменения из гита.

Поиск по содержимому файлов: grep ripgrep

ripgrep более быстрая версия grep. Сравнение скорости работы показывает, что ripgrep быстрее всех :)

Поиск файлов: find fd, fzf

Для поиска файлов можно использовать стандартную утилиту find. Для неё есть альтернатива fd. Она работает быстрее, поддерживает цветной вывод, по умолчанию игнорирует скрытые файлы и файлы из .gitignore. Посмотрите на гифку, которая демонстрирует работу fd.

Ещё одна утилита для поиска fzf [fuzzy finder]. Это утилита с TUI для интерактивного поиска файлов с использованием нечёткого поиска по названиям.

Ещё из приятного есть предпросмотр содержимого.

Подсчёт количества строк кода: wc tokei

Стандартная утилита wc [word count] считает количество слов, символов и строк в файлах, но чтобы с помощью неё посчитать количество строк кода в проекте, придётся написать что-то такое:

$ fd -g '*.kt' | xargs wc -l

У такой команды есть сразу несколько недостатков:

  • считаются все строки, включая комментарии и пустые строки,

  • ищутся только файлы с расширением .kt, для поиска других придётся менять команду,

  • сгенерированные файлы и остальные файлы, которые заигнорены в гите, тоже попадут в статистику,

  • такую команду долго писать.

Утилита tokei лишена перечисленных недостатков. Вот пример вывода tokei на одном из наших проектов:

Упс, файлы proguard засчитались в пользу PrologУпс, файлы proguard засчитались в пользу Prolog
Свободное место на диске: du ncdu

Ещё один пример разницы CLI и TUI. В отличие от du, ncdu это TUI. Тут же можно перейти внутрь папки или удалить ненужное нажав d.

Хм, накопилось много врапперов и кэшей Gradle. Можно почистить.Хм, накопилось много врапперов и кэшей Gradle. Можно почистить.
Сравнение файлов: diff delta

Отличная замена старому-доброму diff - delta. Можно использовать режим отображения side-by-side, если больше нравится, включить отображение номеров строк. Даже без дополнительных настроек диффы выглядят приятно:

Измерение времени работы программы: time hyperfine

Не верьте на слово, если я говорю, что одна утилита работает быстрее другой. Лучше проверьте.

Можно измерить время выполнения команды с помощью time (в macOS gtime). Эта утилита не предназначена для бенчмарков нет возможности прогрева, команда выполняется один раз. hyperfine подойдёт лучше, потому что изначально разработан для бенчмарков.

Попробуем замерить время выполнения команды tree:

Вывод команды tree перенаправлен в пустоту (/dev/null), потому что здесь не важен вывод команды, важно только время её выполнения. С hyperfine этого делать не нужно, он сам отбрасывает вывод команды.

Hyperfine умеет отслеживать признаки неправильного бенчмарка. Вот некоторые подсказки:

  • если во время замера есть выбросы, hyperfine посоветует закрыть другие программы, потому что они могут влиять на бенчмарк,

  • если первый запуск команды дольше остальных, hyperfine посоветует сделать прогрев, задав параметр --warmup N. Перед бенчмарком программа выполнится N раз.

Можно сравнивать скорость выполнения нескольких команд. Для этого нужно написать команды друг за другом, заключив их в кавычки:

$ hyperfine 'command_one' 'command_two' 'command_three'

Шаг 5: Сохраняем настройки

Чтобы не настраивать каждый раз все программы заново, нужно где-то сохранять настройки.

Конфиги это файлы. Обычно они хранятся в корневой директории пользователя вместе со скриптами инициализации командной оболочки, например, в папке .config/. Если вы установили fish, то найдёте папку .config/fish/ и в ней файлы с настройками. Самый простой способ сохранить конфиги сохранить их в Git-репозиторий.

Имена файлов и папок с настройками обычно начинаются с точки, поэтому одним словом их называют dotfiles. На момент написания статьи на GitHub опубликовано 138 425 репозиториев с именем dotfiles есть куда подсмотреть.

На странице awesome-dotfiles вы найдёте много информации про dotfiles. Там же есть ссылки на инструменты, которые помогают управлять dotfiles.

Я использую yadm. Мне важна кроссплатформенность, поэтому пригождается его возможность создавать альтернативные версии файлов для разных ОС.

Заключение

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

Чтобы не настраивать рабочее окружение на каждом новом компьютере заново, сохраняйте конфиги в Git-репозиторий. Но не забывайте о безопасности следите, чтобы в репозиторий не попал ключи и пароли.

На замену старым утилитам приходят новые, нацеленные на пользователя, а не на скрипты. Используйте их и ищите новые! Если вам кажется, что утилита слишком сложная, возможно, вы такой не один и кто-то уже написал более простой в использовании аналог.

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

Если будут вопросы или вам понадобится помощь с освоением консоли, пишите мне в Telegram@osipxd. Ещё я иногда пишу в канал @rareilly заметки про Android и вообще про всё интересное, что нахожу. Спасибо за внимание!

Что ещё почитать

Подробнее..

Линукс-порт Far Manager прогресс за 4 года

20.10.2020 22:18:35 | Автор: admin
Первая публикация исходников far2l, порта Far Manager под Линукс штуки, которая когда-то считалась принципиально невозможной состоялась 10 августа 2016го. Поскольку главный разработчик поговаривает о переходе проекта в статус беты, решил написать обзорный пост, как там идут дела и чего удалось добиться за прошедшие четыре года.

Консольная версия


Первый же вопрос, который все задавали и здесь, и на опеннете, и на лоре а чего не в консоли? Изначально far2l действительно работал только в графическом режиме, через wxWidgets. Это оказалось самым простым способом быстро получить работающий порт со всеми пользовательскими удобствами: иксовым буфером обмена и всеми сочетаниями клавиш, причем с поддержкой событий не только KeyDown, но и KeyUp.

Сейчас это ограничение в прошлом: far2l прекрасно себя чувствует в консоли. Более того, там появились так называемые расширения терминала far2l, поэтому если запускать консольный far2l внутри графического (например, зайдя куда-нибудь по ssh), они сконнектятсямежду собой, и внутренний far2l тоже будет видеть и буфер обмена (с разрешения пользователя, конечно), и все возможные горячие клавиши. Более того, сделана даже специальная сборка putty, позволяющая наслаждаться всеми этими фишками из Windows.

Вот, смотрите, это far2l в GNOME Terminal


А вот в putty


Русские буквы в .zip'ах с винды


Вы не поверите, но Windows, вплоть, как минимум, до семерки создавала .zip архивы, записывая туда имена файлов в OEM (DOS) кодировке! Совместимость страшная штука. В итоге StackOverflow переполнен вопросами как мне правильно распаковать зип с кракозябрами.

В far2l мы это починили. В процессе родилась демонстрационная утилитка, которая показывает, как правильно работать с кодировками в .zip'ах, чтобы кракозябров не было (пригодится авторам архиваторов), а также патчик к p7zip, применяющий аналогичный алгоритм. p7zip-с-патчиком даже запакован в .deb'ку (ubuntu 20.04+, amd64), установка которой чинит поддержку зипов, например, в Engrampa, используемом в MATE.



Приведение в порядок зависимостей


Изначально порт нёс в себе кучу кода библиотек, и так присутствующих в мире Linux в системных пакетах: pcre, minizip, universal charset detector. Сейчас всё это заменено на грамотное использование зависимостей: системные библиотеки используются везде, где это возможно (исключение: 7z и unrar, там статически компилируются самые свежие библиотеки для поддержки самых свежих особенностей форматов).

Человеческая поддержка .tar.gz


Плагин multiarc, который используется в far2l для доступа к архивам, приехал к нам из мира Windows, и не умел воспринимать .tar.gz как один архив. Он видел .gz, и внутри него .tar. Следовательно, чтобы получить список файлов, приходилось делать полную распаковку. Такой себе экспириенс. Сейчас перешли на libarchive, и эта проблема исчезла.

Красивое консольное окно


В графической версии far2l была проблема: символы рисования рамок показывались раздражающим пунктиром.

image

У нас долго не получалось найти причину или подобрать настройки рендеринга, исправляющие этот бесячий баг, пока я не заглянул в исходники терминалок из GNOME и KDE. А там, оказывается, давным-давно символы рисования рамок рендерятся не из шрифта, а вручную, чтобы линии четкие и красивые получались.

В итоге сделали так же в far2l, только немножко лучше: добавив сглаживание. А то в терминалах KDE и GNOME максимально контрастные линии слишком сильно отвлекают на себя внимание рядом со сглаженным текстом.

В процессе пришлось разобраться в психовизуальных нюансах антиалиасинга, отлавливая несуществующие слишком яркие пиксели, которые упорно видели глаза. Подробности.

image

Пакеты для дистрибутивов


В репозитории дистрибутивов мы пока не попали. Зато давно есть пакеты во всех основных форматах. Даже скрипт сборки для Amazon Linux есть! Ну и конечно есть ppa для *buntu самый удобный способ просто поставить фар для большинства пользователей.

Свежий Colorer


far2l форкнулся от ветки Far 2, так что некоторые плагины там требовали срочного обновления. Недавно такое обновление было проведено: обновили код распаковки 7z и unrar, а также схемы цветовой подсветки синтаксиса Colorer. Остальные портированные плагины не содержат каких-то регулярно обновляющихся штук, а вот старые раскраски и не открывающиеся новые архивы были реальной проблемой. Всё, её больше нет!



NetBoxRocks


Это был второй вопрос, который обычно задавали в каментах: а нетбоооокс буууудеееет?. Нет, нетбокса не будет! Там putty внутри, и тащить в far2l её linux-версию показалось странной затеей. Да и сам код netbox'а не слишком располагал к портированию.

Поэтому автор порта, великолепный elfmz, сделал свою версию сетевого плагина: NetRocks. Там есть всё, что только может понадобиться и ftp[s], и scp, и sftp, и nfs, и webdav, и даже samba! Всё работает на нативных линуксовых библиотеках. В sftp можно даже удаленные команды запускать и в удаленную консоль ходить.

А ещё в NetRocks есть псевдо-сетевой плагин file, который позволяет работать с локальной файловой системой. Зачем? Потому что NetRocks умеет в фоновые операции. А обычное копирование файлов Far не умеет.



А как там вообще с плагинами?


Помимо NetRocks есть в комплекте есть:
colorer (подсветка синтаксиса, свежий!)
multiarc (работа с архивами, доработанный, свежие архиваторы!)
tmppanel (временная панель)
align block (форматирование блоков для редактора)
autowrap (автоперенос слов в редакторе)
drawline (рисование линий в редакторе)
editcase (конвертация регистра в редакторе)
SimpleIndent (работа с отступами в редакторе)
compare (продвинутая версия сравнения папок)
editor autocomplete (автодополнение в редакторе)
filecase (конвертация регистра имен файлов)
incremental search (быстрый поиск в редакторе)
inside (показывает, что внутри ELF и некоторых других форматов)
и даже плагин для написания других плагинов на Python!

Есть парочка сторонних, far2-gvfs и far2l-fuse, но после появления NetRocks они в некоторой степени утратили актуальность.

В общем, базовый набор для комфортной работы с локальными и удаленными файлами и архивами, а также написания кода прямо в редакторе far2l имеется :)

А со стабильностью как? Когда релиз уже?


Со стабильностью всё хорошо: за 4 года использования в работе (webdev + всякое офисное) ни одной потери данных с far2l я не поймал. Автор порта готов понемножку менять статус с альфы на бету, если в течении месяца-двух не вылезет критических ошибок. Тогда можно будет подумать об отправке пакетов в репозитории дистрибутивов, например.

Bonus #1. А там правда Wine под капотом?


Отчасти :) Из Wine были вытащены некоторые кусочки трансляции WinApi в нативный API Linux (конвертация кодировок, например; к слову, этот кусок недавно попробовали переписать на iconv, но выяснилось, что код из Wine делает то же самое в 4 раза быстрее). Со временем обращения к этим функциям, разбросанные по всему коду far2l, можно будет понемножку заменять на прямые вызовы нативных функций. А пока и эта конструкция работает очень даже быстро (самое медленное, на чём пробовали запускать raspbery pi, полет нормальный) и вполне надежно.

Bonus #2. Хватит фигней страдать, консоль учите, дурни!


За время работы над far2l (тестировщиком, а ещё иногда нехитрые патчи шлю) я узнал о линуксовой консоли больше, чем за всю жизнь до этого :) А ещё понемногу осваиваю си, на которых кодить со времен института не приходилось повода не было (заодно, кстати, перешел на ты с git). Так что ждите скоро статью как веб-макака си на опен сорсе учила!

Bonus #3. А нескучные обои имеются?


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



Bonus #4. А на маке взлетит?


Взлетит. Половину тикетов в багтрекер с маков пишут. И да, на BSD работает тоже.
Подробнее..

Error success и что делать по этому поводу

04.12.2020 12:06:11 | Автор: admin

image


Всем начинающим программистам всегда рассказывают о важности правильного формирования сообщения об ошибках. Всегда говорят о том, что если программа что-то не смогла сделать, то она должна ясно и недвусмысленно рассказать почему это произошло. Рассказывают о важности контроля возвращаемого значения вызываемых функций. При этом даже компиляторы научились выдавать предупреждения если возвращаемое определенными функциями значение игнорируется. Надо сказать что важность обработки ошибок современными программистами принята. Временами это приводит к занятным казусам, как на КДПВ (взята здесь). Мне в реальной жизни несколько раз приходилось сталкиваться с подобными странными диагностическими сообщениями. О последнем случае и методах преодоления такой вот диагностики я и хочу рассказать. Если стало интересно милости прошу под кат. Опытные программисты наверняка не откроют для себя ничего нового, но пофилософствовать о разработке ПО точно смогут.


Для тех, кому букв много
  • правильно обрабатывать возвращаемые функцией read() значения важно
  • не забываем проверять актуальность используемого софта (а OpenSource особенно)
  • многофункциональные программы-комбайны в большинстве случаев одинаково неудобны для решения любой из допустимых задач. С другой стороны плодить москитный флот не лучшая идея

А вообще у меня грустная новость. Не будет больше никаких картинок. Мы опустимся на уровень системной консоли Linux и будем жить там. При этом будем радоваться. Ибо проект, с которым предстоит работать достаточно известный загрузчик U-Boot. Проект с открытым исходным кодом, поддерживаемый компанией DENX Software Engineering. Потому порадуемся тому, что у нас есть консоль, есть системное окружение и вообще жизнь кипит. Потому как при работе с этим проектом, как правило, нет ничего подобного сплошные области памяти, пересылки байт из одного места в другое да ожидание готовности периферии. Но, к частью, эта часть уже выполнена и есть вполне себе рабочий загрузчик для железки. Пора заняться украшениями, которые позволят прикладным программистам как-то влиять на процесс загрузки системы. Ничто не предвещает проблем. Задача давно решена и активно используется таким популярным проектами как OpenWRT и множеством других, чуть менее известных.


Суть очень проста. U-Boot корректирует свое поведение в зависимости от переменных среды. Переменные среды между перезагрузками могут быть сохранены в энергонезависимой памяти. Утилиты командной строки fw_printenv и fw_setenv позволяют соответственно выводить и менять их значение прямо из Linux. Все. В принципе большего и не требуется. Как всегда инструкцию мы будем читать когда дым рассеется. Да и откуда здесь взяться дыму? Дым весь был выпущен когда загрузчик под эту плату адаптировали. Потому смело набираем команду fw_printenv, потому как она-то точно ничего сломать не может.


localhost ~ # fw_printenvCannot open /dev/mtd1: No such file or directorylocalhost ~ # fw_printenv --helpUsage: fw_printenv [OPTIONS]... [VARIABLE]...Print variables from U-Boot environment -h, --help           print this help. -v, --version        display version -c, --config         configuration file, default:/etc/fw_env.config -n, --noheader       do not repeat variable name in output -l, --lock           lock node, default:/var/lock

Ну ожидаемо. Конечно. Мы не указали где именно хранятся переменные среды. А быстрая помощь однозначно говорит о том, что указать в командной строке и не получится. Надо править конфигурационный файл /etc/fw_env.config. Формат файла довольно простой и интуитивно понятный. Для того, чтоб не создавать самому себе (и окружающим) трудностей я разместил переменные среды U-Boot в самом доступном месте, которое только можно себе придумать. Конкретно в файле uboot.env первого раздела основного накопителя, отформатированного реально переносимой файловой системой vfat (она же FAT-32). И проверил. Из консоли U-Boot переменные сохраняются в файл, при старте из него читаются. Красота. Осталось только дать возможность их править из Linux. Раздел c файлом uboot.env, а еще ядром, файлом дерева устройств, и некоторым дополнительным наполнением критичным для работы системы совершенно логично монтируется к /boot. Потому совершенно не сомневаясь комментирую строчки 11 и 12 (/dev/mtd1 и /dev/mdt2 соответственно) и убираю комментарий со строчки 30 (/boot/uboot.env) в конфигурационном файле.


# VFAT example/boot/uboot.env 0x0000          0x4000

Все. Вроде все подготовительные операции выполнены. Дубль два.


localhost ~ # fw_printenvRead error on /boot/uboot.env: Success

Ну здравствуй, КДПВ. Первая разумная мысль, которая посещает любого Linuxоидника в такой ситуации а что с правами. Впрочем, наш лозунг Слабоумие и отвага мы работаем от rootа. Логично. Чего бояться человеку, который для железки делает загрузчик и имеет самый что ни на есть физический (с паяльником) доступ к плате? А может файла просто нет? Забыл в консоли U-Bootа сказать saveenv? Проверим


localhost ~ # ls -l /boot/uboot.env-rwxr-xr-x 1 root root 8192 Dec  2 13:22 /boot/uboot.env

Нет, есть он. И даже читаться может всем миром (ай, как не хорошо). Интересно, а если его не будет?


localhost ~ # mv /boot/uboot.env /boot/uboot.env.baklocalhost ~ # fw_printenvCannot open /boot/uboot.env: No such file or directorylocalhost ~ # mv /boot/uboot.env.bak /boot/uboot.env

Логично. Тут все правильно. Ладно, тяжело вздохнули и Это наш кактус, нам его и грызть. Хорошо хоть исходники есть. Надо глянуть, что там у нас происходит. Может мысли какие появятся? Очень быстро находим строку 950 в файле tools/env/fw_env.c:


        lseek(fd, blockstart + block_seek, SEEK_SET);        rc = read(fd, buf + processed, readlen);        if (rc == -1) {            fprintf(stderr, "Read error on %s: %s\n",                DEVNAME(dev), strerror(errno));            return -1;        }        if (rc != readlen) {            fprintf(stderr,                "Read error on %s: Attempted to read %zd bytes but got %d\n",                DEVNAME(dev), readlen, rc);            return -1;        }

Нет. Тут вполне себе классическая обертка над функцией read(). Практически прямиком из учебника. И, судя по поведению итоговой программы не остается сомнений в том, что read() возвращает -1, но при этом errno остается нулевым. Подождите. Что это за скрежет раздается? А, это мозги шевелиться начали Хорошо


Ну что, можно почитать мануал на read? Не, ерунда Уж вроде про read-то все читано-перечитано. Все мыслимые и немыслимые варианты ошибок с функцией read() давно известны. Не должно быть такого. Что делаем дальше? Правильно, раз исходники не дают ответ пусть его даст сама система.


localhost ~ # strace fw_printenvexecve("/usr/bin/fw_printenv", ["fw_printenv"], 0x7ebf2400 /* 28 vars */) = 0brk(NULL)                               = 0x2118000uname({sysname="Linux", nodename="localhost", ...}) = 0access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3fstat64(3, {st_mode=S_IFREG|0644, st_size=42265, ...}) = 0mmap2(NULL, 42265, PROT_READ, MAP_PRIVATE, 3, 0) = 0x76f14000close(3)                                = 0openat(AT_FDCWD, "/lib/libc.so.6", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\f~\1\0004\0\0\0"..., 512) = 512fstat64(3, {st_mode=S_IFREG|0755, st_size=1286448, ...}) = 0mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x76f12000mmap2(NULL, 1356160, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x76da1000mprotect(0x76ed7000, 65536, PROT_NONE)  = 0mmap2(0x76ee7000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x136000) = 0x76ee7000mmap2(0x76eea000, 8576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x76eea000close(3)                                = 0set_tls(0x76f12ca0)                     = 0mprotect(0x76ee7000, 8192, PROT_READ)   = 0mprotect(0x4a9000, 4096, PROT_READ)     = 0mprotect(0x76f1f000, 4096, PROT_READ)   = 0munmap(0x76f14000, 42265)               = 0openat(AT_FDCWD, "/var/lock/fw_printenv.lock", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3flock(3, LOCK_EX)                       = 0brk(NULL)                               = 0x2118000brk(0x2139000)                          = 0x2139000openat(AT_FDCWD, "/etc/fw_env.config", O_RDONLY) = 4fstat64(4, {st_mode=S_IFREG|0644, st_size=1342, ...}) = 0read(4, "# Configuration file for fw_(pri"..., 4096) = 1342read(4, "", 4096)                       = 0close(4)                                = 0openat(AT_FDCWD, "/boot/uboot.env", O_RDONLY) = 4fstat64(4, {st_mode=S_IFREG|0755, st_size=8192, ...}) = 0close(4)                                = 0openat(AT_FDCWD, "/boot/uboot.env", O_RDONLY) = 4_llseek(4, 0, [0], SEEK_SET)            = 0read(4, "n.'\202__INF0__=Ravion-V2 I.MX6 CPU"..., 16384) = 8192write(2, "Read error on /boot/uboot.env: S"..., 39Read error on /boot/uboot.env: Success) = 39close(4)                                = 0flock(3, LOCK_UN)                       = 0close(3)                                = 0exit_group(1)                           = ?+++ exited with 1 +++localhost ~ #

Люблю Linux. Ай какая красота. Все сразу встало на свои места. Ладно, согласен не все. Но уже что-то. Самое интересное здесь:


openat(AT_FDCWD, "/boot/uboot.env", O_RDONLY) = 4_llseek(4, 0, [0], SEEK_SET)            = 0read(4, "n.'\202__INF0__=Ravion-V2 I.MX6 CPU"..., 16384) = 8192write(2, "Read error on /boot/uboot.env: S"..., 39Read error on /boot/uboot.env: Success) = 39

Пытаемся прочитать 16384 (16K), а можем только 8192 (8K). В принципе всё. Бинго. Поднимаемся выше и смотрим размер файла. Да, он действительно 8192 байта. Поднимаемся еще выше и смотрим строку в конфиге. Смещение 0, длинна 0x4000 или 16384. Исправляем на 0x2000


# VFAT example/boot/uboot.env 0x0000          0x2000

Да, черт возьми я очень стар. По мне U-Bootу для переменных среды и килобайта хватит. Еще тут драгоценную память просто так расходовать. Что вы хотите. Коренной житель Питера. Нам с детства вбили что хлеб (ресурсы) надо беречь. А выбрасывать его это не ценить память погибших из-за его отсутствия в блокаду. И да, мы такие. Спасибо реальным ветеранам, спасибо городским музеям. Которые не смотря ни на что сохраняют память о тех страшных временах. Надеюсь и мои дети этому научатся.


Так вот о переменных среды для U-Boot. Ну два килобайта. Ну, ладно четыре. Куда больше? Что там можно писать в таких количествах (и главное зачем)? Потому действительно был момент когда выделенные по умолчанию 16К урезал до 8. Еще думал куда столько? Ладно, лирику в сторону проверяем.


localhost ~ # fw_printenv__INF0__=Ravion-V2 I.MX6 CPU Module BSP package__INF1__=Created: Alex A. Mihaylov AKA MinimumLaw, MinimumLaw@Rambler.Ru[]boot_os=1localhost ~ #

Работает. И даже fw_setenv работает.


localhost ~ # fw_setenv boot_os 0; fw_printenv boot_osboot_os=0

Можно ставить точку? Думаю нет. Остался один важный вопрос, который уважающий себя программист не имеет права оставить без внимания. Как думаете какой вопрос?


Правильно думаете. Если посмотреть на код, который лежит в репозитарии U-Boot, то можно легко заметить, что такая ситуация должна корректно отрабатываться. Я выше специально не стал вырезать этот кусок. Больше того, strace совершенно честно и открыто говорит что read возвращает значение 8192. Так почему же мы оказываемся в ветке с ошибкой чтения? Ведь 8192 никак не может равняться -1.


Давайте разбираться. Первая мысль, которая приходит в голову подождите, но ведь Das U-Boot это динамично развивающийся проект. Может быть мы смотрим репозитарий с последним релизом загрузчика. Но та часть, которую используем мы совсем не обязана быть последней. Она часть пользовательского окружения операционной системы. Это я адаптирую последнюю версию загрузчика, чтоб пульс проекта ощущать. А авторы сборок прикладного ПО скорее за стабильность ратуют. Потому она наверняка последней и не будет. Проверяем.


localhost ~ # fw_printenv --versionCompiled with U-Boot 2019.10localhost ~ #

Ага! А у меня в работе последняя стабильная (2020.10). Разница в год. Огромная дистанция для динамично развивающегося OpenSource проекта. А давайте посмотрим .


        lseek(fd, blockstart + block_seek, SEEK_SET);        rc = read(fd, buf + processed, readlen);        if (rc != readlen) {            fprintf(stderr, "Read error on %s: %s\n",                DEVNAME(dev), strerror(errno));            return -1;        }

Ну да. Так и есть. Уже все исправили. Обидно. Такой красивый баг был. Ладно, на нашу жизнь багов еще припасено. Только успевай разбирать.


Но ведь и это ещё не все. А давайте заглянем в файл uboot.env


localhost ~ # hexdump -C /boot/uboot.env00000000  0a 43 62 eb 5f 5f 49 4e  46 30 5f 5f 3d 52 61 76  |.Cb.__INF0__=Rav|00000010  69 6f 6e 2d 56 32 20 49  2e 4d 58 36 20 43 50 55  |ion-V2 I.MX6 CPU|00000020  20 4d 6f 64 75 6c 65 20  42 53 50 20 70 61 63 6b  | Module BSP pack|00000030  61 67 65 00 5f 5f 49 4e  46 31 5f 5f 3d 43 72 65  |age.__INF1__=Cre|[...]00000720  3d 71 70 00 76 65 6e 64  6f 72 3d 72 61 76 69 6f  |=qp.vendor=ravio|00000730  6e 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |n...............|00000740  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|*00002000localhost ~ #

Вполне очевиден факт оценка размера блока достаточного для хранения переменных среды, которая была дана мною выше, вполне справедлива. На данный момент использовано 1837 байт (0x7031 4) и формат блока довольно простой. Первый 4 байта CRC32, а дальше разделенные нулем переменные в формате переменная=значение. Другими словами поведение утилиты все равно вызывает вопросы. Ну напишет она что размер файла меньше ожидаемого и завершится с ошибкой. Но ведь это не правда. Все значимые и важные данные в него (даже в двухкилобайтный!) вполне бы влезли. Может все же стоит поправить?


Увы нет. И причина этому вполне банальна. Переменные в U-Boot могут храниться в самых разных местах. Файл на vfat разделе это самое приятное место. За это его и выбрал. Но в том же OpenWRT нет таких удобных накопителей. Там SPI-flash. И под переменные среды выделяется целый сектор. Но и тут все может быть не так плохо. Сектор целиком надо стирать. Писать можно частями. Беда с системами, которые используют dataflash или некоторые варианты raw-NAND накопителей. Т.е. с теми системами, которым помимо данных нужна еще и контрольная информация для контроля целостности и исправности. Вот они обязаны писать весь блок целиком.


Получается интересная альтернатива. Или мы для разных накопителей делаем разные утилиты для работы с переменными среды или Делаем одинаково неудобно для всех. Посмотрите на код утилиты. Посмотрите на формат сохраняемых данных. К сожалению почти всегда так и получается. Хотели, чтоб всем было одинаково хорошо. На выходе всем получилось одинаково плохо. Впрочем, свою функциональность решение вполне обеспечивает. Пусть так и остается.


Вот так неожиданно и получилось легонькое пятничное чтиво. Было бы интересно посмотреть сколько времени суммарно прожила эта ошибка, но Уже не на столько интересно, чтоб тратить на это время. Как говаривал классик: Сказка ложь, да в ней намек. Добрым молодцам урок. Спасибо за то, что дочитали.


P.S.
Пользуясь случаем передаю привет CodeRush Еще раз благодарю за приглашение на Habr. И да, всегда хочется писать о серьезном о компиляторах, о безопасном программировании непосредственно по железу. А сил хватает только на легкое пятничное чтиво. Ладно, будем считать что начало положено. Большое путешествие всегда начинается с маленького шага.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru