Mikrotik VPN routing

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

Но поскольку Telegram на территории России «заблокирован», тестирование программ создаёт определенные затруднения.

К счастью, есть VPN, но заворачивать весь трафик с компьютера в этот VPN мне кажется не очень разумным, поскольку, например, смотреть Youtube через VPN нет смысла.

Задачу я себе поставил следующую: пробросить несколько адресов в VPN силами роутера Mikrotik. Схема прохождения пакета приблизительно такая:

%3 list Список адресовmangle Разметка пакетовlist->mangle packet Пакетpacket->mangle route Маршрутmangle->route interface VPN-интерфейсroute->interface

Что для этого нужно:

  1. Поднять VPN-интерфейс;
  2. Прописать route и установить ему отметку (например to-vpn);
  3. Создать список адресов и присвоить ему имя;
  4. Установить правило mangle;
  5. Установить правило NAT.

Естественно, я буду пользоваться командной строкой.

Mikrotik позволяет работать в графическом интерфейсе WinBox, и в этом графическом интерфейсе есть командная строка, однако лично мне проще подключиться по ssh: kitty -ssh -P <ПОРТ> -l <ЛОГИН> -pw <ПАРОЛЬ> -auto-store-sshkey <IP роутера> или kitty -kload <ПУТЬ К ФАЙЛУ НАСТРОЕК>

Включить ssh-доступ в Mikrotik можно в меню IP > Services, где нужно задать номер порта для доступа, а также установить ограничения, с каких ip-адресов будет возможен доступ по ssh: по умолчанию это внутренняя сеть роутера, 192.168.xxx.0/24.

  1. Подключившись, создаём интерфейс: /interface l2tp-client add name=<ИМЯ ИНТЕРФЕЙСА> user=<ИМЯ ПОЛЬЗОВАТЕЛЯ>  password=<ПАРОЛЬ ПОЛЬЗОВАТЕЛЯ> [use-ipsec=yes ipsec-secret=<КЛЮЧ>] connect-to=<АДРЕС VPN-СЕРВЕРА> allow=mschap2. Параметр allow=mschap2 позволяет выбрать возможные алгоритмы подключения, у меня это mschap2.
  2. Прописать route достаточно просто: /ip route add gateway=<ИМЯ ИНТЕРФЕЙСА> routing-mark=to-vpn, свойства dst-address и distance будут взяты по умолчанию (0.0.0.0/0 и 1 соответственно).
  3. Добавим ip-адрес сайта, обращения к которому поедут в VPN, к списку адресов «vpn-list»: /ip firewall address-list add address=<IP-адрес> list=vpn-list. Адрес сайта, естественно, выясняется через ping или как-то так.
  4. Правило Mangle означает, что пакеты, соответствующие определенному критерию, будут определенным образом обработаны. Команда: /ip firewall mangle add action=mark-routing new-routing-mark=to-vpn dst-address-list=vpn-list chain=premangle означает, что пакеты на адреса из списка «vpn-list» будут помечены меткой для перенаправления «to-vpn».
  5. И последнее, network address traversal: /ip firewall nat add chain=srcnat out-interface=<ИМЯ VPN-интерфейса> action=masquerade.

Примечание: 1) почему-то через некоторое время могут наблюдаться проблемы с доступом; 2) это решение не устраняет проблему DNS Leak, нужно изучать далее.

Кадастровые игры

«Коммерсантъ» и другие издания где-то с сентября периодически пишут о волнениях, которые происходят вокруг кадастровой стоимости. В частности, в НК РФ просочилась поправка, согласно которой перечень имущества, налог на который можно взимать от кадастровой стоимости объекта, расширен.

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

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

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

По сути, весь кадастровый учёт (в первую очередь земельных участков) всегда преследовал цель сделать налоговую систему прозрачной и понятной, а объём взимаемых в казну налогов — прогнозируемым. Нынешняя ситуация на мой взгляд выросла исключительно из лоббирования своих интересов крупными компаниями. Согласно цитате «Коммерсанта»:

Фактически, указывает глава РСПП, это «означает непредсказуемое для делового сообщества расширение налогообложения по кадастровой стоимости на любые объекты недвижимого имущества, в том числе на производственные комплексы и списанное, но не снятое с учета в Росреестре имущество, начиная с 2020 года». Формулировка может привести к «существенному росту налоговой нагрузки и вызовет сокращение инвестиционной активности», отметил господин Шохин.

То есть крупные компании держат на балансе множество объектов недвижимости, которые «списаны» согласно данным балансового учёта, но не сняты с кадастрового учёта в Росреестре с соответствующим погашением прав. Тратить силы на снятие объектов с учёта они либо не считают нужным, продолжая делать из Росреестра помойку из «мёртвых» записей (между делом искажая реальную картину с объектами недвижимости по региону), либо эти объекты списаны только по балансу, а де-факто используются в экономической деятельности. Попытку государства подойти к вопросу формально, как и положено («есть зарегистрированные права на объект — плати!») компании блокируют лоббистским ресурсом. Удобненько устроились.

Я так думаю, что если поставить реальный и неотменяемый срок на введение обложения любой недвижимости от кадастровой стоимости года эдак с 2022, бизнес очень быстро поможет в разработке нормальных методик кадастровой оценки. Но на «чистку» Росреестра от «мертвечины» бизнесу придётся потратиться, а за реально используемое — заплатить.

Впечатления от Go

