Перейти к главному содержимому

LaTeX: параметры ключ=значение

Определение команды:

\ExplSyntaxOn

\keys_define:nn { mymodule }
  {
    color .tl_set:N = \l_mymodule_color_tl ,
    color .initial:n = { black } ,
    text .tl_set:N = \l_mymodule_text_tl ,
    text .initial:n = { Default~Text } ,
    size .dim_set:N = \l_mymodule_size_dim ,
    size .initial:n = { 12pt } ,
  }

\NewDocumentCommand { \mycommand } { O{} }
  {
    \keys_set:nn { mymodule } { #1 }
    \group_begin:
      \color{\l_mymodule_color_tl}
      \fontsize{\l_mymodule_size_dim}{\dim_eval:n{\l_mymodule_size_dim * 1.2}}\selectfont
      \l_mymodule_text_tl
    \group_end:
  }

\ExplSyntaxOff

Использование:

\begin{document}

\mycommand % Uses default values
\par
\mycommand[color=blue, text=Hello~World, size=18pt]
\par
\mycommand[size=24pt, color=red]

Свойства ключей в keys_define

Каждый ключ в keys_define может иметь набор свойств:

  • .code:n = code to execute: задает код для выполнения, когда ключ задан, при этом в коде может использоваться #1 и туда будет подставлено значение ключа.
  • .initial:n = initial value: Задает начальное значение. Оно будет использоваться, если ключ вообще не задан.
  • .default:n = default value: Задает значение по умолчанию. Оно будет использоваться если ключ задан без значения (например, key=).
  • .choices:nn = { список выбора }: Задает список допустимых значений для ключа. Для каждого возможного значения задается код, который должен выполниться в соответствующем случае.
  • .bool_set:N, .bool_set_true:N, .bool_set_false:N: Задает что делать для boolean-ключей, установить значение, true или false.
  • .int_set:N, .dim_set:N, .tl_set:N: Задает присвоение переменным для числа, размера или строки.

Пример:

\ExplSyntaxOn
\keys_define:nn { mymodule }
  {
    color .code:n = \tl_set:Nn \l_mymodule_color_tl { #1 },
    color .initial:n = black,
    shape .choices:nn =
      {
        circle .code:n = \tl_set:Nn \l_mymodule_shape_tl { circle },
        square .code:n = \tl_set:Nn \l_mymodule_shape_tl { square }
      },
    shape .initial:n = circle,
    linewidth .int_set:N = \l_mymodule_linewidth_int,
    linewidth .initial:n = 1
  }
\ExplSyntaxOff

В этом примере определены ключи color, shape и linewidth для модуля mymodule.

color принимает значение, shape значение из списка, a linewidth задает числовую переменную.

LaTeX mbox на список

Есть: строка, разделенная запятыми.

Надо: завернуть каждый элемент отдельно в функцию (например, в \mbox) и собрать строку обратно, возможно с другими разделителями.

\ExplSyntaxOn

\NewDocumentCommand{\MBoxingList}{O{,\ }m}
{
  \seq_clear_new:N \l__in_seq
  \seq_set_from_clist:Nn \l__in_seq {#2}
  \seq_set_map:NNn \l__in_seq \l__in_seq {\mbox{##1}}
  \seq_use:Nn \l__in_seq {#1}
%  \quad\seq_count:N \l__in_seq
}

\ExplSyntaxOff

Использовать так: `\expanded{\MBoxingList[, ]{123, 123, 5234, 6543}}

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

P.S. Вроде и работает, но с этим expansion что-нибудь да не нравится. В общем, глаз да глаз.

P.P.S. Можно попробовать через пакет listofitems.

P.P.P.S Есть какое-то решение на классическом синтаксисе:

\makeatletter

% Functional foreach construct
% #1 - Function to call on each comma-separated item in #3
% #2 - Parameter to pass to function in #1 as first parameter
% #3 - Comma-separated list of items to pass as second parameter to function #1
\def\foreach#1#2#3{%
  \@test@foreach{#1}{#2}#3,\@end@token%
}

% Internal helper function - Eats one input
\def\@swallow#1{}

% Internal helper function - Checks the next character after #1 and #2 and
% continues loop iteration if \@end@token is not found
\def\@test@foreach#1#2{%
  \@ifnextchar\@end@token%
    {\@swallow}%
    {\@foreach{#1}{#2}}%
}

% Internal helper function - Calls #1{#2}{#3} and recurses
% The magic of splitting the third parameter occurs in the pattern matching of the \def
\def\@foreach#1#2#3,#4\@end@token{%
  #1{#2}{#3}%
  \@test@foreach{#1}{#2}#4\@end@token%
}

\makeatother

%% Функция для использования
\def\makembox#1#2{\mbox{#2}, }

HTML redirect

Перенаправить статически страницу можно html-тегом <meta... внутри <head>...</head>, можно при помощи javascript. Лучше использовать и то и другое. И на всякий случай ещё и явную ссылку, вдруг у кого-то браузер сам не перейдёт.

Вот пример:

<!DOCTYPE HTML>
<html lang="en-US">
<head>
  <meta charset="UTF-8">
  <!-- html redirect with timeout=0 -->
  <meta http-equiv="refresh" content="0; url=http://example.com">
  <!-- for search engines -->
  <link rel="canonical" href="http://www.example.com"/>
  <!-- javascript redirect -->
  <script type="text/javascript">
      window.location.href = "http://example.com";
  </script>
  <title>Page Redirection</title>
</head>
<body>
  <!-- Note: don't tell people to `click` the link, 
       just tell them that it is a link. -->
  If you are not redirected automatically, 
  follow this <a href='http://example.com'>link to example</a>.
</body>
</html>

Вызов сторонних скриптов из LaTeX

Latex при сборке документа может вызывать сторонние программы. Иногда это удобно.

По умолчанию такое поведение запрещено, но опцией командной строки --shell-escape это разрешается.

Настройка latexmk

Если сборка производится через latexmk, то в файл конфигурации latexmkrc надо добавить строчки (переопределить переменные для запуска, по сути):

$latex = 'latex  %O  --shell-escape %S';
$pdflatex = 'pdflatex  %O  --shell-escape %S';

В документе

В самом документе можно вызывать внешние программы с параметрами и сразу же вставлять на это место документа их выхлоп. Например:

\makeatletter
\@@input|"python3 ff.py"
\makeatother

Функции в bash

Задать функцию

Функции в bash можно задавать двумя способами:

# Способ 1
function_name () {
  commands
}

# Способ 2
function function_name {
  commands
}

Простые функции можно объявлять в одну строку. При этом пробелы между фигурными скобками и телом функции важны!. И последним в теле должен быть ; (точка с запятой).

Объявление функций в одну строку:

# Способ 1
function_name () { commands; }

# Способ 2
function function_name { commands; }

Итого по объявлению функций:

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

Переменные

Глобальные переменные видны в функциях и могут быть модифицированы в функциях. Локальные переменные (область видимости внутри функции) должны объявляться с ключевым словом local.

Локальные переменные имеют приоритет над глобальными если используется одно и то же имя.

#!/bin/bash

var1='A'
var2='B'

my_function () {
  local var1='C'
  var2='D'
  echo "Inside function: var1: $var1, var2: $var2"
}

echo "Before executing function: var1: $var1, var2: $var2"

my_function

echo "After executing function: var1: $var1, var2: $var2"

Вывод этого скрипта:

Before executing function: var1: A, var2: B
Inside function: var1: C, var2: D
After executing function: var1: A, var2: D

Параметры

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

  • Внутри функции доступ к параметрам осуществляется через переменные $1, $2, $3 и т.д.
  • Переменная $0 содержит имя самой функции.
  • Переменная $# содержит количество переданных параметров.
  • Переменные $* и $@ содержать все парамеры, переданные функцию.

Отличия переменных $* и $@

  • Если эти переменные НЕ заключать в кавычки, то они эквивалентны;
  • В кавычках "$*" раскрывается в одну строку "$1 $2 ... $n";
  • В кавычках "$@" раскрывается в разные строки "$1" "$2" ... "$n".

Пример:

#!/bin/bash

greeting () {
  echo "Hello $1"
}

greeting "World"

Возвращаемые значения

В отличие от функций в "настоящих" языках программирования, функции в Bash не позволяют возвращать значения как это принято. Когда функция bash заканчивает выполнение, ее возвращаемое значение это статус последней выполненной команды в теле функции, обычно 0 при успешном завершении, число 1-255 код ошибки.

Это число можно указать явно при использовании команды return, после чего оно будет сохранено в специальной переменной $?. Команда return прерывает выполнение функции (как и в обычных языках), а код ошибки можно рассматривать как статус выполнения.

Пример:

#!/bin/bash

my_function () {
  echo "some result"
  return 55
}

my_function
echo $?

Вывод этого скрипта:

some result
55

Чтобы по-настоящему что-то вернуть из функции надо использовать что-то другое. Самый простой вариант - присвоить что-то глобальной переменной:

#!/bin/bash

my_function () {
  func_result="some result"
}

my_function
echo $func_result

Вывод этого скрипта:

some result

Есть способ получше. Отправить требуемое возвращаемое значение на вывод (при помощи echo или printf, например):

#!/bin/bash

my_function () {
  local func_result="some result"
  echo "$func_result"
}

func_result="$(my_function)"
echo $func_result

Вывод этого скрипта:

some result

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

Переопределение команд

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

Пример:

#!/bin/bash

#overriding command
echo(){
    builtin echo "The name is : $1"
}

echo "Edward III"

Вывод этого скрипта:

The name is : Edward III

SSH через прокси

Для работы ssh через прокси используется может использоваться ряд программ:

Для того, чтобы подключаться через прокси ко всем или выборочным серверам надо прописать в файле config (~/.ssh/config) соответствующие настройки. Там можно настроить и другие параметры: имя пользователя, порт и т. п.

Работает и под Windows тоже (proxytunnel уж точно):

Пример:

Host имя_хоста1
  ProxyCommand connect -H 192.168.25.2:3128 %h %p

Host имя_хоста2
  ProxyCommand corkscrew 192.168.25.2 3128 %h %p

Host имя_хоста3
  ProxyCommand proxytunnel -q -p 127.0.0.1:8081 -d %h:%p -H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)"

Копирование метаданных с одного mp3 в другой (ffmpeg)

Файл mp3copy_id.bat следующего содержания:

@echo off

:: имя второго файла без пути и расширения
set F=%~n2
:: расширение второго файла
set E=%~x2

ffmpeg -i "%1" -y -f ffmetadata _metadata.txt

ffmpeg -i "%2" -f ffmetadata -i _metadata.txt -c copy -map_metadata 1 "%F%.1%E%"

del _metadata.txt

Запускать так:

mp3copy_id <src_mp3_file> <dest_mp3_file>

Скрипт создаст третий файл (медиа скопировано со второго, метаданные с первого). Имя файла будет такое же как у второго, но с добавленной .1 в имя файла.

QEMU run

Скрипт для запуска qemu с типовыми ключами (и образом CD в качестве опционального параметра).

#!/bin/bash

# Имя файла образа жесткого диска
IMG=./img/main_disk.img

if [[ "$OSTYPE" == "linux-gnu" ]]; then
  mkdir -p tmp
  CMDLINE=qemu-system-x86_64
  OPTS="-cpu qemu64 -enable-kvm -cpu host -hdb fat:rw:./tmp"
else
  CMDLINE=qemu-system-x86_64
  OPTS="-cpu qemu64"
fi

if [ $# -gt 0 ]; then
echo "external iso as cdrom $1"
CDROM=-cdrom $1 -boot d
else
CDROM=
fi

$CMDLINE \
    -hda $IMG \
    $CDROM $OPTS \
    -m 8192 \
    -vga cirrus \
    -net nic,model=rtl8139 \
    -net user \
    -usbdevice tablet \
    -vga virtio

Для начального создания образа диска можно использовать:

qemu-img create -f qcow2 main_disk.img 20G

Если надо упаковать каталог tmp в iso-образ, например, чтобы подсунуть в качестве cdrom при запуске qemu, то сделать можно так:

mkisofs -R -J -o dat.iso tmp/*

Telegraph ссылка авторизации

Введение в суть проблемы

В сервисе публикаций telegraph в явном виде авторизации нет. В неявном оно, конечно, есть (иначе было б невозможно редактировать ранее написанное), но хранится исключительно в cookies браузера. Грубо говоря, из другого места не получится редактировать, да и с этого ровно до тех пор пока не очистишь cookies.

Конечно, всегда можно тупо скопировать картинки и текст и создать новый пост в telegraph, но это как-то не то...

Разработчики предлагают частично решить проблему при помощи telegram-бота @Telegraph. Бот @Telegraph позволяет авторизовываться в Telegraph через учётную запись Telegram. Фактически он авторизует браузер, а так как telegram можно запускать на разных устройствах, то можно авторизовать (через этого же бота) браузеры на разных устройствах и это будет один и тот же аккаунт.

Проблема решена! Но не совсем... Бывает нужно авторизовать браузер другого (удаленного) человека. Или где-то нельзя (или не хочется) запускать telegram...

Для этого случая предусмотрено формирование ссылки авторизации, которая действует (суля по описанию) 5 минут, и по ней можно авторизовать браузер. Сложности начинаются с того, как её сформировать. Бот почему-то не умеет (зато закрывать лишние сеансы/сессии бот прекрасно умеет).

А ещё у telegra.ph есть web-api и там авторизация ведётся по access_token. То есть, базовым элементом авторизации является access_token, только явно его не показывают.

Пора сводить всё это вместе и решить уже проблему генерации ссылки.

Получение токена

Обычно все описания пишут, что надо создать аккаунт и тогда API вернет токе нового аккаунта. У нас же задача подцепить уже существующий аккаунт.

Для начала надо иметь авторизованный (лучше через бота) браузер.

В браузере надо открыть средства разработчика, загрузить https://telegra.ph и из Application/Cookies скопировать tph_token (набор букв и цифр).

Получаем ссылку авторизации

Можно использовать curl, можно использовать python.

Использование curl

После этого можно (уже на любом устройстве, не обязательно авторизованном) сделать запрос ссылки для авторизации. Под Windows так:

curl "https://api.telegra.ph/getAccountInfo?fields=%%5B%%22auth_url%%22%%5D&access_token=<token>"

Под bash так:

curl "https://api.telegra.ph/getAccountInfo?fields=%5B%22auth_url%22%5D&access_token=<token>"

где вместо <token> следует вписать скопированный токен.

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

Список постов можно посмотреть так:

curl "https://api.telegra.ph/getPageList?access_token=<token>"

Вообще есть Описание telegra.ph API и там можно, например, выводить статистику просмотров...

Использование python

Установить пакет telegraph ( вот описание ).

pip3 install telegraph

Создать простой скрипт для получения ссылки авторизации (токен тоже понадобится).

from telegraph import Telegraph

telegraph = Telegraph(access_token="<token>")
r = telegraph.get_account_info(fields=['auth_url'])
print(r)

MTProxy debian

Установка MTProto-proxy в Debian

Исходная информация взята отсюда и отсюда.

Зависимости для сборки

apt install git curl build-essential libssl-dev zlib1g-dev

Исходный код

Можно вытащить в любой каталог любого пользователя.

git clone https://github.com/TelegramMessenger/MTProxy.git

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

cd MTProxy

Сборка

Для сборки просто выполнить команду:

make

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

Установка

Будем устанавливать в каталог /usr/local/bin. Для этого надо скопировать и на всякий случай выставить разрешения на выходной файл:

cp objs/bin/mtproto-proxy /usr/local/bin/
chmod 777 /usr/local/bin/mtproto-proxy

Возможно эти действия следует выполнить от имени root.

Создание файлов конфигурации

Делать от имени root.

mkdir /etc/mtproto-proxy

Скачать файлы для подключения к серверам Telegram:

curl -s https://core.telegram.org/getProxySecret -o /etc/mtproto-proxy/proxy-secret
curl -s https://core.telegram.org/getProxyConfig -o /etc/mtproto-proxy/proxy-multi.conf

Сгенерировать секретный ключ для подключению к MTProto Proxy:

head -c 16 /dev/urandom | xxd -ps

Сам ключ будет выведен в консоль и он понадобится позже. Его надо будет вставлять вместо слова SECRET ниже.

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

Для проверки надо бы запустить:

/usr/local/bin/mtproto-proxy -u nobody -p 8888 -H 443 -S SECRET --aes-pwd /etc/mtproto-proxy/proxy-secret /etc/mtproto-proxy/proxy-multi.conf -M 1 

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

Если вдруг вылетает ошибка про недопустимый PID что-то наподобие:

mtproto-proxy: common/pid.c:42: init_common_PID: Assertion '!(p & 0xffff0000)' failed.

В этом случае можно попробовать ограничить PID на уровне системы. Для этого от имени root выполнить:

echo "kernel.pid_max=65535" > /etc/sysctl.conf
sysctl -p

На виртуалке это, скорее всего, не сработает, так как на установку параметра kernel.pid_max будет выдана ошибка permission denied.

В этом случае придется править исходник, перекомпилировать и снова копировать выходной исполняемый файл в /usr/local/bin

В исходнике common/pid.c в районе строки 42 закомментировать строку:

assert (! (p & 0xffff0000));

После этого пересобрать и переустановить как сказано выше в разделах Сборка, Установка.

Автоматический запуск

Надо создать unit-файл для systemd /etc/systemd/system/mtproto-proxy.service:

[Unit]
Description=MTProxy
After=network.target
[Service]
ExecStart=/usr/local/bin/mtproto-proxy -u nobody -p 8888 -H 443 -S SECRET --aes-pwd /etc/mtproto-proxy
Restart=on-failure
[Install]
WantedBy=multi-user.target

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

-H 443 это порт, на котором будет работать прокси. Его можно задать любым допустимым, не обязательно 443. Но надо убедиться (и настроить при необходимости), что пакеты на этот порт не режутся firewall-ом.

-p 8888 - локальный порт для просмотра статистики. Доступен только с самого сервера командой типа wget localhost:8888/stats.

Перезагружаем systemd:

systemctl daemon-reload

Запускаем службу и проверяем нормально ли она запустилась:

systemctl restart mtproto-proxy
systemctl status mtproto-proxy

Если всё ок, то добавляем в автозагрузку:

systemctl enable mtproto-proxy

Автоматическое обновление конфигурации

Можно настроить обновление конфигурации по планировщику. Для этого от имени root выполнить:

crontab -e

Сформировать cron-файл (в примере обновление происходит в 3:00 каждый понедельник):

0 3 * * 1  curl -s https://core.telegram.org/getProxyConfig -o /etc/mtproto-proxy/proxy-multi.conf && systemctl restart mtproto-proxy.service

Настройка клиента

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

Настройки находятся в меню Настройки/Данные и память/Настройки прокси.

Можно применить настройки прокси автоматически открыв специального вида ссылку:

tg://proxy?server=<IP сервера>&port=<ПОРТ>&secret=<СЕКРЕТ>

добавка к секретному ключу

Говорят:

UPD 24.02.2019
Что бы прокси было сложнее обнаружить, в клиенте есть возможность 
динамически изменять размер пакета, добавьте в начале ключа две 
буквы dd

Должно получится так dd575b04f4b28f72263965d1d371707c06

Если примут коммит то в самом прокси mtproto можно будет 
запретить подключение без dd флагом "-R"