Іграшка 1-ша: CLI кліент для DW. Користуюсь десь з місяць, ніби працює.
( Read more... )Іграшка 2-га: транслітератор з російської в українську
( Read more... )Іграшка 1-ша: CLI кліент для DW. Користуюсь десь з місяць, ніби працює.
( Read more... )Іграшка 2-га: транслітератор з російської в українську
( Read more... )https://sigwait.gitlab.io/les_podervyansky--plays/
Коли 2 роки тому, мені закортіло перечитати п'єси ЛП, я не спромігся їх знайти у нормальному epub. Тоді я зробив свою компіляцію і розповів про це десь у LJ. Не знаю чому, але the resulting epub/pdf найбільше скачували з Росії. Навіщо їм там Подерв'янський?
На останній сторінці epub'у я попрохав надсилати мені erratum. За 2 роки це наважилася зробити аж 1 людина з Чехії. Сьогодні я, нарешті, знайшов сили epub обновити.
( Read more... )Майкрософт вперше додали механізма autounattend.xml
за часів
захоплюючих краєвидів Вісти. Користувач завантажував те що зараз
називається ADK віндюковий, який містив графічну ютіліту WSIM, яка
випльовувала xml-файла. Останній клавсь до кореня usb-драйву і віндюка
(ув ідеалі) інсталювався без втручання хомо сапієнса.
Нічого революційно нового ув тому не було--лайнакси схожі механізми мали багато років, наприклад kickstart ув Федорі.
Прублема ADK є ув тому, що він потребує інстальованої ОС, а способу
генерації autounattend.xml
з-під ОС конкурентів Майкрософт не надає,
окрім цінних порад писати його ув текстовому редакторі вручну (дуже
дякую).
Не слідкуючи за їх цирком на дроті, деякий час назад я почув, що з винахідливим змістом того .xml можна отримати інсталяцію віндюка без сучасних та корисних аплікації типу коупайлота або ноутпеда (модерн) чи аутлука (н'ю), та без необхідності ув танцюванні гопака для створювання 'офлайнового' користувацького акавнту.
Різноманітні онлайнові генератóри autounattend.xml
змінюються як
пори року. Поточним, працюючим, популярним (ППП) залишається
дойчландський unbeaufsichtigter (вимовляйте це самі)
generator.
На цьому пригоди лише починаються:
Якщо потрібен .iso з доданим autounattend.xml
для створення
мошини віртуальної, то як модифікувати оригінальний .iso? Всередені
там є хфайлова система udf, яку змаунтувати можна тільки read-only.
Якщо треба інсталювати віндюка на справжнього комп'ютера (ви не повірите, але такі випадки трапляються непоодиноко), то як створити з оригінального .iso завантажувальний usb?
Нагадаю: ми ув лайнаксі, де жодні rufus'и працюють не тут. Різноманітні ютіліти різної ступені занедбаності або підтримують лише .iso лайнаксних дістро, або написані ув найкращому випадку школярами, ув найгіршому--вініпухами чи руснею (маґа-патріот-джон с тексастской області лєнінґрадскоґо района). Ув 2х останніх різновидах є гарний шанс отримати гарного .iso с безоплатним подарунком всередині.
З найсмішніших варіянтів що я бачив, кількість залежностей є така, що аплікацію, разом з залежностями, завертають ув appimage як ковбасу.
Rufus'и та друзі для режиму UEFI роблять нічого складного: на usb-дівайсі створюється дві GPT партішони:
До 1ї переписують бутлоадера (зазвичай 'універсального', який може вантажити декілька різних ОС). До ntfs копіюють вміст .iso.
Ув нашому варіянті майкрософтський .iso має свого бутлоадера, тому скопіювати можна його. Головний трюка є ув правільному створені партішонів: (1) fat32 не повинен бути з позначками esp та boot, інакше віндюковий інсталятора вирішить що саме на usb-драйві буде EFI розділ для ОС, хоча останню він ставить до геть іншого драйва; (2) без позначки msftdata на ntfs партішоні віндюка кидає "Install driver to show hardware" даялога, а її diskpart малює його як "Unknown". Вкрай неввічливо.
$ parted test.img print | grep -v ^Disk
WARNING: You are not superuser. Watch out for permissions.
Model: (file)
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Number Start End Size File system Name Flags
1 1049kB 1074MB 1073MB fat32 BOOT msftdata
2 1074MB 7516MB 6442MB ntfs INSTALL msftdata
Наївний скрипта, який це втомлююче робить, на жаль, вимагає рута через створення loop devices, але не чіпає хадварних драйвів:
$ sudo ./winiso2img Win11_24H2_English_x64.iso test.img
test.img
потім можна перенести на usb-драйв будь-яким улюбленим
способом, наприклад:
$ sudo dd if=test.img of=/dev/XXX status=progress bs=1M oflag=direct,sync
Ув принципі, щоб згенерувати хфайла з потрібною таблицею партішонів можна обійтися без losetup+parted, а взяти sfdisk, але як потім змонтувати партішони всередині test.img звичайним користувачем я не знаю.
Майкрософтські .iso зроблені з хфайлової системи udf, а крихітний шар iso9660 там містить лише хфайла README.TXT з повідомленям про skill issue читача.
Лайнакс знає про udf, але маунтить оті .iso лише ув read-only.
Спочатку я хотів взяти 7-zip. Розаркування великого аркайву (> 5GB)
займає час навіть на сучасних мошинах, тому бажано повідомляти
користувачу що коїться. 7-zip друкує лінію стану розаркування, але
робить це ув найворожіший спосіб. По-1ше, автор 7-zip'у вважає що \r
(carriage return) то є буржуазноє ізлішєство. Ось фрагмент
збереженого аутпуту:
00000000: 2020 304d 2053 6361 6e20 2f68 6f6d 652f 0M Scan /home/
00000010: 616c 6578 2f44 6f77 6e6c 6f61 6473 2f77 alex/Downloads/w
00000020: 696e 2f69 736f 2f77 3131 2f08 0808 0808 in/iso/w11/.....
00000030: 0808 0808 0808 0808 0808 0808 0808 0808 ................
00000040: 0808 0808 0808 0808 0808 0808 0808 0808 ................
00000050: 0808 0808 0808 2020 2020 2020 2020 2020 ......
00000060: 2020 2020 2020 2020 2020 2020 2020 2020
00000070: 2020 2020 2020 2020 2020 2020 2020 2020
00000080: 2008 0808 0808 0808 0808 0808 0808 0808 ...............
00000090: 0808 0808 0808 0808 0808 0808 0808 0808 ................
000000a0: 0808 0808 0808 0808 0808 0808 2020 3025 ............ 0%
000000b0: 2031 3030 204f 7065 6e08 0808 0808 0808 100 Open.......
000000c0: 0808 0808 0808 2020 ......
Воно видаляє рядок за допомогою циклу з \b
(backspace), потім друкує
пробіли щоб видалити їх знову.
По-2ге, воно відмовляється малювати лінію стану коли stdout не є терміналом. Зі script(1) це можна побороти і, з деяким зусиллям, я змусив аутпут від 7z бути зумісним з dialog(1) gauge, але після проглядання 7-zip src, огида до нього у мене переросла бажання його торкатися. Це 1 з найгірших src, які я бачив. То є диво що воно працює взагалі, а кількість CVE починаючи з 2022 наводить на конспірологічні думки які я тут писати не буду.
Якщо ігнорувати 7-zip, як тоді модифікувати змонтований read-only .iso? Можна скопіювати все ув іншу директорію, але існує більш шляхетний спосіб: overlayfs.
З ним гагібайти хфайлів не копіються, але метод вимагає рута. FreeBSD ніби мала раніше щось схоже (unionfs), але коли я користувався fbsd, воно часто працювало з помилками.
Після отримання директорії з autounattend.xml
хфайлом, її завертають
ув .iso знову.
Скрипта,
який це робить, є на 24 рядки більший за попередній winiso2img
:
$ wc -l *
67 mkwiniso
91 winiso2img
158 total
$ sudo ./mkwiniso add lol.xml Win11_24H2_English_x64.iso test.iso
Якщо просто треба з розаркованого Майкрософтського .iso створити новий, рута є непотрібний:
$ ./mkwiniso generate dir 1.iso
712 безглуздих слів про "renewing the Brand".
Дякуючи Зевсу, Гері та іншим богам, ЕйАй, який нищить Stack Exchange, вміє перетворювати воду на сенс:
"Stack Exchange is launching a rebrand because they think their identity is a mess, and it’s hurting their business. Instead of fixing real problems, they’re wasting time and money on consultants and vague “brand strategy” to make everything look more polished while AI eats their lunch. They say they want community input, but this is corporate theater."
З безтурботних часів кінця сивочолого криголама (що мені не подобалось?), для знаходження діректових лінків на .iso, Майкрософт кілька разів змінювали внутрішній ойпіай, загалом, потреба ув якому як була так і залишається незрозумілою, якщо не рахувати необхідність свіжих булітів кожного end-of-year рів'ю.
Щоб завантажити 24h2, треба отримати ойді сесії та список мов, за якими Майкрософт подарує згенерований url ув джейсоні. Як робити найостанніший ріквеста забагато разів на годину (наприклад, нахабно 5 разів), Майкрософт зобанить. Покласти всі .iso на статичного сервера не можна--це буде глузування над здоровий глуздом, потрібна Процедура.
$ grep -Ev '^#|^$' windows-iso-url | wc -l
28
Скрипта вимагає nokogiri.
Спостерігаючи за бійкою між Поттерінґом та термінальними пуристами, я дізнався про таке собі XTGETTCAP. Це є механізма (відносно новий, ~2019?) для отримання можливостей терміналу без консалтінґу terminfo. Це означає, що брехати про тип терміналу з XTGETTCAP не вийде.
Наприклад, якщо хочеться намалювати користувачу саме монохроматичного інфобокса, зараз можна підсунути назву терміналу, для якого ув terminfo зазначено безбарвне життя:
$ TERM=vt100 dialog --infobox 'ну шо ти малá' 0 0
Це працює тому що даялоґа використовує ncurses, який дивиться ув
terminfo. Якби даялоґа стріляв ув термінала XTGETTCAP'апом, замість
читання змінної середовища TERM
, такий хфокус би не спрацював:
$ echo $TERM
xterm-256color
$ TERM=vt100 ./XTGETTCAP TN
xterm-256color
Якщо ув теорії XTGETTCAP--майбутнє вже сьогодні, то на практиці цей
механізма мені не подобається. Можливо in-band signalling було
надією людства ув 1970ті, але зараз щоб імплементувати XTGETTCAP запита
та прочитати результата, треба перемикати термінала ув raw режим,
конвертувати ім'я capability ув hex строку і писати до tty обгорнувши
її з \e
(ascii 033 ув октал) з обох боків, поряд з іншим мотлохом:
printf '\033P+q%s\033'\\ "$capablity" > "`tty`"
Що для "TN" виглядає як
00000000: 1b50 2b71 3534 3465 1b5c .P+q544e.\
Потім треба обережно прочитати результата все ще знаходячись ув raw режимі. Якщо робити це з акуратністю, з якою нарід пише скрипти шелóві, вірогідність того, шо термінал буде заблоковано перманентно, наближається до 1.
Для wayland'у є емулятор терміналу foot, який має окрему C ютіліту для читання XTGETTCAP, але вона наглухо висне, якщо термінала не розуміє запиту. Як можна сподіватися на здібності гіпадріла звичайного, якщо це не можуть порядно зробити навіть люди, які пишуть термінального емулятора.
Окрім xterm (який вимагає увімкненого allowTcapOps
, який вирублений
по замовчуванню на всіх ОйБіЄм'івських дістро через цирка на
дроті),
найбільше просунулося у цьому напрямку індійське диво kitty (на яке я
маю алергію після стількох років страждань з calibre (автор той
самий)), та усілякі ghostly та ін. штукенції, якими користуються 12
людей на планеті земля. iterm2 та діфолтне еплівське одоробло також ніби має
підтримку, але мені є лінь перевіряти.
Найсумніше є те, що XTGETTCAP наразі не працює через gnu screen чи tmux, що хоча є вина останніх, популярності XTGETTCAP не додає.
Скрипта, яким можна тестувати:
#!/bin/sh
set -e
eh() { echo "Error: $*" 1>&2; trap - 0; exit 1; }
text2hex() { od -A n -t x1 | tr -d ' \n'; }
hex2text() { sed 's/../0x& /g' | xargs printf '\\\\%03o\n' | xargs printf %b; }
stty_orig=`stty -g`
stty_restore() { stty "$stty_orig"; }
[ -n "$1" ] || eh Usage: XTGETTCAP capability
capablity=`printf '%s' "$1" | text2hex`
trap stty_restore 0
stty -echo raw time 1 min 0
printf '\033P+q%s\033'\\ "$capablity" > /dev/tty
buf=`dd status=none count=1`
stty_restore
[ -n "$buf" ] || eh unsupported terminal
[ "$V" ] && printf %s "$buf" | xxd
buf=`printf %s "$buf" | sed 's/[^a-zA-Z0-9+=]//g'`
[ P1 = "${buf%+*}" ] || eh unknown capabilitah
printf %s "${buf#*=}" | hex2text | xargs
Повинно працювати з пацаватими dash, busybox sh та ін.
hex2text()
тут не фонтан, звичайно, але нопешіть як то можна інакше,
щоб (а) було сумісно з fbsd, (б) не вимагало чогось ікстернального, як
xxd.
$ ./XTGETTCAP TN
xterm-kitty
$ V=1 ./XTGETTCAP colors
00000000: 1b50 312b 7236 3336 6636 6336 6637 3237 .P1+r636f6c6f727
00000010: 333d 3332 3335 3336 1b5c 3=323536.\
256
$ ./XTGETTCAP шо?
Error: unknown capabilitah
Ув вікіпідіа написано шо хадварні термінали VT100 (продавалися з 1978 року) та VT220 (1983) були монохромні (кольорові варіянти, напевно, лише для заможних верств гіпадрілів). Terminfo ув лайнаксі про ці термінали містить ту саму інформацію:
$ for i in dumb vt100 vt220 screen xterm $TERM; do printf "%-20s" $i; TERM=$i tput colors; done
dumb -1
vt100 -1
vt220 -1
screen 8
xterm 8
xterm-256color 256
Кілька днів тому, нєкто Поттерінґ, мастодонів про діфолтне значення
TERM
, яке обирає systemd:
'The default fallback $TERM we so far picked has been "vt220" … This deemed us to be a good choice, since (unlike the better known vt100) these kind of terminals support PageUp/PageDown keys'
Загалом, ув terminfo дивляться зазвичай тільки ютіліти які для користувацького інтерхфейсу використовують ncurses; решта вгадує можливості терміналу шляхом слідкування за візерунками руху небесних тіл, e.g.:
COLORTERM
(нещодавно обраний шлях systemd);NO_COLOR
(я би віддав перевагу, навпаки, щось на
кшталт COLOR=1
, бо діфолтне плювання кольорами ув консолі є
страшенний несмак);TERM
і шукати збіг ув своїй вкомпайлений (не жарт)
табличці, як то робить ls з coreutils;Лайнаксний ls, звичайно, перемагає ув конкурсі найдебільнішого рішення.
Воно друкує барвами для vt100, тому що протягом компіляції coreutils хапається текстовий хфайла з налаштунками для dircolors(1), який має ось такі рядки:
TERM vt100
TERM xterm*
Цей хфайл (його копія зазвичай є ув /etc/DIR_COLORS
) скрипта
перловий конвертує ув символьного масива
$ head -c7 src/dircolors.hin # ув ріпо coreutils
# Confi
$ (head -c61 && printf 🤡 && tail -c39) < <(src/dcgen src/dircolors.hin)
static char const G_line[] =
{
'#',' ','C','o','n','f','i',🤡,'r','i','a','b','l','e','s','.',0,
};
який потім інклудиться як dircolors.h
ув ls.c
, де є хфункція
/* Check if the content of TERM is a valid name in dircolors. */
static bool known_term_type(void) {
char const *term = getenv("TERM");
if (!term || !*term)
return false;
char const *line = G_line;
while (line - G_line < sizeof(G_line)) {
if (STRNCMP_LIT(line, "TERM ") == 0) {
if (fnmatch(line + 5, term, 0) == 0)
return true;
}
line += strlen(line) + 1;
}
return false;
}
dircolors(1) також парсить нещасну G_line
та друкує 0 інструкцій для
ls, коли TERM
є відсутня чи несе невідоме значення:
$ env -i SHELL=/bin/sh dircolors
LS_COLORS='';
export LS_COLORS
У чому сенс тоді, навіть за відсутності LS_COLORS
, розмальовуювати
множинні character devices з директоріями (довгий масив
color_indicator[]
ув ls.c
, якщо комусь цікаво), але полишати файли
звичайні (кольори для яких G_line
має), залишається невідомим.
Найвизначнішою рисою істот з СНД, навіть якщо останнє вони покинули фізично, залишається гарантована compassion towards victims.
Приклад типовий: https://stas.dreamwidth.org/1468036.html разом з коментарями.
Бовзер Кроум має інтернáльну сторінку chrome://gpu/
, яка малює,
поміж інших речей, статуса хадварних відеокодувальників. Якщо драйвер
gpu не опиняється ув блеклисті (це слово можна знову
використовувати?), ближче до кінця там з'являється таблиця з рядками
Decode hevc main 64x64 to 8192x4352 pixels
З інтернáльною сторінкою пов'язано багато смішного. Наприклад, одного разу на безмежні узбережжя скелястих форумів гоогла викинуло монголку:
"Добрий дєнь! вопрос про страніцу chrome://gpu - у мєня поддєржка ЯндєксКарти запросіла пріслать содєржімоє єтой страніци. Єто что, вообщє? Єто бєзопасно? Я вставіла єє в адрєсную строку і получіла коллапс какой-то - цвєтниє полоси, клінья, сєґмєнти і прочую бєлібєрду, послє чєґо всє откритиє страніци інтєрнєта пошлі "плясать" - содєржімоє пєрєкорєжіло. Вірус, что лі?"
Раніше, якщо треба було дивитися на окремі відеофрейми, брався src відеокодеку на C та відчайдушно компілювався ув вебасемблі. Зараз це ліпше робити ні, т.я. бовзер розуміє вдосталь відеокодеків самотужки і має новітнього ойпіай (йому 5 років) для маніпулювання фреймами--WebCodecs.
Ув лайнаксі, Кроум використовує хадварні відеокодувальникі через завдяки за допомогою VAAPI. Якщо дістро має застарілі (або занадто нові) версії Mesa з їх DRI-драйверами, відеокодувальники ув Кроумі будуть софтварними, навіть якщо GPU підтримує їх хадварну акселерацію. З ідіотськими порадами "не викидайте старі комп'ютери, а інсталюйте на них лайнакса", наївний користувач отримує гул кулерів, який був відсутній ув віндюку, де Кроум дружив з драйверами GPU.
WebCodecs має метода статичного VideoDecoder.isConfigSupported()
,
яким можна перевірити чи використовує Кроум могутню акселерацію для
конкретного кодека. З VideoEncoder
це можна
перевірити для конкретної роздільної здатності.
Наприклад, якщо пастнути текста нижче ув дівтулзову консоль, воно
надрукує трохи більш зручну табличку, аніж простирадла з
chrome://gpu/
:
Promise.all([
['AV1', 'av01.0.08M.10'],
['H.264', 'avc1.640033'],
['H.265', 'hev1.1.6.L120.90'],
['VP8', 'vp8'],
['VP9', 'vp09.00.40.08']
].map( async v => ({
name: v[0],
codec: v[1],
support: (await VideoDecoder.isConfigSupported({
codec: v[1], hardwareAcceleration: "prefer-hardware",
})).supported
}))).then(console.table)
(Звичайно шо isConfigSupported()
є червона хфункція, т.я. потрібно
нагадувати гіпадрілам постійно шо життя це є боротьба.)
На жаль, console.table()
не вертає html, тому щоб написати
уйоб-сторінку, яку зручно відкривати на SBC з ондроїдом або кетайських
тівібоксах, треба малювати все самому.
Мінімальний приклад (працює лише ув Кроумі; Файрфокс видає галімат'ю):
$ wc -l *html
47 index.html
є отут (вибачте що не Реакт). Ув якості домашнього завдання,
додайте вибір варіянтів baseline/main/high кодеків з <select>
.
Я уявляю це собі так: ув старі добрі часи для швидкого анкетування
можна було пнути хропучого сисадміна, який би написав html форму та
елементарний сервер (на його улюбленій мові), що з'їдав би post
ріквеста і складав результат ув якійсь директорії. Після
чого сисадмін вертався до своєї комірки сопти далі, а ікзéк'ютів
починав спамити колег адресою
172.20.1.15/surveys/job-satisfaction.html
.
Тобто, як то воно була насправді, я гадки не маю. 10+ років тому, коли потрібні були анкети, я робив їх ув гоогл хформах і читав результати ув гоогл шітс. Зараз, звичайно, варіянти коливаються від $0/mo (вся дейта вільно продається кому завгодно) чи $199/mo за "1 active project" з "10K сабмітів" (вся дейта дістається русскім з рансомware угрупувань) до селф-хостинґу з хропучим дівопсом фултайм ув комплекті (вся дейта губиться, коли дівопс звільняється).
Чомусь останній варіянт з селф-хостинґом завжди виглядає як конкурс мошин Руба Голдберґа, хоча такий "проєкта" має бути на рівні можливостей будь-якого школяра. 2 файли ув найпростішому вигляді: form.html та server.js. Ні?
server.js містить статичного http сервера. На GET /simplest
шукає
public_html/simplest/index.html
та виставляє сесію ув cookie. Автор
анкети пише simplest/index.html
руками ув текстовому
редакторі. Найпростіша анкета:
<form method="post">
Name? <input name="name" type="text" required>
<input type="submit">
</form>
Це є класичний postback, тобто POST ріквест форма відсилає на той
самий pathname за яким було GET сторінки. Ув даному випадку це
дозволяє з URL викопирсати ім'я анкети. Ув директорії
public_html/simplest/
можуть бути будь-які файли, які треба для
рендеренгу екстравагантної форми: світлини, відео, джаваскрипт і т.ін.
Число анкет обмежується лише лімітами файлової системи. Додавати анкети можна ув ріалтайм, перезапускати сервера є непотрібно.
Якщо POST був без помилок, application/x-www-form-urlencoded конвертується ув json і ✍️ ув окремій директорії.
Бовзерна валідація хформ працює всюди > 10 років (окрім ондроїдного файрфоксу, тому що кожного року треба усім світом збирати мульйони долярів на зарплату CEO і часу на все не вистачає). Перевірку пейлоаду з post ріквеста можна робити загальнопролетарським json schema, який можна генерувати зі статичного form.html на льоту.
Є купа мотлоху який генерує html з json schema, але не навпаки: я
знайшов нічого, що оналізує якийсь <input type="text" minlength="2" maxlength="50">
і випльовує скіму, але з ноудівським cheerio можна
написати свій генератор за кілька годин. Форми не можуть бути
вкладеними одна в одну, тому складність генератора буде рости лише з
кількістю віджетів.
Наприклад, з
<input type="radio" name="os" value="linux" required> Linux
<input type="radio" name="os" value="macos"> macOS
генерується
{
"type": "object",
"properties": {
"os": { "enum": [ "linux", "macos" ] }
...
},
"required": [ "os", ... ]
}
Ванільний фронтенд не був ви фронтендом, якщо би всі стандартні
віджети працювали однаково. Якщо для множини radio кнопок
підтримується валідація "required", то для множини чекбоксів немає ані
"required", ані перевірки мінімальної кількості увімкнених прапорців
(навіщо bother, як то кажуть ув Калґарі). Я виставляв
data-required="true"
та data-min="N"
на парентному div.
Коли анкетування є номінально анонімним, auth можна пропустити і просто встановлювати cookie щоб користувач, який згадає шо він цейво, як його, забувсь, шо він хотів написати, міг якийсь час свою анкету редагувати. З
Set-Cookie: sid=2025/01/26/a8b92708-f1dc-4870-abee-907a50a10800; Max-Age=31536000; Path=/js101/
Set-Cookie: signature=3c564fa3f3b534b85fdab82deb575c2ab2af48b7; Max-Age=31536000; Path=/js101/
результати записуються ув db/js101/2025/01/26/XXXX/results.json
, які
~легко трансформуються ув csv. Без couchbase, mongo чи mariadb. Жах.
(Здогадайтеся для чого потрібна "signature".)
Анкета стає автоматично 403, якщо хфайл з формою має mtime < зараз
. Тобто, валідна анкета має обов'язково мати дату модифікації
ув майбутньому, що є трохи незвично, але звільняє від необхідності
тримати якогось конфігураційного хфайла.
Якщо над директорією з results.json запустити іншого статичного http сервера, то анкета, сімлінка якої пишеться поряд з results.json, може читати results.json і заповнювати ту саму форму результатами тяжкої роботи користувача.
Стандартних має вистачити, але як тільки закортить мінімального дайґрешона, треба готуватися до ведмежих кутів бовзерних ойпіай.
Наприклад, я хотів вертикального слайдера з підписом рисок:
Кастомний уйоб-компонента якого виглядає ось так:
<spartaforms-slider name="knowledge" min="0" max="4" value="0" required>
<span data-value="4">I'm Douglas Crockford 🧙♂️</span>
<span data-value="3"><i>Très bien</i></span>
<span data-value="2">Can code with an LLM</span>
<span data-value="1">Syntax only</span>
<span data-value="0">Nil</span>
</spartaforms-slider>
Всередині то є звичайний <input type=range>
, розвернутий на -90°; зі
<span>
генерується datalist, з якими слайдер стає схожим на
словацького термометра. Можна було би писати datalist самотужки, та
його відмовляється рендерити ie6 еплівський вебкіт.
Але <input type=range>
сидить ув shadow dom уйоб-компонента, тому
форма на сторінці його не бачить! Будь-які зміні ув атрибутах
<spartaforms-slider>
треба переносити до <input>
(і навпаки)
самому. Щоб дружити з формами існує спеціяльний ойпіай, який став з
~2023 таким популярним, що про нього користоґо є ~0, окрім скупих
речень на whatwg.org.
Якоїсь підтримки ув збереженні інпуту, набраного користувачем ув елементах форми, від бовзера чекати є марно. Доводиться перехоплювати submit івента щоб зберігати їх десь (хоча би ув localStorage) і самотужки відновлювати, коли користувач вирішує ту форму відредагувати. Все це вміють робити різноманітні фреймвоки багато років, а ванільний джаваскрипта полишає нічого, окрім дратування через витрачання часу на цю ґілімат'ю.
Загалом, екперимента визнано невдалим. Що здавалося елементарним 1+N файловим рішенням (де N--кількість анкет), ув реальності з 3ма анкетами виросло на
$ tree --gitignore -I test --dirsfirst
.
├── public_html
│ ├── eng210
│ │ ├── form.css -> ../form.css
│ │ ├── form.js -> ../form.js
│ │ ├── index.html
│ │ └── widgets.js -> ../widgets.js
│ ├── js101
│ │ ├── form.css -> ../form.css
│ │ ├── form.js -> ../form.js
│ │ ├── index.html
│ │ └── widgets.js -> ../widgets.js
│ ├── simplest
│ │ └── index.html
│ ├── 60438.svg
│ ├── favicon.ico
│ ├── form.css
│ ├── form.js
│ ├── index.html
│ ├── posted.html
│ └── widgets.js
├── Makefile
├── mkschema.js
├── package.json
└── server.js
з малонадихаючим розміром джаваскрипта:
$ wc -lc *js public_html/*js
196 5260 mkschema.js
249 8322 server.js
136 4193 public_html/form.js
101 2906 public_html/widgets.js
682 20681 total
Приклад анкети є ось тут. Чи має сенс цей ковгосп викладати на ґітгаб, я не знаю.
Such is human stupidity that whatever is difficult
to obtain is always thought to be better.
-- Peter Martyr d'Anghiera, 1524.
Колись давно, років 8 тому, ув часи сивочолого гетьмана і кволих реформ мусоріату, на HN з'явився лінка на http-сервера розміром 1KB. Не src того сервера був 1KB, а його статично зкомпільований байнарник. Все що той сервер вмів робити це відповідати на будь-який ріквест 1 файлом. Я проявив 0 інтересу, тому що написано воно було на asm/C ув пропорції 60/40.
Ідея такого розміру програм сподобалася французькому автору haproxy і він вмовив нарід ув LKML погодитися закомітити окрему бібліотеку для написання "nolibc" аплікацій. Прикладом такої аплікації є rcutorture--тестування кернела--де генерується невеличкий initrd, написаний на C без використання будь-якої libc.
Іронія ув необхідності спеціяльної бібліотеки щоб не використовувати стандартну бібліотеку нагадує спробу обійти коло, рухаючись по його контуру.
На тему < 1KB байнарників є класичний текста Really Teensy ELF Executables, але у ньому йде мова про асемблера, на який я не бажав звертати уваги ув минулому і на який не бажаю звертати уваги зараз.
Чи можна створювати крихітні (байнарі-вайз) аплікації без асемблеру на
C без стандартної бібліотеки? Майже пусту main()
лінкер--лінкує, але:
$ cat 42.v1.c
int main() { return 42; }
$ clang -Os 42.v1.c -o 42.v1.o -c
$ ld.lld -e main -o 42.v1 42.v1.o
$ ./42.v1
Segmentation fault (core dumped)
Виявляється, осемблер потрібен щоб кликати сисколи лайнакса:
$ cat 42.v2.c
static inline long syscall(long NR, long arg1) {
long r;
__asm__ volatile("syscall" : "=a" (r) : "a" (NR),
"D" (arg1) : "rcx", "r11", "memory");
return r;
}
int main() { return 42; }
void _start() { syscall(60, main()); }
$ clang -Os 42.v2.c -o 42.v2.o -c
$ ld.lld -e _start -o 42.v2 42.v2.o
$ strip -s 42.v2
$ strace ./42.v2
execve("./42.v2", ["./42.v2"], 0x7ffc7190b9a0 /* 105 vars */) = 0
exit(42) = ?
+++ exited with 42 +++
Ага! Такий статичний байнарник має розмір 800 байт і залежить від 0
бібліотек. Номер сисколу (60) для exit можна подивитися ув файлі
arch/x86/entry/syscalls/syscall_64.tbl
кернельного src. На відміну
від віндюка, де ABI змінюється з щорічними "feature updates", такий
800-байтовий ікзек'ютабл буде працювати доки є жива орхітектура
x86_64.
Чи складно тоді, маючи обгортки для лайнаксних сисколів, написати елементарного TCP сервера для x86_64? Я вирішив спробувати:
$ _out/uptime-tcp-nolibc &
[1] 53161
$ nc --recv-only 127.0.0.1 1377
07:16:29 up 1 day(s) 22:14, load average: 0.19, 0.31, 0.30
Воно вертає шось схоже на команду uptime на ремоутній мошині, але не використовує ніяких екстернальних команд чи бібліотек. Якого розміру uptime-tcp-nolibc та скільки він їсть пам'яті написано ув кінці посту.
Якщо без libc немає жодного способу робити I/O чи відкривати сокета, тоді С стає фанкшонал мовою, про яку мріяли вчоні з 80х років, але, на їх невдачу, кернела набув IP стек та аналоги open(2) з write(2).
42.v2.c не вміє читати argv чи environ, а варіятивні хфункції у ньому роблять кордамп. Останне лікується
__attribute__((force_align_arg_pointer))
void _start() { ... }
З argv все є набагато гірше: якщо мета є саме отримувати поінтера на
argv, потрібен знову осемблер (~14 рядків), який я тут постити не
буду, щоб не лякати людей. Технічно, прочитати argv можна просто
відкривши /proc/self/cmdline, ув якому \0
є роздільником аргументів:
$ tr \\0 \\n < /proc/self/cmdline
tr
\0
\n
але вас засміють ув інтервебах.
Перша каменюка на шляху до сокетів лежить ув man-сторінках відповідних
libc враперів. Наприклад, bind(2) чекає на аргумента struct sockaddr *
,
опис якого ув man-сторінці ip(7) був скопійований з документації
якоїсь 4.3BSD-Tahoe 1987 року і відповідає реальності нутрощів
лайнуксного сьогодення з висоти дзвіниці Києво-Печерської
лаври. (Потрібний додатковий паддінґ' unsigned char __pad[8]
.)
Якщо libc-врапери вертають -1 на помилку та виставляють errno, то сисколи вертають errno × -1. Ув справжніх libc errno є локальна для поточного треду. Для мікімаусного nolibc серверу писати свою версію позікс тредів це як збирати гелікоптер AK1-3 на подвір'ї поміж 2ма хрущовками, тому тут errno--глобальна змінна. Як зберігати інтерхфейса, до якого звикли всі, хто коли-небудь писав мережеві майкросервіси на C, то найпростіший враппер виглядає як
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
int r = my_syscall(43, sockfd, (long)addr, (long)addrlen, 0, 0, 0);
if (r < 0) {
errno = -r;
r = -1;
}
return r;
}
Ув класичних Unix-серверах, якщо вони писалися не для inetd, конкурентна модель є форкова модель, яка вимагає прислуховуватися до SIGCHLD і чекати доки child завершить роботу, інакше після кожного ріквесту буде залишатися зомбі.
Форкова модель була популярна, тому що дозволяла серверу не лякатися сіґфолта, якщо сервер мав бага на який натрапив клайент: зупинявся лише child-невдаха, всі інші продовжували працювати.
Для встановлення колбеку на окремий сигнал лайнакс має свій rt_sigaction
сискола.
Ув аргументах той має
поінтера на struct sigaction
з дуже підступним філдом
void (*sa_restorer)(void);
Ув man-сторінках про нього написано ось таке:
'This flag is used by C libraries [тобто, нами] to indicate that the
sa_restorer
field contains the address of a "signal trampoline".'… the C library's sigaction(2) wrapper function informs the kernel of the location of the trampoline code by placing its address in the
sa_restorer
field of the sigaction structure.'
Шматок з мікімаусного врапера:
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact) {
// ...
struct kernel_sigaction kact, koact;
kact.k_sa_handler = act->sa_handler;
memcpy(&kact.sa_mask, &act->sa_mask, sizeof(sigset_t));
// ...
kact.sa_restorer = &restore_rt; // THE MOST IMPORTANT STEP
int r = my_syscall(13, sig,
(long)(act?&kact:NULL), (long)(oact?&koact:NULL),
_NSIG/8, 0, 0);
// ...
}
Звідкіля береться адреса хфункції для kact.sa_restorer
? Це є та сама
"signal trampoline", про необхідність якої попереджає лайнаксна
документація. Звичайно, можна її не виставляти, але тоді жоден з
сигналів не буде пійманий і жоден з колбеків не буде виконаний.
Спочатку я наївно написав
void restore_rt() {
my_syscall(15, 0, 0, 0, 0, 0, 0); // 15 це rt_sigreturn
__builtin_trap();
}
сигнали перехоплювалися, колбеки виконувалися,
СЄЧЄНОВ Цей гіпноз корисний дуже, У хазяйстві вєрно служить, Злакі мощно прорастають І надої возрастають!
але після завершення останніх сервер гепався через сіґфолта. На жаль, тут був потрібен осемблер знову:
__asm__(
".globl restore_rt\n"
"restore_rt:\n"
" movq $15, %rax\n"
" syscall\n"
" hlt\n"
);
Чому не працює С-версія я не знаю. Клод ЕйАй вважає вона "may modify registers or the stack layout."
Життя без snprintf--життя варварів.
-- Невідомий філософ.
Ув src кернелу є приклад мінімалістичної vfprintf на 101 рядок, але
вона не підтримує floats, які має друкувати uptime. Замість додавання
їх підтримки, ліпше пошукати справжню snprintf, яка не використовує
malloc. Така є ув pkg Федори (із усіх можливих місць саме тут) і
називається stb_sprintf-devel
. Це самотній .h
файла, який
зкомпільовується ув 18168 байт. Жахливе марнування ресурсів, але
нічого не вдіяти. Тій хфункції потрібні va_start
з друзями, але gcc та
clang мають __builtin_va_*
ув комплекті:
#define va_start(v,l) __builtin_va_start(v,l)
Після інкорпорації stb_sprintf життя заграло новими барвами.
Дізнатися час ув секундах з початку буту можна зі сисколом sysinfo, поточний час--зі gettimeofday. Останній провайдить навіть кількість хвилин на захід від Ґрінвічу! Така розкіш.
Розмір нестріпованого статичного байнарника: 21736 байт. (Я до сих пір не можу повірити як це можливо.)
$ make info
wc -lc *.[ch] | while read -r lines bytes file; do \
elf=-;\
echo $file | grep -q '\.c$' && elf=`stat -c%s _out/${file%.*}.o`;\
echo $file $lines $bytes $elf;\
done | column -t -N\ ,LINES,BYTES,ELF
LINES BYTES ELF
main.c 134 3657 5896
signal.c 76 1699 1704
signal.h 113 2649 -
snprintf.c 6 120 18168
snprintf.h 12 274 -
sockets.c 37 896 1728
sockets.h 45 1119 -
u.c 98 2152 4496
u.h 46 1353 -
uptime.c 67 2148 2128
uptime.h 6 79 -
total 640 16146 -
Якби не stb_sprintf, розмір був би < 10KB.
Але це є не найбільша цікавинка. Ось кількість пам'яті, яку їсть такий конкурентний tcp-сервер після відповіді на 100 одночасних ріквестів:
$ sudo ps_mem -p `pgrep -f uptime-tcp-nolibc`
Private + Shared = RAM used Program
36.0 KiB + 4.5 KiB = 40.5 KiB uptime-tcp-nolibc
---------------------------------
40.5 KiB
Дуже сміюся. Ув сучасному світі, якщо споживання серверу, який робить 0 складного, < 80 _мегабайт_, то це є видатний вчинок і безстрашний прояв волі, за який треба сертифікат хоноровий, медаль та грошовий бонус для походу ув підпільне казино з повіями.
Пан @vak нещодавно знайшов дотепну заміну fortune(1), де парсячи реддітовського джейсона окремого ком'юніті, можна собі ув терміналі друкувати ~свіжі жарти.
У коментарях там мументально переписали пáйфона на шелл, тому мінімальний скрипта, з доданими очікуваннями на деякі помилки, виглядає ось так:
Якщо реддіт вертає не зовсім того джейсона, який ми очікували, ми
друкуємо нічого. w3m потрібен щоб перетворювати &
і друзів на
відповідні символи.
Скрипта має 2 недоліки:
Завантажування можна закешити і перевіряти застарілість кешу на кожному запуску. jq вміє самотужки парсити окремо кожний файловий аргумента. Переписати скрипта ліпше за все як мейкфайла, тому шо лише дурень пише на баші те, шо може зробити Мейка (ніхто так не каже):
#!/usr/bin/make -sf
r := showerthoughts CrazyIdeas oneliners
update := 600
wits := /tmp/reddit-wisdom/all.txt
age := $(shell expr `date +%s` - `date -r $(wits) +%s 2>/dev/null || echo 0`)
$(shell [ $(age) -gt $(update) ] && rm -f /tmp/reddit-wisdom/*)
all: $(wits)
sort -R < $< | head -1 | w3m -dump -T text/html
$(wits): $(patsubst %, /tmp/reddit-wisdom/%.json, $(r))
jq -r '.data?.children[]?.data | "\"\(.title)\" -- \(.author)" | gsub("\\n"; " ")' $^ > $@
/tmp/reddit-wisdom/%.json:
mkdir -p $(dir $@)
echo Fetching r/$*
curl -sf 'https://www.reddit.com/r/$*/top.json?sort=top&t=week&limit=100' > $@
.DELETE_ON_ERROR:
(Ув fbsd потрібно встановити gmake та відредагувати shebang.) cowsay з lolcat'ом я прибрав. Завантажений джейсона застаріває кожні 600 секунд, але IRL краще виставити 48 годин.
$ ./reddit-wisdom
Fetching r/showerthoughts
Fetching r/CrazyIdeas
Fetching r/oneliners
"We’re constantly playing a tug of war against companies by paying YouTube to
not show ads while they pay YT to show them. " -- SillySlothySlug
$ ./reddit-wisdom
"make a law where everybody gives money to me" -- Due_Intention_2473
$ wc -l < /tmp/reddit-wisdom/all.txt
186
Найскладніше тут знайти слушні ком'юніті, які мають цікаві тайтлс постів. showerthoughts інколи буває NSFW.
FreeBSD is very compatible with standards such as POSIX.
-- FreeBSD Developer Handbook
На будь-якому лайнаксі з будь-яким sh-похідним шелом, скрипта нижче друкує hello world:
$ cat -n 1.sh
1 set -e
2
3 hello() {
4 false
5 echo hello
6 }
7
8 true | hello && echo world
$ sh 1.sh
hello
world
fbsd зі своїм /bin/sh робить потужний кєк на рядку 4:
$ sh 1.sh
$ echo $?
1
$ sh -x 1.sh
+ set -e
+ true
+ hello
+ false
$ uname -rs
FreeBSD 14.1-RELEASE-p5
Їхній sh(1) має ось таке речення ув роздлі про set -e
:
"If a shell function is executed and its exit status is explicitly tested, all commands of the function are considered to be tested as well."
Я, звичайно, не є перший хто це випадково помітив: схожі 4 скарги ув їхній баґзилі date back to 1999-2005, всі обнадійливо позначені як FIXED.
Хто і навіщо fbsd використовує для роботи, окрім контори Соні та ображених на Торвальдса русскіх, залишається незрозумілим.
Іграшка нова: маючи колекцію з NN тисяч хфото, локально описувати на лайнаксі що коїться на кожній світлині без допомоги Епла, Гоогла або распазнай-с-єйай.лох:
$ alias omglol='find -name \*txt | xargs -n50 grep -li'
$ omglol selfie
./10/IMG_20241019_204444.txt
./09/IMG_20240930_082108.txt
./09/IMG_20240930_082118.txt
./07/IMG_20240712_154559.txt
./07/IMG_20240712_154554.txt
кожний .txt файл лежить поряд з .jpg, який було відправлено до ollama.
Або для слайдшоу:
$ omglol selfie | sed s/txt/jpg/ | xargs feh
Опис можна пхати до exif UserComment, звичайно, але я не хотів модифікувати оригінали.
На жаль, з поточним ollama'овським cli прикріпляти світлини немає куди. Замість cli пропонують робити json пост-ріквести до ollama'овського ендпоінту, перед чим кожне зображення треба спочатку base64-конвертувати.
Другий недолік діфолтного cli--неможливість читати stdin. Я не розумію чому стандартний паттерна будь-якого текстового редактора 'селекнутий текста передати до stdin зовнішній ютіліти', є наразі бонусна фіча вчорашніх крипто-бро, сабскріпшона на яку вони залюбки продадуть простодушному користувачу.
Третій недолік--ігнорування ліміту вихідного тексту для деякого інпуту, для якого ніякі поради конструювання підказок не працюють, а num_predict ув api просто обрізає генерацію, що можна зробити без їх допомоги і так. Зі своїм враппером стає можливим підсумувати підсумки, на чому я і зупинився:
$ ./llm llama3.2 'summarize the following:' < VI.txt | ./llm llama3.2 'summarize the following in 40 words using the present tense:' | fmt -w50
Hernán Cortés and his men arrive on Cozumel
after rough seas delay them; they're rejoiced to
see Geronimo de Aguilar, a Christian prisoner
who's fluent in Yucatecan. Cortés sees great
potential for Aguilar as an interpreter and
prepares to proceed with his expedition.
Нещасний llm врапер--58-рядковий шелóвий скрипта (осьо є його svg), куди ув якості домашнього завдання можна додати async спінера.
Зі скриптом та невеликим мейкфайлом, процеса аналізу світлин виглядає як:
$ find . -iname \*.jpg | sed 's/\.[^.]*$/.txt/' | xargs -r -d\\n -n50 llm-image
де llm-image це:
#!/usr/bin/make -f
model := llama3.2-vision
define desc
@printf "%s\n\n" $(model) > $@
echo 'describe the image' | llm -i $< $(model) | tee -a $@
endef
%.txt: %.jpg; $(desc)
%.txt: %.JPG; $(desc)
Фасебооківська llama3.2-vision не є єдина модель, знатна описувати зображення (llava:13b, наприклад, вміє це також і працює ув 5 разів швидше), але вона здалася мені найбільш корисною ув виробництві keywords для пошуку.
Як нарід робить векторні скріншоти терміналу? Ось такі:
Я гадав є конвертори аутпуту зі script(1) ув SVG, але нічого такого не знайшов. Результат з asciinema нормально конвертується лише ув бітмапові зображення. Мотлох ansi-ту-шото на пáйфоні та расті або видає скуйовджені світлини або не розуміє ті ansi сіквенсес, що відповідають за рух курсору. Покинутим termtosvg можна генерувати купу svg, але він покладається на пáйфонівський in memory емулятора терміналу, який має купу багів.
Сучасний xterm вміє робити "SVG Screen Dump", але з наївним
позіціонуванням літер. Наприклад, рядок '$ uname
' він серіялізує як:
<g fill='rgb(60.00%, 60.00%, 60.00%)'>
<text x='2' y='17'>$</text>
<text x='22' y='17'>u</text>
<text x='32' y='17'>n</text>
<text x='42' y='17'>a</text>
<text x='52' y='17'>m</text>
<text x='62' y='17'>e</text>
</g>
Це означає, що кернінґа шрифта до уваги не береться і результат виглядає чудернацько.
Сучасний xterm також вміє робити "XHTML Screen Dump" (ув меню Ctrl + Миша-1), який гендериться ув бовзері майже ідеально (з замалим line-height). Додати необхідного стилю до .xhtml файлу ж нескладно, але як конвертнути той xhtml ув svg?
Бовзери вміють друкувати ув pdf. Кроум має headless режима, а гооглова
бібліотека puppeteer (з дебільним інтерфейсом), має Page#pdf()
метода. Нам лише потрібно вибрати розмір в'юпорту та опцію 'друкувати
background graphics'.
$ cat html2pdf
#!/usr/bin/env node
import util from 'util'
import fs from 'fs'
import puppeteer from 'puppeteer'
async function pdf(url, output, opt) {
let bowser = await puppeteer.launch()
let page = await bowser.newPage()
let print_settings = Object.assign({
path: output,
printBackground: true,
width: 1920,
height: 1080,
}, opt)
await page.emulateMediaType('screen')
await page.setUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36')
try {
await page.goto(urlise(url), { waitUntil: 'networkidle0' })
await page.pdf(print_settings)
} catch (e) {
err('Error:', e.message)
}
await bowser.close()
}
function urlise(s) {
return (/^https?:/.test(s)) ? s : 'file://' + fs.realpathSync(s)
}
function err(...msg) { console.error(...msg); process.exit(1) }
let args = util.parseArgs({
options: {
width: { type: 'string', short: 'w' },
height: { type: 'string', short: 'h' },
},
allowPositionals: true
})
if ( !(args.positionals[0] && args.positionals[1]))
err('Usage: html2pdf URL output.pdf')
pdf(args.positionals[0], args.positionals[1], args.values)
Скрипта підтримує -w
та -h
опції і конвертує будь-яку
уйоб-сторінку, а не тільки локальні файли. Якщо не виставити
user-agent, по-замовчуванню буде HeadlessChrome, на що деякі сайти
неприємно реагують.
PDF, на жаль, не буде мати розмір конь тенту, а буде містити borders (як це українською? межа?) Прибрати їх простіше всього Inkscape'ом:
$ inkscape --actions "select-all;fit-canvas-to-selection" 1.pdf -o 2.pdf
$ pdf2svg 2.pdf 1.svg
Воно автоматом перетворює текст ув path. Зменшити розмір .svg можна svgcleaner'ом.
На кроці нумеро дуе можна було би зразу конвертнути рельтат ув svg, але він тоді виходить гаргантюажного розміру.
Енівей, фінальний мейкфайла:
$ cat xterm-xhtml-to-svg
#!/usr/bin/make -f
font := monospace
$(if $(and $(i),$(o)),,$(error Usage: xterm-xhtml-to-svg i=1.xhtml o=1.svg))
__dir__ := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
$(o): $(i).dirty.svg; svgcleaner --quiet $< $@
%.nokogiri.xhtml: $(i)
ruby -rnokogiri -e 'd = Nokogiri.XML(STDIN.read); d.css("#vt100 > pre").first["style"] = "font-family: '$(call se,$(font))'; line-height: 1.2"; puts d.to_xml' < $< > $@
%.uncropped.pdf: %.nokogiri.xhtml; $(__dir__)/html2pdf $< $@
%.pdf: %.uncropped.pdf
inkscape --actions "select-all;fit-canvas-to-selection" $< -o $@
%.dirty.svg: %.pdf; pdf2svg $< $@
se = '$(subst ','\'',$1)'
.INTERMEDIATE: $(i).dirty.svg
Коли рік тому до імаксу додали інтерактивну хфункцію emoji-insert
, я
подумав що hell froze over. Окремий emoji інпут, звичайно, мають всі
ОС багато років. Ув лайнаксі це залежить від середовища стільниці, і
якщо ними (середовищами стільниці) не користуватися, то невблаганну
залізну хватку прогресу можна не помітити.
Нещодавно на хакір н'юз хтось презентував свого менхетнського проекту:
emoji-клавіятуру написану на голому XCB. На що хтось ув
коментарях зазначив
що то є вражаюче досягнення, але маючи список пар
emoji1 опис1
emoji2 опис2
…
то emoji вставляються за допомогою будь-якого динамічного меню без спеціяльної клавіятури.
Раніше таким меню була аплікація dmenu (яку я колись ламав для інкрементного аналога alt-tab для FVWM), але зараз гіпадріли користуються rofi, який є ув пекеджах будь-якого дістро:
$ ls /etc | rofi -dmenu -i
Одного rofi буде замало, треба механізма вставки тексту, який друкує rofi на stdout, до активного вікна. Ув xorg це вміє робити xdotool:
$ ls /etc | rofi -dmenu -i | xdotool type --file -
Де взяти список пар? Федора має unicode-emoji пекеджа (unicode-data ув
Деб'яні) з файлом emoji-test.txt
:
Зліва від середника там список codepoints. Наприклад, щоб отримати чорного кота 🐈⬛, треба взяти звичайного кота (0x1F408, 🐈), додати ZERO WIDTH JOINER (0x200D) та чорного квадрату (0x2B1B, ⬛).
На жаль, %b
та \Uxxxx
ув printf є непортабельні:
$ e="printf '%b\n' '\U1F408\U200D\U2B1B'"
$ bash -c "$e"
🐈⬛
$ bash -c /bin/"$e"
/bin/printf: missing hexadecimal number in escape
$ dash -c "$e"
\U1F408\U200D\U2B1B
$ busybox sh -c "$e"
\U1F408\U200D\U2B1B
$ zsh -c "$e"
🐈⬛
$ ksh -c "$e"
\U1F408\U200D\U2B1B
Але якщо перевести всі hex до int та роздрукувати кожен байт з int ув octal, це буде працювати навіть ув busybox:
$ echo $'\360\237\220\210\342\200\215\342\254\233'
🐈⬛
Файл emoji-test.txt
містить 3773 придатних для використання
emojis. Щоб не генерувати статичні пари ув якомусь my-dull-list.txt,
які будуть задавнюватися при кожному оновленні пекеджу, краще написати
швидкого шелóвого фільтра.
Look, ma--no execve()
after fork()
s:
$ cat codepoint.sh
codepoint_to_oct() {
local dec="`printf %d 0x${1:-0}`"
local b1 b2 b3 b4
if [ "$dec" -lt $((0x80)) ]; then # 1-byte
b1=$dec
elif [ "$dec" -lt $((0x800)) ]; then # 2-byte
b1=$((0xC0 | (dec >> 6)))
b2=$((0x80 | (dec & 0x3F)))
elif [ "$dec" -lt $((0x10000)) ]; then
b1=$((0xE0 | (dec >> 12)))
b2=$((0x80 | ((dec >> 6) & 0x3F)))
b3=$((0x80 | (dec & 0x3F)))
elif [ "$dec" -lt $((0x200000)) ]; then
b1=$((0xF0 | (dec >> 18)))
b2=$((0x80 | ((dec >> 12) & 0x3F)))
b3=$((0x80 | ((dec >> 6) & 0x3F)))
b4=$((0x80 | (dec & 0x3F)))
else
return 1 # out of range
fi
printf \\%03o "$b1" $b2 $b3 $b4
}
emoji_print() { printf %b "`for i; do codepoint_to_oct "$i"; done`"; }
Кросиві var assignments ув codepoint_to_oct()
написав chatgpt, після
чого Гостре Око помітив що генерувати взагалі потрібно нічого, тому що
emoji-test.txt
має для кожного codepint готовий emoji.
$ cat emoji-list.sh
: "${DB:=/usr/share/unicode/emoji/emoji-test.txt}"
grep '; fully-qualified ' "$DB" \
| grep -v 'skin tone' \
| sed -E 's/.+# ([^ ]+) E[^ ]+ (.+)/\1\t\2/'
Як відфільтровувати безцінні варіянти з кольором шкіри, список виходить дещо коротший:
$ ./emoji-list.sh | wc -l
1898
$ ./emoji-list.sh | tail -3
🏴 flag: England
🏴 flag: Scotland
🏴 flag: Wales
Щоб не викидати codepoint.sh
, над яким тяжко пітнів чатбот, можна
зробити додаткового хфінта: друкувати список не лише emojis, а велику
частку utf8:
$ ./unidata-list.sh | grep TELEPHONE
℡ TELEPHONE SIGN
⌕ TELEPHONE RECORDER
☎ BLACK TELEPHONE
☏ WHITE TELEPHONE
✆ TELEPHONE LOCATION SIGN
📞 TELEPHONE RECEIVER
🕻 LEFT HAND TELEPHONE RECEIVER
🕼 TELEPHONE RECEIVER WITH PAGE
🕽 RIGHT HAND TELEPHONE RECEIVER
🕾 WHITE TOUCHTONE TELEPHONE
🕿 BLACK TOUCHTONE TELEPHONE
🖀 TELEPHONE ON TOP OF MODEM
Довжина списку таких пар є 34,856. Джерело є текстовий хфайл
UnicodeData.txt
з unicode-ucd пекеджу ув Федорі (unicode-data ув
Деб'яні):
$ grep TELEPHONE /usr/share/unicode/ucd/UnicodeData.txt | head -3
2121;TELEPHONE SIGN;So;0;ON;<compat> 0054 0045 004C;;;;N;T E L SYMBOL;;;;
2315;TELEPHONE RECORDER;So;0;ON;;;;;N;;;;;
260E;BLACK TELEPHONE;So;0;ON;;;;;N;;;;;
Якщо генерувати списка лінійно, це буде займати кількадесят секунд. Хоча rofi вміє не чекати, а показувати рядки як вони з'являються на його stdin, процес ліпше пришвидшити:
$ cat unidata-list.sh
__dir__=$(dirname "$(readlink -f "$0")")
: "${DB:=/usr/share/unicode/ucd/UnicodeData.txt}"
sed 1,33d "$DB" | while IFS=\; read -r codepoint desc _ _ _ _ _ _ _ _ alt _; do
[ -n "$alt" ] && [ '<' = "`printf %c "$desc"`" ] && desc=$alt
printf '%s %s\n' "$codepoint" "$desc"
done | grep -Eav '^.+\s<' \
| xargs -r -d \\n -n1 -P "`nproc`" "$__dir__/codepoint.sh"
до чатботівского codepoint.sh
додати:
…
set -e
codepoint=${1%% *}; [ "$codepoint" ]
char=`emoji_print "$codepoint"`; [ "$char" ]
printf '%s\t%s\n' "$char" "${1#[0-9A-F]* }"
Якщо навіть з такою кенкаренсі все одно доводиться чекати, споглядаючи цифри прогресу ув rofi, рекомендую користуватися нормальними комп'ютерами, а не raspberry pi.
Намагаючись приготуватися до неминучого колапсу Кроума, згадав шо є бовзер Файрфокс, фундацьоне якого минулого року збирало усім світом на зарплату своєї CEO.
Чому колапсу? Кроум переходить на "новий" API для ікстеншонів (моніфест 3.0), депрікуючи "старий". За винятком Ublock Origin, всі ікстеншони у мене є самописні (99% того що має гооглівський могозин є malware; я не перебільшую). Ublock Origin працювати з "новим" API не збирається, а я маю 0 стимулу та зиску ув портуванні самописного. 1 з ікстеншонів використовує той самий API що і Ublock Origin, але для додавання/редагування http headers.
З цим ікстеншоном були 2 смішні гішторії. Його статистика плаває від 30 користувачів до раптом 900, 1200, 1900 і знову вниз до 30, а т.я. ніякої телеметрії там немає, чому їм починають користуватися на короткий час ув Азії, я не знаю. Якось після такого бурсту активності мені прийшов листа від якогось кетайця, який чемно запропонував продати йому ікстеншона за €600 (я не відповів).
Іншого разу до мене приколупася гамериканська коб'єта, у якої не працювало замовлення з якогось локального склепу, тому що її бовзер видаляв referer і вона якимось незбагненним чином знайшла того ікстеншона та вкрай наполегливо у мене розпитувала як її referer повернути взад. Я чесно намагався їй допомогти, але ув кінці здався і порекомендував встановити Файрфокса, на що вона відповіла "Foxfire [sic] works, thank you!", а я перехрестився.
Чому колапсу Кроума? Я, звичайно, трохи перебільшую, але маю сумніви, що після стількох років з інтервебом без реклами, ореал ойті та їх сімей добровільно погодиться на, та буде сумлінно спостерігати, наприклад, рекламу ув ютубі. Скільки це додасть мульйонів користувачів ФФ це є цікаве питання, але я очікую невеличкого ренесансу.
Як справи ув ФФ на лайнаксі?
Для свого кроуму він використовує gtk3, який фундацьйоне Ґноума та контора Червоний Капелюх вважає депрікейтед. (Для тих хто живе під скелею та є не ув уйоб-дівелопмент дискурсі: кроум--специфічний крос-бовзерський термін, який означає будь-які елементи gui, які не стосуються DOM'у уйоб-сторінки. Наприклад, меню з букмарками або url-бар. Відповідно, кроумлес є вікно лише з уйоб-сторінкою. Фулскрін режим F11 є прикладом кроумлес.)
Gtk3 означає 0 покращень для кроуму, т.я. дівелопмент тулкіту багато років переїхав на gtk4 та майбутнього gtk5. Старий gtk3 бажають викинути геть, але наразі через аплікації типу ФФ це зробити не вдається, хоча люди намагаються.
Наприклад, одного дня ФФ перестав показувати pointer
курсор
(використовується при наведенні на лінка). Раніше мені було все
одно--я пускав ФФ лише для перевірки рендеренґу. Виявляється це
сталосі тому, що ув діфолтний gtk3 темі загубили той курсор для
xorg. Хтось накричав на Ґноум, але їм було по-барабану, тому ФФ
спеціяльно додав widget.gtk.legacy-cursors.enabled
конфіґураційного
параметра, який по-замовчуванню є false--доля лайнаксоїдів не ув
wayland є страждати.
Але найгірша ситуація є зі шрифтами. Gtk3 підтримує fontconfig і
ФФ'шний кроум рендерить заміни згідно користувацьких
~/.config/fontconfig/fonts.conf
та налаштувань ув
~/.config/gtk-3.0/
, як того юзер очікує. Механізма рендеренґу
шрифтів ув бовзерному двигуні ФФ технічно питає fontconfig також,
але за своїми правилами, тому на практиці результати відрізняються
від Гоогл Кроуму та Майкрософтського Edge.
За часів w2k/winxp на fbsd/лайнакс копіювали віндюкові шрифти Arial, Times New Roman, Courier New, &c (така колекція називалася core fonts), і МС деякий проміжок часу дозволяла їх завантажувати легально. Копіювали всі: вільними лайнаксними шрифтами можна було катувати.
Деб'ян має core fonts ув своїх пекеджах (Федора--ні), але ті шрифти застигли ув версіях чи то w98 чи то w2k і відповідних версіях юнікоду. За цей час з'явилося вдосталь metric-compatible families і технічно навіть оновлена колекція core fonts стала непотрібна.
З /usr/share/fontconfig/conf.avail/30-metric-aliases.conf
:
Microsoft | Liberation | Google CrOS core | StarOffice |
---|---|---|---|
Arial | Liberation Sans | Arimo | Albany |
Arial Narrow | Liberation Sans Narrow | ||
Times New Roman | Liberation Serif | Tinos | Thorndale |
Courier New | Liberation Mono | Cousine | Cumberland |
Cambria | Caladea | ||
Calibri | Carlito | ||
Symbol | SymbolNeu |
Але я звик до core fonts. Не оригінальних, а свіжих, які я регулярно оновлював коли оновлювався віндюк. Ув fonts.conf достатньо було мати на кшталт
<alias>
<family>sans-serif</family>
<prefer><family>Arial</family></prefer>
</alias>
і гоогловський Кроум рендерував sans-serif Arial'ом замість лайнаксового Liberation Sans, Nimbus Sans чи DejaVu Sans.
Звісно з ФФ це не працює (навіщо). Той наполегливо ставить 1й ліпший
sans-serif шрифта, який fontconfig знаходить ув
/etc/fonts/conf.d/
. Якщо того шрифта дісейблнути ув fonts.conf, ФФ
бере наступного. На їхній баґзилі відкритий баґ висить 10й рік.
Подолати це можна тільки створивши ув /etc/fonts/conf.d/
хфайла з
ім'ям, яке буде сортуватися (in numeric order) до хфайлів з "family
substitution", наприклад, 12-msfonts.conf
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE fontconfig SYSTEM "../fonts.dtd">
<fontconfig>
<alias>
<family>sans-serif</family>
<prefer><family>Arial</family></prefer>
</alias>
<alias>
<family>Arial</family>
<default><family>sans-serif</family></default>
</alias>
…
</fontconfig>
Це було 1не з 1ших речей, що було, з 1го погляду, безнадійно зломано на ФФ у порівнянні зі Кроумом. Як то буде далі, прогнозувати не хочу, можливо є крихітний шанс що гоогл передумає з моніфестом 3.0 і переїжджати на ФФ не доведеться (шансів немає).
Сміявсь
ОйБіЕм:
'In 1964, for example, one of our branch managers staged a burlesque show at a sales conference in the Midwest.
The skit was too vulgar to believe--about an Indian village, starring the manager himself as the chief and some scantily clad models as squaws. They even had live chickens running around on the stage. At the end of the skit the manager disappeared with one of these models into a tepee, and as he turned to go into the tent, the audience saw a sign on his back that said something like "Branch Manager: I do all things for all people." Then he and the girl pulled the tepee flap closed behind them.
There were families present, and somebody who witnessed the show wrote me a letter saying, "Is this what you call IBM dignity?" So I started a big inquiry.'
(З Father, Son & Co.: My Life at IBM and Beyond Томаса Ватсона молодшого.)
Якщо набрати link checker ув гооглі, то виявиться що перевірка посилань є серйозний бізнес. Запускання скрипта, який пишуть на 1му курсі, коштує від $13 до $159 на місяць. За $13 дозволяється це робити кожні 2 тижні, а за $159 с барскоґо плєча до 1 разу на день.
Для тих хто готовий нарешті "стати професіоналом", контора зовсім іншого гатунку пропонує вдосконалені та ентерпрайзні рішення за $449 та $1249 на місяць відповідно. Тоді відкриваються потужні можливості відправляння csv-звіту до "колеги чи фрілансера".
Є статичний уйоб-сайта з 375ма сторінками, який має 241 ікстернальний лінка. Зазвичай подібні сайти перевіряють локально на рівні .md хфайлів, з яких він генерується, і ютіліти, які це роблять, працюють доволі швидко, т.я. їм не треба повзати по http зі сторінки до сторінки, створюючи графа. Про них ми говорити не будемо.
Щоб перевірити задіплойеного сайта, а не директорію з .md-хфайлами, додається прублема рекурсивного знаходження посилань та питання коли зупинятися. Спочатку, я вирішив пошукати щось готове ув пекеджах Федори: якийсь пáйфонівський скрипта linkchecker, який за майже 4 хв на локалхості і рівнем рекурсії 20, знайшов 60 лінків загалом, замість 241.
Потім я подивився на W3C'шний link-checker (категорія програм, що є сповнена різноманітністю імен). Окрім уйоб інтерхфейсу він має CLI компонента--перл-скрипта, який витратив 13 хв для народження лоґу з 27859 рядками, де посеред машино-нечитабельного мотлоху пропонується шукати зламані посилання. Можливо, як за таке чарджити клаентів по $1249, то це має сенс; найняти когось буде нескладно.
Ґітхаб є повний "fast" та "async" чекерів, найпопулярнійший з яких (1.9K зірочок) не підтримує рекурсії, але друкує затишні 🔍, ✅, 🚫, 💤 емоджі ув консолі та рожеву progress bar. Рисою професіоналізму посеред сучасних лікн-чекерів є порада використовувати їх з докер-контейнеру, тому що для відправляння http ріквестів наш час вимагає >= 216 бібліотек на будь-якій мові пограмування.
Чи складно написати свій, наприклад, на bash? Хоча він буде мати міцний вайб нульових, коли завантажена сторінка якимось wget майже відповідала сторінці яку рендерив бовзер, для перевірки сторінок, наприклад, документації, такий чекер все одно буде залишатися корисним. Назвемо його badlinks:
$ wc -l lib.bash url* badlinks
21 lib.bash
28 urlcheck
14 urlextract.rb
54 urlextract-recursive
24 badlinks
141 total
На чистому bash це зробити майже неможливо: переписування nokogiri на шелі я залишаю комусь іншому, а ліпшого парсера кострубатого html, аніж nokogiri, не існує ув природі.
badlinks складається з 3 компонентів:
text/html
, та занурюється до них; на кожному етапі він друкує
знайдені leafs;$ curl -sL google.com | ./urlextract.rb q:
https://www.google.com/imghp?hl=uk&tab=wi
http://maps.google.com.ua/maps?hl=uk&tab=wl
…
q:/intl/uk/about.html
q:
тут означає будь-яку base, відносно якої друкуються релатівні
посилання.
$ cat urlextract.rb
#!/usr/bin/env -S ruby -rnokogiri -raddressable/uri
include Addressable
def eh = abort "Usage: #{$0} base_url < html"
base = URI.parse $*[0] rescue eh
eh unless base&.scheme
Nokogiri::HTML(STDIN.read).css('links,a,img,iframe,script,video,audio').each {|n|
u = URI.parse(n['src'] || n['href']) rescue next
next if !u || (!u.scheme && u.path.strip == '')
u.fragment = nil
puts u.scheme ? u : base.join(u)
}
Щоб воно працювало, треба сказати gem install nokogiri
addressable
. Вбудований ув stdlib парсер url є не такий гнучкий як
addressable.
Фрагмент (частина рядка після #) видаляється для простоти, хоча, звичайно, можна було би намагатися шукати його (фрагмент) потім ув html.
Філософське питання, яке може виникнути: чи має ютіліта друкувати унікальні лінки лише (тому що уйоб-сторінка може містити кілька геть однакових), чи залишати процес фільтрування downstream, так би мовити, процесам.
… є найбільш неприємним компонентом. Шелóві мовні конструкції допомагають погано, і найпримітивніший пошук ув глибину з пародією на детектора циклів, це є все що вдалося втиснути ув 54 рядка коду.
./urlextract-recursive http://127.0.0.1/
0 Scanning http://127.0.0.1/ .
1 Scanning http://127.0.0.1/foo.html http://127.0.0.1/
2 Scanning http://127.0.0.1/bar.png http://127.0.0.1/foo.html
2 External http://example.com http://127.0.0.1/foo.html
1 Leaf http://127.0.0.1/baz.jpg http://127.0.0.1/
…
Алгоритма є вкрай простий:
Для кожного лінка зі стартового url робимо HTTP HEAD ріквест. Якщо
останній є text/html
, занурюємося в, якщо ні--друкуємо його як
leaf.
Формат друку:
level type url parent
Щоб не сканувати лінка декілька разів, записуємо його ув хфайл history (1 лінк на рядок для макс швидкості grep'у). Рівень занурення регулюється користувачем і по замовчуванню == 20.
parent
потрібен лише для ютіліти ув наступному розділі.
$ cat urlextract-recursive
#!/usr/bin/env bash
set -e -o pipefail
__dir__=$(dirname "$(readlink -f "$0")")
. "$__dir__/lib.bash"
jn() { jobs -l | wc -l; }
scan() {
local url="$1" level="$2" parent="$3"
[ "$level" -ge "$LEVEL" ] && return 0
echo "$level Scanning $url $parent"
level=$((level+1))
for u in `fetch "$url" | "$__dir__/urlextract.rb" "$url" | sort -u`; do
search "$u" "$history" && continue
echo "$u" >> "$history"
if echo "$u" | grep -F -- "${URL[host]}" >/dev/null 2>&1; then
if is_html "$u"; then
local async=
[ 1 == $level ] && [ "$JOBS" -gt 1 ] \
&& [ `jn` -lt "$JOBS" ] && async=1
if [ $async ] ; then
scan "$u" $level "$url" &
else
scan "$u" $level "$url"
fi
else
echo "$level Leaf $u $url"
fi
else
echo "$level External $u" "$url"
fi
done
}
type curl > /dev/null
: "${LEVEL:=20}"
: "${JOBS:=`nproc`}"
url=${1:?Usage [LEVEL=$LEVEL] $0 url}
url_parse "$url"
history=${HISTORY:-`mktemp /tmp/urlextract-recursive.XXXXXX`}
trap 'rm -f $history; exit 130' 1 2 15
trap 'rm -f $history' 0
if is_html "$url"; then
scan "$url" 0 .
wait
else
echo "0 Leaf $url ."
fi
(Вибачте за простирадло.)
Скрипта намагається робити конкурентні ріквести. Найнеприємніше тут є
неможливість знати заздалегідь скільки посилань буде далі і складність
створення фіксованої кількості worker'ів, перевірка яких була би
можлива ув child процесах. Тому "паралелізм" використовується лише на
рівні 1. Це означає якщо стартовий url мав рівно 1 text/html
посилання, поралелізму не буде.
Якщо не робити ліміта на конкурентні ріквести, вбити мошину можна дуже легко. Я так тестував безлімітний варіянт з 6K gnu libstdc++ doxygen сторінками на локалхості: стався еквівалента форк бімби, лайнакс грохнув ікси та почав люто плюватися сторінками вбитих процесів.
Енівей, отримані рядки від urlextract-recursive легко фільтруються. Наприклад, щоб дізнатися який був макс рівень рекурсії і чи не треба його збільшити:
$ ./urllextract-recursive http://127.0.0.1/ | tee 1
$ awk '{print $1}' 1 | sort -un | tail -1
11
або показати лише ікстернальні посилання:
$ awk '$2 == "External"' 1
Нецікаві шматки з lib.bash:
fetch() { curl -sfL --connect-timeout 3 -m 3 -A 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36' -H 'accept-language: en-US,en;q=0.9' -H 'upgrade-insecure-requests: 1' "$@"; }
is_html() { fetch -I "$1" | grep -Ei '^content-type:\s+text/html' > /dev/null; }
search() { grep -Fx -- "$1" "$2" > /dev/null; }
На відміну від попередньої ютіліти, ця чудово і елементарно паралелізується. Сам скрипта про це гадки не має, тому що оперує лише з 1 url, але конкурентна перевірка робиться ув фінальному врапері наступного розділу через xargs.
$ cat urlcheck
#!/usr/bin/env bash
set -e -o pipefail
__dir__=$(dirname "$(readlink -f "$0")")
. "$__dir__/lib.bash"
check_link() {
local ec=0
fetch "$1" | head -c1 > /dev/null || {
ec=$?
[ $ec == 23 ] && return 0 # pipe was closed by 'head'
[ $ec -lt 100 ] && echo "Bad $1 in $2"
return 101
}
}
type curl > /dev/null
url=${1:?Usage: IGNORE=domains.txt $0 url parent}
parent=${2:-.}
url_parse "$url" || { echo "Invalid $url in $parent"; exit 100; }
if [ "$IGNORE" ] && search "${URL[host]}" "$IGNORE" ; then
echo "Ignoring $url in $parent"
else
[ 2 == $? ] && exit 100
check_link "$url" "$parent"
fi
Єдина оптимізація гідна уваги це є check_link()
, який читає лише 1
байт успішного ріспонзу і перевіряє статусного кода curl.
Деякі уйоб-сайти ітервебу (наприклад wsj, або припизджена квора) вважають curl ботом, ув незалежності від user agent, тому якщо вказати ув IGNORE env var шлях до хфайлу зі списком доменів, посилання з ними перевірятися не будуть.
Фінальна обгортка:
$ cat badlinks
#!/usr/bin/env bash
set -e -o pipefail
__dir__=$(dirname "$(readlink -f "$0")")
usage="Usage: $0 [-l max level] [-j max jobs] [-e] [-i domains.txt] url"
link_type=Leaf
while getopts i:l:j:e opt; do
case $opt in
i) opt_i=$OPTARG ;;
l) opt_l=$OPTARG ;;
j) opt_j=$OPTARG ;;
e) link_type="$link_type|External" ;;
*) echo "$usage" 1>&2; exit 1
esac
done
shift $((OPTIND-1))
: "${1:?$usage}"
export LEVEL=${opt_l:-20} JOBS=${opt_j:-`nproc`} IGNORE=${opt_i}
"$__dir__/urlextract-recursive" "$1" | \
awk '$2 ~ /'"$link_type"'/ {print $3, $4}' | \
xargs -r -n2 -P "$JOBS" "$__dir__/urlcheck"
Зі статичним уйоб-сайтом, на який попередньо знайдені готові пекеджи витрачали від 4 до 13 хв, набір скриптів шелóвих впорався за 16 секунд, відсканувавши 769 лінка.
Ґноум втратив директора:
"Holly Million was named the GNOME Foundation Executive Director in late October. Part of her focus was going to be on helping the GNOME Foundation attain more funding. But coming today as a surprise, she will be departing her role with GNOME effective the end of July. She is stepping down on the basis of pursuing a PhD in psychology and dedicating herself to her own private practice."
Намагання знайти фінансування для Ґімпу викликає такий рівень психоемоційного стресу, який лікується лише отриманням PhD ув психології.
До погодження стати директоркою Ґноума, вона працювала "професійним шаманом, артистом, виробником трав'яних ліків та займалася мікро-гомстедингом". Останнє є не сексуальне збочення, а тип сільського господарства ув Гамериці.
"I do energy healing work on behalf of individuals, land, houses, and businesses.", розказувала про себе пані Мілліон, "I can do this work remotely because everything is connected. We live in a quantum universe."
Гомстединг був дуже ув нагоді: вирощування лікарських трав допомагало "with my shamanic work". Якщо у вас врожай з'їдали комахи, пані Мілліон проводила цілющі церемонії та ритуали по телефону: $250 за годину енергетичної роботи 1:1 з вирівнюванням чакри, або $500 за повне очищення енергії у вашому домі чи охфісі.
Наразі рада директорів Фундацьйоне Ґноума, запаливши пахощі та свічки, шукає нового директора.
Як хутко довести дівопсу або хмарному провайдеру що завантаження з серверу є повільне? Можна заміряти час загальний, чи копіювати все що curl написав на stderr, або вислати кєно зроблене на телефоні (вертикальне, тремтячою рукою, під кутом > 10°, на відстані > 1м від монітора).
Не пам'ятаю ув якій версії віндюка його іксплорер почав малювати графіка під час копіювання хфайлів (Vista?), але він (віндюк) так і не почав цього робити ані ув класичних своїх бовзерах минулого, ані ув своєму поточному безглуздому різновиді кроума для блінка.
Найнахабнішим повідомленням дівопсу був би svg:
та/чи рядки
00:00:01 1000k
00:00:02 2000k
00:00:03 2500k
ув якості доказу.
2й варяінт є найпростіший; curl, наприклад, друкує
fprintf(tool_stderr,
"\r"
"%-3s " /* percent downloaded */
"%-3s " /* percent uploaded */
"%s " /* Dled */
"%s " /* Uled */
"%5" CURL_FORMAT_CURL_OFF_T " " /* Xfers */
"%5" CURL_FORMAT_CURL_OFF_T " " /* Live */
" %s " /* Total time */
"%s " /* Current time */
"%s " /* Time left */
"%s " /* Speed */
"%5s" /* final newline */,
…
кожну секунду, тому лише замінивши \r на \n можна відправляти повного логу:
$ curl http://example.com/1.zip -o 1.zip 2>&1 | tr \\r \\n
та вимагати сатисфакції.
Щоб намалювати графіка з gnuplot, нам потрібні Current time
та
Speed
. Останнє доведеться конвертувати з 1234k
чи 4567M
ув
байти. Спочатку я думав малоелегантно замінювати суфікси:
awk 'function expr(s) {
h["k"]=2**10; h["M"]=2**20; h["G"]=2**30; h["T"]=2**40; h["P"]=2**50
for (k in h) sub(k, "*"h[k], s); return s
}
/[0-9.][kMGTP]?$/ { print expr($NF) | "bc"; close("bc") }'
але, виявляється, саме для цього coreutils має ютіліту numfmt(1), про яку я ніколи не чув (додали недавно, лише 12 років тому, не встигаєш слідкувати за всіма ціма новими штуками):
$ echo lol 10M | numfmt --from iec --field 2
lol 10485760
Так, звичайно, зараз не роблять, а кожного разу ретельно пишуть нудні, багатослівні простирадла на пáйфоні. Те що має фітнутися ув 5 рядків шелових макс + 6 рядків скрипта gnuplot'у, потрібно ретельно розмазувати на 70+, ніби працюєш на ойбіем ув році 1984 і тобі платять за кількість рядків коду.
Повна версія іграшкового візуалайзатора пускається як
$ ./curlbench http://example.com/1.zip | gnuplot --persist
де gnuplot дозволяє зберегти графіка ув потрібному форматі.
$ cat curlbench
#!/usr/bin/env -S stdbuf -o0 bash
set -e -o pipefail
numfmt=`type -p gnumfmt numfmt;:`; test "${numfmt:?}"
cat <<E
set xdata time
set timefmt "%H:%M:%S"
set xlabel "Time"
set format y "%.0s%cB"
set grid
plot "-" using 1:2 with lines title ""
E
curl "$@" -fL -o /dev/null 2>&1 | tr \\r \\n | awk '
/[0-9.][kMGTP]?$/ {
time = index($10, ":") == 0 ? $11 : $10
if (time != "--:--:--") print time, $NF
}' | tr k K | $numfmt --from iec --field 2
Має працювати також на fbsd та маку (як були встановлені coreutils), але мені є лінь перевіряти.