В поисках «своего» языка программирования (к которому я сейчас отношу F#) я попробовал и Go.

По простоте он похож на Си (C), но лучше, поскольку из коробки есть приятные бонусы типа каналов, defer и поддержки Unicode (руны).

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

Смысл в этом упражнении был такой, чтобы можно было писать договор кусками: в одном файле под заголовком «# Предмет договора» включается условие о предмете, и в том же файле (после) под заголовком «# Порядок приемки» указываются условия. В другом файле также могут быть заголовки «# Предмет договора» и «# Порядок приемки», но уже с другими пунктами, которые могли бы быть включены в договор.

      Первый файл                            Второй файл

# ПРЕДМЕТ ДОГОВОРА               |       # ПРЕДМЕТ ДОГОВОРА
                                 |
Пункт о предмете договора        |       Дополнительное условие
                                 |       о предмете договора
                                 |
# ПОРЯДОК ПРИЕМКИ                |       # ПОРЯДОК ПРИЕМКИ
                                 |
Приемка товара осуществляется    |       Дополнительное условие
в следующем порядке…             |       о порядке приемки.
                                 |
                                 |       # Порядок возврата товара
                                 |
                                 |       Условие о порядке возврата

На выходе получается

# ПРЕДМЕТ ДОГОВОРА

Пункт о предмете договора

Дополнительное условие
о предмете договора

# ПОРЯДОК ПРИЕМКИ

Приемка товара осуществляется
в следующем порядке…

Дополнительное условие
о порядке приемки.

# ПОРЯДОК ВОЗВРАТА ТОВАРА

Условие о порядке возврата

Аналогичным образом будут объединены заголовки второго уровня.

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

Lua'n'Pandoc

В reStructuredText есть такой элемент, как примечания .. note::. Эти примечания при трансляции rst будут выведены как

::: {.note}
::: {.admonition-title}
Note
:::

Причина необходимости закрепления
понятия?
:::

и, соответственно, в последующих документах будет абзац с текстом «Note» и следующий абзац с текстом самого примечания.

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

Для чего я, собственно, стал изучать Lua

pandoc при обработке абстрактного синтаксического дерева (АСТ) документа может использовать внешние фильтры, которые преобразуют элементы дерева до того, как дерево будет передано на вывод. Они могут быть написаны на Haskell, Python и некоторых других языках, но по мне лучше всего Lua (хотя бы потому, что этот язык в pandoc встроен).

Соответствующий фрагмент АСТ в формате json выглядит так:

{"t": "Div",
  "c": [["", ["note"], [] ],
    [{"t": "Div",
        "c": [
          ["", ["admonition-title"], [] ],
          [{"t": "Para",
              "c": [{
                  "t": "Str",
                  "c": "Note"
                } ]
            } ]
        ]
      },
      {"t": "Para",
        "c": [
          {
            "t": "Str",
            "c": "Причина"
          },
          {
            "t": "Space"
          },
          {
            "t": "Str",
            "c": "необходимости"
          },
          {
            "t": "Space"
          },
          {
            "t": "Str",
            "c": "закрепления"
          },
          {
            "t": "Space"
          },
          {
            "t": "Str",
            "c": "понятия?"
          }
        ]
      }
    ]
  ]
}

Блок примечания состоит из двух вложенных элементов div, из которых внешний включает в себя как div с классом note, так и просто параграф текста самого примечания.

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

function Div (div)
   if div.classes[1] == 'note' then
      div = pandoc.walk_block(div, {
            Para = function(element)
               if element.content[1].text == "Note" then
                  return pandoc.Para { pandoc.Strong(pandoc.Str 'Примечание') }
               end
            end })
      local sep = {pandoc.Str':', pandoc.Space() }
      local inlines = pandoc.utils.blocks_to_inlines(div.content, sep)
      return pandoc.Para(inlines)
   end
end

Фильтр проходит по дереву, и для всех элементов div вызывает функцию с таким именем. Если функция при обработке находит div.note, то заменяет текст «Note» на «Примечание» полужирным шрифтом, после чего при помощи функции pandoc.utils.blocks_to_inlines «сворачиваем» цепочку вложенных элементов в одну строку и возвращаем её обратно в АСТ.

Oracle в терминале

Мне довелось поработать в одном из «подведов» Департамента экономической политики и развития, и одна из задач была на работу и анализ данных Росреестра, для чего необходимо было либо выпрашивать данные у программистов, либо получать их самому. Напишу по памяти, как было дело.

Данные Росреестра хранились в OracleDB 11-й версии, но использовать GUI для построения запросов мне не хотелось, а хотелось командной строки. Дело в том, что наши программисты баз данных считали удобным копировать-вставить запрос в поле графического интерфейса, и полученный ответ экспортировать (нажатием кнопки!) в Excel; мне, юристу, гораздо ближе заранее написанный параметризуемый запрос, который можно один раз написать и дополнять его параметром (кадастровым номером) или двумя. К примеру, запрос к базе на предмет, имелась ли в базе данных на определенную дату установленная связь между зданием и земельным участком, исполнялся так: @okz_zu_conn 20190429 77:05:0001001:1024. Это удобно, потому что в одном заранее написанном файле могут быть разные запросы к разным таблицам, и один и тот же параметр может передаваться в разные запросы несколько раз. Копировать-вставить и потом вставлять параметр по всему тексту запроса — непродуктивная трата времени.

К сожалению, у Oracle инструменты командной строки по мере возрастания версий становились всё медленнее, поэтому для работы с базой я взял утилиту SQLPlus той же версии что и база данных (v11.2), она оказалась быстрее всех. И вообще, я не сторонник устанавливать что-либо в операционную систему; предпочитаю переносимый софт, который хранит файл настроек где-то рядом или хотя бы в понятном месте, а не в дебрях системного реестра.

Чтобы утилита командной строки заработала, нужно скачать с сайта Oracle два пакета: instantclient-sqlplus-windows и instantclient-basiclite-windows. Вот список файлов, которые нужно положить в одну папку: oci.dll orannzsbb11.dll oraocci11.dll oraociei11.dll oraociicus11.dll orasql11.dll orasqlplusic11.dll sqlplus.exe oci.sym orannzsbb11.sym oraocci11.sym oraociei11.sym oraociicus11.sym orasql11.sym sqlplus.sym.

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

server1 =
  (DESCRIPTION=
    (ADDRESS = (PROTOCOL = TCP)(HOST = ▒▒▒.▒▒.▒▒▒.▒▒)(PORT = 1521)
  )
  (CONNECT_DATA =
   (SID = ▒▒▒▒▒▒)
   (SERVER = DEDICATED)
  )
)

server2 =
  (DESCRIPTION=
    (ADDRESS = (PROTOCOL = TCP)(HOST = ▒▒▒.▒▒.▒▒▒.▒▒)(PORT = 1521)
  )
  (CONNECT_DATA =
   (SID = ▒▒▒▒▒▒)
   (SERVER = DEDICATED)
  )
)

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

Подключение простое: sqlplus LOGIN:PASS@server1, а также можно сразу указать файл исполняемого SQL-запроса.

Самое интересное, на мой взгляд, это настройки вывода.

После подключения автоматически выполняется login.sql (glogin.sql не сработает), в котором можно задать настройки. Настройки нужны в первую очередь для того, чтобы ответы базы на запросы были удобно выведены в терминал.

-- Очищаем штатные настройки колонок и прочего
CLEAR BREAKS
CLEAR COMPUTES
CLEAR COLUMNS

-- Создаём своё приглашение на ввод; \ESC необходимо заменить на символ с кодом 0x1b
SET SQLPROMPT "\ESC[0;97m _CONNECT_IDENTIFIER\ESC[0m\ESC[0;92m§\ESC[0m "
-- Эта настройка выдаёт в терминал сразу весь результат запроса, а не постранично
SET PAGESIZE 0 EMBEDDED ON
SET UNDERLINE ON

SET TRIMSPOOL ON
SET TAB OFF
SET ECHO OFF
SET FEEDBACK OFF

-- Какой текст будет выведен при нулевом значении поля
SET NULL (none)
SET RECSEP EACH
SET RECSEPCHAR " "
-- Ширина терминала, в который выводится ответ сервера, в символах
SET LINESIZE 230
SET TIME ON
SET TRIMOUT ON
SET VERIFY OFF
SET WRAP OFF

SET SERVEROUTPUT ON SIZE UNLIMITED FORMAT WORD_WRAPPED
-- Пишем всё в лог, очень полезно
SPOOL log.txt APPEND

Логировать результаты вывода в терминал может не только сам SQLPlus, но и ConEmu, но во втором случае конечно будет протоколироваться не только вывод базы данных.

Таблиц в базе данных много, и колонки названы «понятными» именами типа NAME (в десятке таблиц сразу), ASS_NAME, AREA, ADDR_ID и так далее. Сами таблицы называются, к примеру, ASUR_RR2_20190101.T$RR#REALTY (вспоминаем, что в OracleDB допустимы неалфавитные символы в именах таблиц).

Если не прописывать в теле запроса заголовок, то в выводе запросто появится несколько колонок NAME, да и дат может быть больше одной. Поэтому в каждом запросе необходимо сначала написать CLEAR COLUMNS (очищаем настройки вывода), а потом пишем

COLUMN ASUR_RR_REALTY_REALTY_ID FORMAT A20 HEADING "Номер ОКС"
COLUMN ASUR_RR_REALTYTP_NAME FORMAT A24 WORD_WRAPPED HEADING "Вид объекта"
COLUMN ASUR_RR_REALTY_NAME FORMAT A24 WORD_WRAPPED HEADING "Категория|объекта"
COLUMN ASUR_RR_REALTY_ASS_NAME FORMAT A24 WORD_WRAPPED HEADING "Назначение|объекта"
COLUMN ASUR_RR_REALTY_AREA FORMAT 999999.9 HEADING "Площадь"
COLUMN ASUR_RR_REALTY_ADDR_ID FORMAT A24 WORD_WRAPPED HEADING "Id адреса"
COLUMN ASUR_RR_REALTY_REG_NUMR_DTCR FORMAT DATE HEADING "Дата|постановки"
COLUMN ASUR_RR_REALTY_REG_NUMR_DTREM FORMAT DATE HEADING "Дата|снятия"
COLUMN ASUR_RR_REALTY_NOTE FORMAT A32 WORD_WRAPPED HEADING "Примечание"

За словом FORMAT следуют настройки: A20 означает, что эта колонка — текстовое поле шириной 20, число 999999.9 означает, что результат будет выведен с округлением до одного знака после запятой, WORD_WRAPPED будет переносить текст (а не обрезать его по размеру поля), а HEADING задаёт выводимый текст, причём | — это разрыв строки.

Перед самим запросом я бы для удобства рекомендовал создать служебный заголовок вида PROMPT \ESC[7m ЗАПИСИ ОБ ОКС \ESC[0m (где \ESC это символ 0x1b), то есть выделить заголовок цветом (если терминал умеет обрабатывать ESC-последовательности). К счастью, SQLPlus не обрабатывает выдаваемые строки, так что такие последовательности работают отлично.

В самом запросе необходим alias для запрашиваемой колонки данных вида

SELECT r.realty_id as ASUR_RR_REALTY_REALTY_ID
   , t.NAME as ASUR_RR_REALTYTP_NAME
   , r.NAME as ASUR_RR_REALTY_NAME
   , r.ASS_NAME as ASUR_RR_REALTY_ASS_NAME
   , r.AREA as ASUR_RR_REALTY_AREA
   , r.ADDR_ID as ASUR_RR_REALTY_ADDR_ID
   , r.REG_NUMR_DTCR as ASUR_RR_REALTY_REG_NUMR_DTCR
   , r.REG_NUMR_DTREM as ASUR_RR_REALTY_REG_NUMR_DTREM
   , r.NOTE as ASUR_RR_REALTY_NOTE

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

Персональные данные

«В Комитете ГД по информационной политике посоветовали взвинтить штрафы за утечку личных данных». Член экспертного совета Комитета Госдумы по информационной политике, информационным технологиям и связи Евгений Лифшиц, сообщил на «Радио 1» следующее:

«У нас тоже разрабатываются методы борьбы с утечкой личных данных, меры ответственности. Идёт работа над созданием реестра персональных данных – по итогам на портале госуслуг, например, вы сможете в личном кабинете увидеть, кому вы подписали эти бумаги: согласие на обработку персональных данных и сможете этим управлять», – рассказал Лифшиц.

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

Пол из стекла

В Huffington Post «открыли», что элиты стремятся сохраниться в качестве элит. Эка невидаль. Ниже вольный пересказ.

Проблема социальной мобильности в США существует — дети, родившиеся в 1940-е, имели 90%-й шанс зарабатывать больше родителей. Для родившихся в 1984 такие шансы в лучшем случае 50 на 50.

В основном при обсуждении мобильности напирают на отсутствие мобильности «вверх»: бедным стать богатыми всё сложнее. Но в равной степени важна и мобильность «вниз» — однако богатые, вне зависимости от своих ума и талантов, скорее всего таковыми и останутся.

Если бы США становились менее классовым обществом, то вероятность того, что богатые передадут этот статус своим детям, снижалась бы. Но она увеличивается. И вообще, проблема отнюдь не в этом.

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

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

В прошлом месяце было выяснено, что в Duke University 43% процента принято не по способностям: это спортсмены, студенты в списке декана и дети персонала. Список декана — это эвфемизм для тех, чьи родители являются состоятельными жертвователями в пользу университета.

Ещё раньше иск о расовой дискриминации против Гарварда показал, что дети из верхнего 1% по уровню богатства в 77 раз чаще попадают в Лигу плюща, чем беднейшие 20%. Аналогичный дисбаланс обнаружен и у других частных учебных заведений. Для государственных учебных заведений статистика аналогична: две трети студентов университета штата Мичиган — из верхних 20% по доходам; из нижних 20% — 1 из 30.

Академические заслуги перестали играть роль; исследование 2005 года показало, что у состоятельных учащиеся с самыми низкими оценками шанс поступить куда-либо выше, чем у бедных с самыми высокими оценками. Состоятельные студенты со средними оценками в шесть раз чаще поступают в лучшие университеты.

Аналогичным образом замыкаются в себе и сферы деятельности.

В 2016 году исследователи послали сотни резюме в лучшие юридические фирмы. Резюме были идентичны по степени и средним оценкам, но исследователи подправили их таким образом, чтобы часть резюме в своих «увлечениях» указывала на богатство кандидатов (яхтинг, классическая музыка) или бедность (прогулки, музыка кантри). Выяснилось, что такой «высший класс» будет приглашён на собеседование в 12 раз чаще.

В других исследованиях было обнаружено, что «культурное сходство» являлось критерием №1 для более чем половины нанимателей в финансовых и юридических фирмах. Некоторые отсеивали кандидатов, у которых не было нужных увлечений. Остальные кидали в мусорную корзину резюме кандидатов не из элитных учебных заведений.

Ну и разумеется, не обошлось без непотизма.

Возрастание разрыва (например, коэффициента Джини — прим. моё) создаёт порочный круг, в котором для нижних слоёв жизнь всё сложнее, а элиты отчаянно пытаются сохранить тот же статус для своих детей. Чем выше неравенство, тем сильнее негативное воздействие его на возможности.

Замыкание элит в себе несёт в себе ряд рисков, в первую очередь стагнации самого управленческого класса и падения его качества (что мы уже наблюдаем). Но что важнее, такие элиты имеют все поводы и возможности учитывать только интересы правящего класса, и корректировать политики требуемым им образом.

На этом звёздно-полосатом фоне конкурс «Лидеры России» — вполне себе решение.

Powershell alias

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

В частности, для ConEmu я написал пару алиасов следующего содержания: alias v="%ConEmuDir%\vim\vim.exe" -new_console:s50V:noR:d:"%CD%":C:"%ConEmuDir%\icons\pidi-icons\dark\tool.ico":W:"%Conemudir%\Backgrounds\outlets.png" $* и alias vh="%ConEmuDir%\vim\vim.exe" -new_console:s50H:noR:d:"%CD%":C:"%ConEmuDir%\icons\pidi-icons\dark\tool.ico":W:"%Conemudir%\Backgrounds\outlets.png" $*

Эти команды позволяют открывать редактор vim в отдельной вкладке ConEmu, которая будет располагаться справа от или внизу под экраном действующего терминала. Параметры команды -new_console: s50V = открыть новую консоль с разделением экрана вертикально на 50%; noR = disable «Press Enter or Esc to close console», don't enable «Long console output» when starting command from Far Manager, force start hooks server in the parent process; d:"%CD%" = установить текущую директорию для открываемой консоли; C:"%ConEmuDir%\icons\pidi-icons\dark\tool.ico" = установить иконку; W:"%Conemudir%\Backgrounds\outlets.png" = установить фон. "$*" передаёт развернутой команде все параметры, которые были переданы самому алиасу.

Мне понравился Powershell, так что я решил сделать такие же алиасы там, однако это оказалось не так-то просто.

Powershell не умеет делать длинные алиасы — для этого необходимо создавать функцию, и делать алиас уже на неё. Если мы хотим создать алиас vv (vim vertical), то это можно сделать так:

Function VimVertical {
   <...>
}

New-Alias -Name vv -Value VimVertical

Казалось бы, вполне очевидно переписать "%ConEmuDir%\vim\vim.exe" на ${env:ConEmuDir}\vim\vim.exe, однако это не сработает, он будет ругаться. Также возникают проблемы с обработкой кавычек, которые необходимо передавать в ConEmu именно в таком виде.

Через немалое время в поисковике было выяснено, что нужно поставить перед такой командой амперсанд (&). Кавычки экранируются символом `, а аргументы полностью передаются параметром $args.

В итоге получилось следующее:

Function VimVertical {
   & ${env:ConEmuDir}\vim\vim.exe -new_console:s50V:noR:C:`"${env:ConEmuDir}\icons\pidi-icons\dark\tool.ico`":W:`"${env:ConEmuDir}\Backgrounds\outlets.png`" $args
}

New-Alias -Name vv -Value VimVertical

Ну и до кучи сделал алиасы на системные папки:

Function Go-Downloads
{
   chdir $env:USERPROFILE\Downloads
}

New-Alias -Name dl -Value Go-Downloads -Force

Function Go-Home
{
   chdir $env:USERPROFILE
}

New-Alias -Name ~ -Value Go-Home -Force

Function Go-Documents
{
   chdir $env:USERPROFILE\Documents
}

New-Alias -Name docs -Value Go-Documents -Force

Пока всё.

Ещё об F#

Что мне ещё понравилось в F#, так это его «вежливость». Он удивительным образом умеет не путаться под ногами; если в Haskell у меня было ощущение борьбы, то здесь язык в основном предлагает мне функциональную парадигму программирования, но при необходимости вежливо отходит в сторону и не мешает, если ты решил написать что-нибудь, например, в императивном стиле.

F#|>♥

Через PowershellKoans я узнал о F#Koans, и считаю, что они просто фантастические. А уж после того, как я моментом (оценил удобство) прикрутил Ionide к Vim, который я до этого момента недолюбливал, моё отношение к F# и Vim такое — F#|>♥, let g:Vim=☺

Lua.Busted в Windows

В качестве системных скриптов Powershell очень неплох, но мне было интересно разобраться с Lua. Для этого я зарегистрировался на http://exercism.io на соответствующий трек.

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

В Elementary OS всё это заработало за несколько команд в консоли, однако я всё равно предпочитаю Windows, но там всё оказалось несколько сложнее.

Busted ставится через менеджер пакетов Luarocks, а Luarocks ставится через хороший менеджер пакетов — Chocolatey.

В менеджере пакетов Chocolatey сразу можно написать choco install luarocks, оно установится и будет работать. А вот luarocks install busted будет загружать зависимости и сразу упадёт, поскольку не найдёт cl.exe для выполнения команды… компиляции. Да, Busted хочет собираться из исходников.

К счастью, у меня как адепта C# & F#, был установлен Visual Studio 2019. Однако, cl.exe среди установленных мною пакетов для этих языков отсутствовал. Было выяснено, что это утилита из пакета MS Build Tools. Однако далее возникает ошибка, так же команда не находит errno.h, заголовочный файл.

Дабы опустить многочасовую историю выгрузки MinGW и попытки прописать директории /include, сразу скажу: необходимо поставить в Visual Studio пакет Windows 10 SDK, а чтобы Busted собрался: запустить от имени администратора файл C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\launchdevcmd.bat — командную строку разработчика в Visual Studio, в нём подхватываются нужные пути к исходникам.

И немаловажное: в path не нужно брать в кавычки длинные пути с пробелами, там уже есть разделитель ;. Если кавычки будут, то (в частности) эта самая строка разработчика тоже будет падать…

Достаточно длинный путь, чтобы просто написать в Lua «Hello, world».

Чтобы исполнять файлы .lua в командной строке без вызова интерпретатора, нужно сделать несколько вещей.

  • дописать .lua к переменной PATHEXT: set PATHEXT=.py;.py3;.msc;.exe;.com;.cmd;.bat;.lua (или set PATHEXT=.lua;%PATHEXT%), что позволит вызывать файл без дописывания расширения;
  • создать расширение: assoc .lua=LuaScript;
  • при помощи утилиты создания типа файла создать тип файла, например, Luascript: ftype LuaScript="%ПОЛНЫЙ ПУТЬ К ИСПОЛНЯЕМОМУ ФАЙЛУ%\lua5.1.exe" "%1" %* (lua5.1.exe устанавливается вместе с Luarocks по умолчанию и называется почему-то именно так, а не иначе, но можно поставить и что-нибудь посвежее). Файловые ассоциации не используют переменные окружения, так что путь необходимо прописывать полностью.

Horizon Datasys

Если с Windows 7 ещё можно как-то обходиться без снимков файловой системы, то с Windows 10 это становится неудобно. Во-первых, любое обновление может систему сломать, а во-вторых, попытки внести какие-либо изменения в систему могут обернуться непредвиденными и даже фатальными последствиями.

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

У компании интересующих меня продуктов два: Reboot Restore RX и Rollback RX. Каждый из них имеет как профессиональную, так и бесплатную версию с некоторыми ограничениями.

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

При загрузке можно нажать Home и в меню выбрать принудительное восстановление системы из образа (или одного из слепков системы).

Reboot Restore RX

Функциональность продукта в следующем: в любой момент состояние системы можно сохранить в образ, который автоматически будет восстанавливаться каждый раз после перезагрузки. Естественно, что доступ на запись в такой системный образ имеют только пользователи категории «Администратор». Решение рассчитано на публичные компьютеры, которые после работы пользователя должны восстановливаться к предусмотренному администратором состоянию.

К сожалению, после менее чем месяца работы система «глюкнула», в результате чего диск пришлось полностью вычистить. Причиной, как мне представляется, стала ошибка в установщике: при установке программы пользователю на выбор предлагаются разделы диска, которые подлежат защите и сохранению в образ. В этом списке, предлагаемым установщиком, был указан шифрованный системный раздел EFI, чего делать категорически не следовало. Я, по недопониманию, этот раздел включил в сохраняемые, чем скорее всего и предопределил исход — через месяц система рухнула.

Rollback RX

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

Судите сами: автоматические обновления Windows 10 необходимо отключать, иначе мёртвая система — дело ближайшего времени. В ходе отключения обновлений, удаления телеметрии и приложений Metro, а также просто настройки системы «под себя» велик риск что-нибудь сломать.

В частности, у меня перестали работать установочные пакеты; чем это было вызвано, я так и не понял, потому что существенной разницы между слепками системного реестра из рабочего состояния и нерабочего не нашлось (разницу между файловыми системами я делать не стал, не до этого). Но только наличие нескольких предыдущих снимков позволило мне откатиться в рабочее состояние, поставить всё что мне нужно, а снимок с нерабочим состоянием удалить.

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

Всем стабильных систем.

Смысл автоматизации

Как обычно, с запозданием читаю новости: «Искусственному интеллекту подыскивают занятие».

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

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

Чем будут жить высвобождаемые, не ясно.

Магия Ада

Речь, конечно, о магии языка Ада.

Возможно, это восторг неофита, но тем не менее. Недели две назад я решил погрузиться в язык Ada. Он вполне вписывается в мои предпочтения: компилируемый, строго (точнее, очень строго) типизированный, со сборщиком мусора. По таким критериям сегодня выбор достаточно широк, я хорошо отношусь к C# и Go.

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

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

Объявление

Мы объявляем тип Indextype длиной 3, а также объявляем структуру точки и функцию расстояния между двумя точками.

geometry.ads (Источник)

WITH Ada.Text_Io;

PACKAGE Geometry IS
   TYPE Indextype IS MOD 3;
   PACKAGE Index_Io IS NEW Ada.Text_Io.Modular_Io (Num => Indextype);

   TYPE Point IS RECORD
      X, Y  : FLOAT   := 0.0;
      Index : INTEGER := 0;
   END RECORD;
   FUNCTION Get_Distance (M, N : POINT) RETURN FLOAT;
   PROCEDURE Put (P : POINT);
END Geometry;

Детали реализации

В самой тестовой программе создаём переменную Index модульного типа, объявленного в пакете Geometry. Объявляем тип «треугольник»: массив точек длиной, равной размерности типа Indextype (по-моему, это очень здорово).

Для начала заполняем массив точек случайными координатами и выводим их на экран.

geometry.adb (Источник)

WITH Ada.Numerics.Elementary_Functions;
WITH Ada.Float_Text_Io;
WITH Ada.Integer_Text_Io;
WITH Ada.Text_Io;
USE Ada.Numerics.Elementary_Functions;

PACKAGE BODY Geometry IS

   FUNCTION Get_Distance (M, N : POINT) RETURN FLOAT IS
   BEGIN
      RETURN Sqrt (FLOAT ((N.X - M.X)**2) + FLOAT ((N.Y - M.Y)**2));
   END Get_Distance;

   PROCEDURE Put (P : POINT) IS
   BEGIN
      Ada.Text_Io.Put ("Point");
      Ada.Integer_Text_Io.Put (P.Index);
      Ada.Text_Io.Put (": ");
      Ada.Text_Io.Put ("p.X is");
      Ada.Float_Text_Io.Put (P.X, Aft => 4, Exp => 0);
      Ada.Text_Io.Put (", p.Y is");
      Ada.Float_Text_Io.Put (P.Y, Aft => 4, Exp => 0);
      Ada.Text_Io.New_Line;
   END Put;

END Geometry;

Тестовая программа

И в самом конце самое интересное: мы можем пройтись итератором по всему диапазону (размерности) типа Indextype, и для каждого случая подсчитать расстояние между текущей и следующей точкой, а также любой другой, используя ±N; в языке типа C++ мы не cможем, находясь на последней позиции массива, взять значение из позиции «текущая + 1». Здесь же — легко и просто.

getline.adb (Источник)

-- This program fills an array with three points with random coordinates
-- and then calculates the distances between them.

WITH Ada.Float_Text_Io;
WITH Ada.Integer_Text_Io;
WITH Ada.Text_Io;
WITH Ada.Numerics.Float_Random;
WITH Geometry;
USE Geometry;

PROCEDURE Getline IS

   Index : Geometry.INDEXTYPE := 0;
   Seed  : Ada.Numerics.Float_Random.GENERATOR;

   TYPE Triangle IS ARRAY (Geometry.INDEXTYPE'Range) OF Geometry.POINT;
   Tri : TRIANGLE;

BEGIN
   Ada.Numerics.Float_Random.Reset (Seed);

   FOR I IN Geometry.INDEXTYPE'Range LOOP
      Tri (I) :=
        (X     => Ada.Numerics.Float_Random.Random (Seed) * 10.0,
         Y     => Ada.Numerics.Float_Random.Random (Seed) * 10.0,
         Index => INTEGER (I));
   END LOOP;

   FOR I IN Geometry.INDEXTYPE'Range LOOP
      Geometry.Put (Tri (I));
      Geometry.Put (Tri (I + 1));
      Ada.Text_Io.Put ("Distance between points");
      Ada.Integer_Text_Io.Put (Tri (I).Index, Width => 2);
      Ada.Text_Io.Put (" and ");
      Ada.Integer_Text_Io.Put (Tri (I + 1).Index, Width => 2);
      Ada.Text_Io.Put (" is ");
      Ada.Float_Text_Io.Put
        (Geometry.Get_Distance (Tri (I + 1), Tri (I)), Aft => 4, Exp => 0);
      Ada.Text_Io.New_Line (2);
   END LOOP;
END Getline;

Делаем gnatmake getline && getline, вывод программы будет приблизительно таким:

Point          0: p.X is 4.5906, p.Y is 9.9080
Point          1: p.X is 3.4309, p.Y is 8.8065
Distance between points 0 and  1 is  1.5994

Point          1: p.X is 3.4309, p.Y is 8.8065
Point          2: p.X is 9.1367, p.Y is 9.4752
Distance between points 1 and  2 is  5.7448

Point          2: p.X is 9.1367, p.Y is 9.4752
Point          0: p.X is 4.5906, p.Y is 9.9080
Distance between points 2 and  0 is  4.5667

Организация быстрого запуска

В ходе работы привыкаешь к определенному набору софта. Сам набор, конечно, постепенно менялся — MS Word уступил своё место Sublime Text, а графические интерфейсы постепенно были заменены работой в строке терминала ConEmu. Но самое важное, что должно быть у такого софта — переносимость на флешке с одного компьютера на другой с сохранением всех настроек, ведь далеко не во всякой организации тебе позволят установить то, что ты считаешь нужным. К тому же, настройка того же MS Word на каждом рабочем компьютере по третьему или пятому разу может стать достаточно раздражающей задачей.

Так что для упрощения работы администраторам у меня постепенно оформилась подборка переносимого ПО, которое не нужно устанавливать.

Лаунчеров для таких комплектов достаточно много, но я пользовался PSMenu, в меню которого со временем образовалось больше 40 подменю редакторов текста, CSV и XML, аудиоредакторов и плееров, инструментов для сравнения текстов и графики, папок и файлов, ftp и ssh-клиентов, инструментов для работы с базами данных, просмотрщиков графики и шрифтов, редакторов графических, генераторов QR- и bar-кодов, поисковиков и индексаторов (из которых мне особо нравится hddb), редакторов реестра и т.д. и т.п. — мало ли что может понадобиться.

Конечно, со временем эта коллекция стала излишне большой, и управляться с ней стало достаточно сложно; часть инструментов больше не работает даже в Windows 7, потому что жили они у меня с незапямятных времён Windows XP и Vista.

К слову, та же проблема уже намечается и в хаосе консольных утилит, запомнить кто и что делает трудновато. Но там я дописываю ту или иную утилиту в справку, которая автоматически выдается при запуске новой консоли: там и fd и fzf, sd и rg, pandoc и pp, curl и aria2c, а также выводятся alias.

К тому же этот PSMenu стал не совсем удобным. Да, у него есть несколько очень полезных опций — к примеру, при запуске этого меню отдельные программы могут быть запущены автоматически. У меня это Minibin, ClipDiary и True XMouse (хотя вместо последнего уже прижилась настройка из WinAeroTweaker).

Однако помимо этого удобства заканчиваются, приходится делать много лишних щелчков мышью, а я после перехода на механическую клавиатуру предпочитаю последнюю. Не помню с чьей подсказки, но мне на глаза попался ueli, современное творчество на JS: по нажатию выбранной комбинации клавиш на экране возникает строка ввода, и по мере ввода в неё нужного слова происходит нечёткий поиск среди ярлыков программ, файлов в выбранных директориях, настроек системы и много чего ещё по выбору. Скорость и удобство совершенно другого уровня. Программа, что приятно, умеет быть переносимой (хотя индекс она, конечно, хранит в %USERPROFILE%, потому что в индекс как правило включаются и программы, установленные на компьютере).

Не обошлось и без недостатков — в частности, если старая версия ueli работала на Windows 7 нормально, то после обновления всё сломалось. К счастью, я достаточно хорошо разбираюсь, чтобы отследить источник проблемы: для получения списка файлов ueli под капотом использует Powershell, но поскольку в Windows 7 Powershell по умолчанию старый, то и ueli ломается, поскольку требует новой функциональности. К счастью, установка Win7AndW2K8R2-KB3191566-x64 решила проблему.

В начале при наличии профиля Powershell (Windows.Powershell_profile.ps1) в соответствующем месте пользовательского профиля индексация в ueli «ломается» (а это немаловажный момент, Chocolatey пишет в профиль загрузку автодополнений командной строки и прочего устанавливаемого), однако это легко поправимо: в строке Powershell, запущенной с полномочиями администратора, пишется Set-ExecutionPolicy -ExecutionPolicy Bypass. По умолчанию при наличии профиля применяется ограничение на исполнение скриптов, поэтому ueli и не может выполнить команду, из которой он получит список строк…

Конечно, натравить ueli индексировать всю помойку утилит в поисках исполняемых файлов неразумно, да и в меню они будут выведены как regass.exe — очень «информативно». Поэтому я решил создать ярлыки (.lnk) на отдельные файлы в отдельной же директории.

Создавать ярлыки вручную — последнее дело; к счастью, мне удалось найти утилиту shortcut от Marty List, которая позволяет одной командой создать ярлык в нужном месте расположения. В качестве параметров этой утилите можно передать не только полный путь до файла, но и путь до его директории, которая автоматически помещается в ярлык.

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

Второй выявленный недостаток: ueli не конвертирует имена файлов в UTF-8, из-за чего русский текст в именах файлов (ярлыков) превращается в непонятное месиво; возможно, это связано с тем, как Windows хранит имена файлов, но разработчикам программы я на это укажу.

К числу активно используемых мною функций относится и поиск из той же строки программы: пишешь g? запрос и производится поиск слова запрос в Google. По желанию можно указать свои сайты для выполнения поискового запроса. Аналогичным образом подключается и поиск по Everything (устанавливается Everything + интерфейс командной строки для него).

The Indispensable Man

В прощальном письме от T. Boone Pickens, которое было написано незадолго до его смерти 11.09.2019, он упомянул строки Saxon White Kessinger из поэмы «The Indispensable Man» («Незаменимый человек»), которые тот написал в 1959 году. Вот они.

Sometimes, when you feel that your going
Would leave an unfilled hole,
Just follow this simple instruction,
and see how it humbles your soul:

Take a bucket and fill it with water,
Put your hand in it up to the wrist,
Pull it out and the hole that’s remaining
Is a measure of how you’ll be missed.

You may splash all you please when you enter,
You can stir up the water galore,
But stop, and you’ll find in a minute
That it looks quite the same as before.

Я по мере скромных способностей перевёл:

Коль случайно тебе показалось,
Что умри ты — мир станет бедней,
Ты инструкциям ниже последуй
И в душе своей скромность посей.

Ты водою наполни ведерко,
Руку — в воду по самую кисть.
Руку вынь; видишь, дырка осталась?
Вот настолько бедней станет жизнь.

Можешь сколько угодно плескаться,
Гладь воды возмущать, теребя,
Но лишь стоит тебе устраниться,
Станет мир тем, что был до тебя.

Клавиатурные похождения

Мне кажется, ни у кого не вызовет сомнения тезис, что переключать языки через Alt-Shift или Ctrl-Shift — как минимум неудобно, если не сказать больше.

Достаточно долгое время для переключения языков я пользовался старой программой capsmin.exe. Эта программа перехватывает нажатие клавиши Caps Lock (переназначение этой в общем-то ненужной клавиши — весьма популярное решение) и переключает раскладку клавиатуры с русского на английский и обратно.

К сожалению, несмотря на все достоинства, у программы был выявлен баг — бывало так, что в момент переключения языков используемая программа (как правило, это MS Word либо Sublime Text) зависала. Не знаю, чем это вызвано, но это факт, отслеженный мною на множестве использовавшихся машин.

В один момент после обидного зависания мне это надоело и я решил изменить эту ситуацию.

В Windows7, как известно, можно производить замену нажатия одной клавиши на другую. Так что первый ход был ясен: заменить программу системной настройкой, подменив Caps Lock на ` (он же ~), и установить переключение языков на этот самый `.

В интернете много рецептов прямого редактирования системного реестра для выполнения такой замены, однако я считаю, что проще воспользоваться программами типа sharpkeys и mapkeyboard (архивы с этой программой в интернете на каждом углу, а вот на сайте разработчика программы нет). Ради смеха я даже прописал замену Caps Lock сразу и на LShift и на LAlt, однако это ничего не дало, система обрабатывает только одно вхождение замены.

К сожалению, при этой замене Caps Lock на backtick мы теряем как сам backtick (`) и тильду (~), так и дорогую моему сердцу клавишу ё. Никакого способа обойти это штатными средствами замены нет.

Однако, решение нашлось.

С давних времён я пользуюсь клавиатурной раскладкой Ильи Бирмана. Это крайне удобная вещь, позволяющая в первую очередь писать правильные «кавычки» и ставить типографское тире вместо категорически неграмотного дефиса или иного подобного тире символа, который услужливо подставляет столь же неграмотный в вопросах русской типографики MS Word. Также с её помощью удобно ставить квадратные скобки, до которых из русской раскладки добираться затруднительно (нужно сначала переключиться в английскую).

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

Так что я поставил MS Keyboard Layout Creator 1.4, открыл уже установленную у меня в системе раскладку (для начала русскую) Ильи Бирмана и приступил. Отличной фишкой этой программы является то, что установочные пакеты для раскладок клавиатуры она создаёт автоматически — очень удобно.

Букву ё мы получаем стандартным для его раскладки способом: нажимая AltGr+Shift+;, что переводит клавиатуру в режим ожидания символа, над которым нужно поставить две точки (умляут), ведь ё — это именно что е с умляутом. Так что нажимаем е и пожалуйста. Впрочем, тем же способом можно получить и другие буквы с умляутом, если такой символ есть в стандарте Unicode и такая замена прописана в раскладке. Но не будем на этом останавливаться.

Восстановить тильду чуть сложнее — для этого я модифицировал обе языковых раскладки, так что теперь тильда вводится через AltGr+q или AltGr+й. Опять-таки, это удобно, потому что на тильду у меня завязаны некоторые alias в командной строке, и вызывать тильду можно из двух языков.

Сложнее всего вернуть backtick; для этого на знак ' пришлось переназначить всё то, что было назначено на `. Зачем? Дело в том, что ` работает в раскладке Ильи Бирмана точно так же, как и AltGr+Shift+;, то есть через комбинацию AltGr+Shift+` можно вводить как буквы с ударѐнием (не все, это зависит от языка и от раскладки, нужно ещё разбираться как это устроено), так и непосредственно сам символ `.

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

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

Готовые версии моей раскладки можно скачать здесь.

О важности видеокарты

В этом году мне наконец-то удалось собрать себе компьютер по желанию.

Довольно давно у меня в заначке была материнская плата Asus Z9PE D8WS — два сокета 2011 с поддержкой Xeon E5; на мой взгляд, отличная плата для мощной рабочей станции.

Пару подходящих процессоров я нашёл не сразу, в итоге остановился на достаточно бюджетных 2667v2 (3,3Ггц). Позже удалось вполне удачно купить 256Гб (8×32Гб) регистровой памяти на 1866Мгц; видеокарту я взял старую, Nvidia 950, поскольку не видел смысла её менять — мне нужно было два выхода DVI на два моих старых монитора.

256 Гигабайт памяти материнская плата увидела на этапе BIOS POST, однако при заходе внутрь BIOS два последних слота показывались с нулевым размером, и ни одна из имеющихся у меня операционных систем либо не признавала наличия 256Гб памяти вообще (ограничиваясь указанием на 192Гб), либо признавала наличие всех планок памяти, но 64Гб были «зарезервированы». Я помню, что Windows 7 больше 192Гб всё равно не увидит, но есть же ещё Ubuntu и Windows 10.

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

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

Я до сих пор не понимаю, каким образом видеокарта «съедала» 64Гб памяти на втором процессоре в двух последних слотах. В итоге я почти даром взял Nvidia 2000D с двумя DVI, чем закрыл вопрос.

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

Версионный контроль документов с Fossil

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

На сегодняшний момент текстовые редакторы удобной возможности для версионного контроля и последующего объединения не предоставляют, по крайней мере мне так кажется. MS Word неудобен, особенно в тех случаях, когда работа идёт в таблицах.

Предлагаю следующий вариант:

  1. Таблица конвертируется в html
  2. html пропускается через HTML Tidy (tidy -w 64 -i -m [%source%|-o %target%]), тем самым получается более-менее нормальный html. Почему так сделано? Потому что из MS Word получить нормальную grid table через pandoc невозможно, он почему-то делает колонки узкими и разрывает слова, а значит такой grid table при обратной конверсии будет нечитабелен, слова будут разорваны.
  3. Этот html мы помещаем в репозиторий fossil (fossil add %file% && fossil commit -m "Версия документа, отправленная на согласование")
  4. После поступления правок от второй стороны вносим нужные правки и сохраняем документ.
  5. Помещаем измененную версию в новую ветку (fossil commit -m "Версия с правками от Министерства" --branch Ministry_edits), эта команда сразу создаст нужную ветку на основе существующей.
  6. Чтобы посмотреть разницу, пишем fossil diff --branch Ministry_edit --command "icdiff.py -N --cols 200", то есть делаем diff сразу по всей ветке (по умолчанию --branch сравнивает нужную ветку с trunk), и для дифференциала вызываем скрипт icdiff с указанием номеров строк и общей шириной двухколоночного вывода в 200 символов (что соответствует моему терминалу).

Чтобы сравнить две ветки (по последним коммитам), пишем fossil diff --from branch01 --to branch02  --command "icdiff.py -N --cols 200". Вместо имени ветки может стоять хэш отдельного коммита: fossil diff --from 1c24 --to fe53 --command "icdiff --cols 230".

Чуть позже появился другой вариант, основанный на reStructuredText.

B rst используем формат .. list-table:: и далее по вертикали построчно печатается таблица.

Таблицу из формата grid-table в list-table можно конвертировать при помощи rsrlisttable из питоновского пакета rstdoc.

Такие вертикальные псевдотаблицы отлично читаются pandoc и другими конвертерами; в grid-table их можно конвертировать тем же pandoc.

То есть тулчейн выглядит так: docx -> pandoc --wrap=none -> markdown_grid-table -> rstlisttable -> rst, после чего помещаем rst с такими таблицами в репозиторий.

Вспомнил ещё кое-что: в pandoc есть параметр --columns, его надо указывать, чтобы таблицы выводились на экран из rst в markdown правильно (необходимо поставить ширину хотя бы 200-250 поставить) если нужно посмотреть содержимое репозитория; если мы выводим в репозиторий, то конечно нужно использовать --wrap=none, иначе принудительные разрывы строк придётся чистить. Чтобы посмотреть состояние репозитория (в частности, хэши коммитов в ветках, если мы хотим их сравнить), пишем fossil timeline. А чтобы вывести нужный коммит… fossil cat map.rst -r 03c11 | pandoc -f rst -t markdown --columns 270, и вуаля.

В pandoc есть параметр --track-changes=accept|reject|all, с его помощью можно поиграться с правками. Поиграть можно так:

  • pandoc -f docx -t markdown --columns 270 --wrap=none --track-changes=reject %1 > from.txt && rstlisttable -i from.txt
  • pandoc -f docx -t markdown --columns 270 --wrap=none --track-changes=accept %1 > to.txt && rstlisttable -i to.txt
  • icdiff --strip-trailing-cr --cols 270 from.txt to.txt

…и мы смотрим все правки параллельно.

Эх, Михаил Евграфович…

В сети на каждом углу и от каждого самозваного эксперта по преобразованию всего плохого в самое хорошее можно увидеть цитату:

«Они сидели и день и ночь, и еще день, и еще ночь, и все думали, как бы сделать их убыточное предприятие прибыльным, ничего в оном не меняя».

—М.Е.Салтыков (Н.Щедрин)

Кто-нибудь, укажите конкретное место, где же такая цитата у Михаила Евграфовича встречается. Особо умные пишут, что дескать в «Истории одного города» есть. Я поискал по электронному 17-томному сборнику его трудов, включая письма — нет такой фразы, и похожей нет. И однокоренных слов от «убытки» не нашёл. Неужто опять выдумали, приписав известному автору свою самодеятельность?